CairngormでViewHelperを使っているコンポーネントをPopupしたい場合どうするか、という話です。
ちょっと卵が先かニワトリが先か、ということで考えてしまったのでメモ。
Cairngormでは、ViewLocatorを使ってViewとロジックを分離します。
アプリケーションの見た目に関る部分はMXMLに記述して、動作に関る部分はViewHelperに記述することで
見た目と動作を別のものにするわけですね。
具体的にはMXMLの中でそのViewに該当するViewLocatorを宣言しておきます。
Hogeというコンポーネントに対するViewHelperがHogeViewHelperならこんな感じです。
<?xml version="1.0" encoding="utf-8"?> <mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:viewhelper="hoge.viewhelper.*" > <viewhelper:HogeViewHelper id="hogeViewHelper" />
そうするとHogeViewのインスタンスが生成された時に
HogeViewHelperのインスタンスが生成されて、
HogeViewHelperのviewフィールドにHogeViewのインスタンスへの参照が設定されます。
で、あわせてViewLocatorにMXMLで指定したHogeViewHelperのidをキーとして
(Cairngormのドキュメントで言うところのcanonical name)
HogeViewHelperのインスタンスが登録されます。
後はViewLocatorのインスタンスにgetViewHelper("hogeViewHelper")とすると
HogeViewHelperのインスタンスを返してくれます。
もう少しつっこんで書くと、
HogeViewHelperのスーパクラスであるViewHelperクラスには
initializedメソッドが定義されていて、
このメソッドの中で自身のviewとidを設定して、
対象となるViewに対してイベントリスナを設定しています。
イベントリスナはEvent.ADDEDとEvent.REMOVEDで
対象となるViewが表示対象となった時には
ViewLocatorに対して自身を登録して、
対象となるViewが表示対象外となった時には
ViewLocatorに対して自身をします。
つまり、ViewHelperの準備が完了するには以下のステップがあります。
- ViewHelperが設定されたViewのインスタンスが生成される。
- ViewHelperのインスタンスが生成される。
- ViewHelperがimplementsしているIMXMLObjectのinitializedメソッドが呼ばれる。
- initializedメソッドの中でViewHelperのidフィールドとviewフィールドが設定される。
- initializedメソッドの中でViewに対してイベントリスナが登録される。
- Viewが表示対象となる。(ApplicationでaddChildされるとか)
- イベントリスナに登録されたコールバックメソッドの中でViewHelperがViewLocatorに登録される。
これでようやくViewLocatorからViewHelperが取得できるようになるわけです。
反対にViewが表示対象外(ApplicationでremoveChildされるとか)すると
イベントリスナに登録されたコールバックメソッドの中でViewLocatorからViewHelperが削除されます。
はい、長々書きましたけど次に進みます。
FlexでPopUpを実現するには、PopUpManagerというクラスの3つのスタティックメソッドを使います。
シグネチャはこんな感じになってます。
public static function addPopUp(window:IFlexDisplayObject, parent:DisplayObject, modal:Boolean = false, childList:String = null):void public static function createPopUp(parent:DisplayObject, className:Class, modal:Boolean = false, childList:String = null):IFlexDisplayObject public static function removePopUp(popUp:IFlexDisplayObject):void
addPopUpかcreatePopUpでPopUpを表示して、removePopUpでPopUpを削除します。
addPopUpとcreatePopUpの違いは、
PopUpの対象をインスタンスで指定するか(addPopUp)
クラス名で指定するか(createPopUp)の違いです。
何も考えずApplicationの中でPopUpするなら、こんなコードを書くわけです。
var hogeView:HogeView = new HogeView(); PopUpManager.addPopUp(hogeView, this, true);
さて、ViewHelperを使うならどうしましょう。
しれっと考えるとViewHelperの中にpopUpとかいうメソッドを作って
それを呼びたいところですが、Viewのインスタンスが存在していないと
ViewHelperのインスタンスも存在していません。
ViewHelperがViewLocatorに登録されるのは
Viewが表示対象になった後なので、こんなコードもアウトです。
var hogeView:HogeView = new HogeView(); var hogeViewHelper:HogeViewHelper = HogeViewHelper(ViewLocator.getInstance().getViewHelper("hogeViewHelper")); hogeViewHelper.popUp();
Viewが表示対象になるまではViewLocatorからViewHelperを取得できないので
PopUpしてからじゃないとViewHelperを使うことができない。ということです。
付け加えておくと、PopUpするためにはPopUp対象となる親コンポーネントを指定する必要があるので
親コンポーネントのViewHelperにViewを返すメソッドを定義しておいて、
子コンポーネントのViewHelperから親コンポーネントのViewHelperを取得して
親コンポーネントのViewHelperから親コンポーネントを取得してっていう
何やらスパゲッティな予感漂う実装になっちゃいますね。
はい、ということでこんな感じの実装はどうでしょう。
まず親コンポーネントのViewHelperにこんなメソッドを用意しておきます。
public function createPopUp(className:Class, modal:Boolean = false, childList:String = null):IFlexDisplayObject { var parent:DisplayObject = ; return PopUpManager.createPopUp(parent, className, modal); }
で、PopUpする子コンポーネントのViewHelperにはこんなメソッドを。
public function close():void { var window:IFlexDisplayObject = IFlexDisplayObject(this.view); PopUpManager.removePopUp(window); }
closeメソッドはこんな感じ親コンポーネントに実装することもできるでしょうけど
public function close():void { var hogeViewHelper:HogeViewHelper = HogeViewHelper(ViewLocator.getInstance().getViewHelper("hogeViewHelper")); var window:IFlexDisplayObject = IFlexDisplayObject(hogeViewHelper.getView()); PopUpManager.removePopUp(window); }
これだとHogeViewHelper側にViewを返すメソッド(getView())が必要になるので
ちょっといやな予感がしますね。
ちなみに親コンポーネントのViewHelperのcreatePopUpは、
addPopUpメソッドでもいける・・・と思います・・・試してませんけど^^;
PopUpManagerのremovePopUpメソッドが実行された時には、
REMOVEDなEventがdiapatchされますので、
ViewLocatorからHogeViewHelperの登録は削除されます。
つまり、後処理もちゃんとしてくれてるってことですね。