goog-inline-blockとgoog.style.setInlineBlock(element)の違いについて
何の違いがあるんだろう...と思っていたんだけど、goog.style.setInlineBlock(element)はgoog.style.removeInlineBlock(element)がないのに対して、goog.dom.classes.add(element,'goog-inline-block')はgoog.dom.classes.remove(element,'goog-inline-block')することが出来る、とのこと。
途中でinline-blockを切り替えたい、っていうのもあまり思いつかないので、どちらでも良いような気はしますが、使い分けがわかったので、それはとても良かった。
- 作者: Michael Bolin
- 出版社/メーカー: Oreilly & Associates Inc
- 発売日: 2010/09/15
- メディア: ペーパーバック
- クリック: 37回
- この商品を含むブログ (3件) を見る
ほんとおススメ。というか、これないと厳しそうな感じがするよ。
今現在のClosureに対しての感想。
closure-library始める4 uiコンポーネント作ってみた
いやね、Containerのサンプル見たら、HorizontalのコンテナにControlをaddChildする前に、addClassName("goog-inline-block")してて、そこコメントアウトしたら横に並ばず...。
goog.provide('hoge.HBox'); goog.require('goog.ui.Container'); goog.require('goog.style'); hoge.HBox = function(opt_renderer,opt_domHelper){ goog.base(this,goog.ui.Container.Orientation.HORIZONTAL,opt_renderer,opt_domHelper); }; goog.inherits(hoge.HBox,goog.ui.Container); hoge.HBox.prototype.addChild = function(child, opt_render){ goog.base(this,'addChild',child, opt_render); if(child.getElement()){ goog.style.setInlineBlock(child.getElement()); } }; hoge.HBox.prototype.addChildAt = function(control, index, opt_render){ goog.base(this,'addChildAt',control, index, opt_render); if(control.getElement()){ goog.style.setInlineBlock(control.getElement()); } };
closure-library始める3 uiコンポーネントについての続き
enterDocumentはDOMが作成されて、document上に配置された時に呼ばれる。だから、イベントリスナーを張るのに適した場所だ。
その前に、プロパティに、goog.events.EventHandlerを持っておく。
goog.require('goog.events.EventHandler'); //略 hoge.Box = function(opt_label,opt_domHelper){ … this.eh_ = new goog.events.EventHandler(this); … }
これ、なんでEventHandlerをわざわざプロパティに持っておくのかな...と思ったら、new goog.events.EventHandler(this)としておくことで、このオブジェクトを使ってlistenさせるとその時のthisがEventHandlerのコンストラクタで渡したオブジェクトになるのね。
というか、このオブジェクトがEventHandlerって名前は微妙におかしくないか...?
hoge.Box.prototype.enterDocument = function(){ goog.base(this,"enterDocument"); this.eh_.listen(this.getElement(),goog.events.EventType.CLICK, function(){alert('clicked ' + this.label_)}); };
goog.base(this,"enterDocument")で、スーパークラスのenterDocumentを呼んでいる。で、その後、自分のルート要素のイベントをlistenする。最後の関数でthis.label_を呼び出せているのがポイント。
...この方が簡単だと思って、無名関数使ったが、unlistenもあるので、元ソースと同じように変更する。
で、enterDocumentと対になっているのがexitDocumentで、ここでイベントリスナーを外しておかないと、オブジェクトがリークして後々大変なことになったりするのでしょう。まとめると
hoge.Box.prototype.enterDocument = function(){ goog.base(this,"enterDocument"); this.eh_.listen(this.getElement(),goog.events.EventType.CLICK, this.onDivClicked_); }; hoge.Box.prototype.exitDocument = function(){ goog.base(this,"exitDocument"); this.eh_.unlisten(this.getElement(),goog.events.EventType.CLICK, this.onDivClicked_); }; hoge.Box.prototype.onDivClicked_ = function(event){ alert(this.label_); };
で、DOM関連の後始末はexitDocumentで行って、そのほかのオブジェクトの破棄は、disposeInternalで行う。
hoge.Box.prototype.disposeInternal = function(){ goog.base(this,"disposeInternal"); this.eh_.dispose(); };
これで非常に簡単なコンポーネントができた。こうやってみると、意外とわかりやすい?。
script/apps.js
goog.provide('hoge.App'); goog.require('hoge.Box'); hoge.App = function(){ this.initialize_(); } hoge.App.prototype.initialize_ = function(){ var he = new hoge.Box(); he.render(document.body); }; new hoge.App();
script/box.js
goog.provide('hoge.Box'); goog.require('goog.ui.Component'); goog.require('goog.events.EventHandler'); hoge.Box = function(opt_label,opt_domHelper){ goog.ui.Component.call(this,opt_domHelper); this.label_ = opt_label || 'Click Me'; this.eh_ = new goog.events.EventHandler(this); this.initialize_(); } goog.inherits(hoge.Box,goog.ui.Component); hoge.Box.prototype.initialize_ = function(){ }; hoge.Box.prototype.createDom = function(){ this.decorateInternal(this.dom_.createElement('div')); }; hoge.Box.prototype.decorateInternal = function(element){ this.setElementInternal(element); this.dom_.setProperties(element, {"style":"border: 1px solid black; width: 150px; background-color: gray; color: white; text-align: center; font-weight: bold;"}); if(!this.getLabelText()){ this.setLabelText(this.label_); } }; hoge.Box.prototype.getLabelText = function(){ if(!this.getElement()){ return ''; } return this.dom_.getTextContent(this.getElement()); }; hoge.Box.prototype.setLabelText = function(text){ if(this.getElement()){ this.dom_.setTextContent(this.getElement(), text); } }; hoge.Box.prototype.enterDocument = function(){ goog.base(this,"enterDocument"); this.eh_.listen(this.getElement(),goog.events.EventType.CLICK, this.onDivClicked_); }; hoge.Box.prototype.exitDocument = function(){ goog.base(this,"exitDocument"); this.eh_.unlisten(this.getElement(),goog.events.EventType.CLICK, this.onDivClicked_); }; hoge.Box.prototype.disposeInternal = function(){ goog.base(this,"disposeInternal"); this.eh_.dispose(); }; hoge.Box.prototype.onDivClicked_ = function(event){ alert(this.label_); };
closure-library始める2 uiコンポーネントについて
まず、コンポーネントを作ってみる。
参考。http://code.google.com/p/closure-library/wiki/IntroToComponents
参考のページとそのサンプルコードを見ればそれでいいような気もするが、ちょっと簡略化してちょっとずつ確認してみる。
goog.provide('hoge.Box'); goog.require('goog.ui.Component'); hoge.Box = function(opt_label,opt_domHelper){ goog.ui.Component.call(this,opt_domHelper); this.label_ = opt_label || 'Click Me'; this.initialize_(); } goog.inherits(hoge.Box,goog.ui.Component); hoge.Box.prototype.initialize_ = function(){ };
goog.provideで自分が何のクラスかを宣言して、goog.requireで依存を表現している。uiを作るので、goog.ui.Componentに依存している。
コンストラクタの最初でgoog.ui.Componentのコンストラクタを呼んでいる。で、goog.inheritsでhoge.Boxはgoog.ui.Componentを継承することを宣言している。次はDOMを構築する。
hoge.Box.prototype.createDom = function(){ this.decorateInternal(this.dom_.createElement('div')); };
createDomは実装必須。decorateInternalはオプション。createDomでは、このクラスのDOM表現のルートとなる要素をつくる。で、decorateInternalでは、外部から要素が与えられたときに、その要素を自分のルート要素として、必要なお膳立てを行う、という感じのようだ。ここでは、ルート要素を空のdivで作って、それ以外の装飾の部分はdecorateInternalに任せるという形になっている。いきなりthis.dom_とか出てきてなによ、と思うがAPIリファレンスを読むと、goog.domのショートカットだ。
hoge.Box.prototype.decorateInternal = function(element){ this.setElementInternal(element); this.dom_.setProperties(element, {"style":"border: 1px solid black; width: 150px; background-color: gray; color: white; text-align: center; font-weight: bold;"}); if(!this.getLabelText()){ this.setLabelText(this.label_); } }; hoge.Box.prototype.getLabelText = function(){ if(!this.getElement()){ return ''; } return this.dom_.getTextContent(this.getElement()); }; hoge.Box.prototype.setLabelText = function(text){ if(this.getElement()){ this.dom_.setTextContent(this.getElement(), text); } };
this.setElementInternal(element)が重要。closureのuiコンポーネントでは、DOM表現のルート要素はsetElementInternal(element)でセットして保持しておく。以降はgetElement()で取り出せる。getLabelText/setLabelTextはおまけ。
これで、実行してみる。app.jsに以下のように記述する。
goog.provide('hoge.App'); goog.require('hoge.Box'); hoge.App = function(){ this.initialize_(); } hoge.App.prototype.initialize_ = function(){ var he = new hoge.Box(); he.render(document.body); }; new hoge.App();
なんかボタンみたいなものが表示できた。
closure-library始める
closure-libraryはgoogleがつくった、gmail等で利用されているライブラリ。かっちり作りこみたい向きのためのライブラリなので、小さい規模の開発には面倒が多い、という感じを受ける。
関連プロダクトであるclosure compilerを使って、最適化して使うことが前提になっており、そのためにはpythonインストールする必要があるなど、さらに敷居が高い。
しかし、最近はjava製のplovrというツールがあり、これをつかうとオンデマンドでコンパイルしてくれるようになり、若干手軽に開発できる。
環境を準備する。DocumentRoot以下に適当なディレクトリを作る。
closure libraryはsvnでチェックアウトして持ってくる。
svn checkout http://closure-library.googlecode.com/svn/trunk/ closure-library
plovrは以下からjarがダウンロードできる。最新の持ってくればいいんだと思う。
http://code.google.com/p/plovr/downloads/list
plovrはjavaなので、javaが実行できるようにしておく必要がある。
ドキュメントルート下の適当なディレクトリ下の構成は下記の通り。cssの下にスタイルシートを、scriptsの下にjavascriptを置く想定。closure-libraryはチェックアウトしてきたclosure library。
closure-library/ css/ scripts/app.js config.json index.html plovr.jar
config.jsonはplovrのための設定ファイル。
{"id":"base","paths":"scripts/","inputs":"scripts/app.js"}
scripts/app.jsに最小限のスクリプト。
goog.provide('hoge.App'); goog.require('goog.dom'); hoge.App = function(){ this.initialize_(); } hoge.App.prototype.initialize_ = function(){ var hc = goog.dom.createDom('span',null,'Hello Closure'); goog.dom.appendChild(document.body, hc); }; new hoge.App();
index.html
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>closure test</title> <script type="text/javascript" src="http://hostname:9810/compile?id=hoge"></script> </head> <body> <div id="hoge"></div> <script type="text/javascript"> goog.require('hoge.App'); </script> </body> </html>
これで、用意はできたので、まずplovrを起動してみる。デフォルトでは9810ポートで上がる。
java -jar plovr.jar serve config.json > /dev/null 2>&1
ブラウザで http://ホスト名:9810/ でアクセスしてみるとそれっぽい画面が起動していれば、まぁ、いいんじゃないか。
http://ホスト名:9810/compile?id=hoge でアクセスしてみると、minifyされたscriptが返ってくる。よく見ると、自分がapp.jsで書いたコードが含まれているのがわかるはず。以降app.jsを書き換えて、表示する都度コンパイルしてくれる。この状態になら、あまり意識せず開発できそうだ。
で、ブラウザで、http://ホスト名/適切な/パス名/index.html にアクセスすると間違いがなければ、ちゃんとHello Closureと表示されているはずだ。はじめの一歩。