Application Layout Control

Extension Librrayには"Application Layout"というControlが用意されている。 これを使うと、さくさくっとOneUIに準拠したアプリケーションのレイアウトを作ることができる。

きっとパタンさえわかってしまえばうまく使えると思うのだが、さっと見た感じではよくわからん。 そこでじっくり解析して理解を深めてみよう。

Application Layout Controlの解説ページについて

まだ日本語の解説ページは少なそう。

以下のページにApplication Layout Controlのサンプルがある。

また、以下の動画でApplication Layoutを使ってのアプリ開発の手順を説明している。

Application LayoutのサンプルDBの解析

まずは、サンプルDBのソースコード解析をしてみよう。 先ほどのサンプルDBをDLし、Designerでひらいてみよう。 「02. Application Layout, Navigator, Dynamic View Panel, Pager Save State コントロール」というやつだ。

サンプルDBの構造

サンプルDBのイメージはこんな感じ。

サンプルのイメージ

このサンプルDBは以下のような構造になってる。

XPageが2つ

  • xpHome.xsp: ビュー用の画面
  • xpForm.xsp: 文書表示用の画面

XPageから参照されてるカスタムコントロールが以下の4つ。

  • ccOneUI.xsp: Application Layoutを使っているところ
  • ccMenu.xsp: メニュー部
  • ccView.xsp: ビューの部分
  • ccForm.xsp: 文書表示用

このサンプルは、"Dynamic View Panel"というExtension Libraryも使っていて、NotesビューをそのままXPageに出すことができるらしい。 そのあたりも見ていこう。

xpHome.xsp

xpHome.xspはビュー用のXpageだ。

サンプルのイメージ

左のメニュー部がビューの切り替えになってて、そこをクリックするとビューが切り替わる。

  01: <?xml version="1.0" encoding="UTF-8"?>
  02: <xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xc="http://www.ibm.com/xsp/custom">
  03:     <xp:this.beforePageLoad><![CDATA[#{javascript:
  04:       var vwNameParam = param.viewName;
  05:       if ( vwNameParam == null ){
  06:         var pagename = "/"+@RightBack(context.getUrl().getAddress(),"/")+"?viewName=vwCategory";
  07:         context.redirectToPage(pagename);
  08:       }
  09:     }]]></xp:this.beforePageLoad>
  10:     <xc:ccOneUI>
  11:         <xp:this.facets>
  12:             <xc:ccMenu xp:key="facetLeft"></xc:ccMenu>
  13:             <xc:ccView xp:key="facetMiddle"></xc:ccView>
  14:         </xp:this.facets>
  15:     </xc:ccOneUI>
  16: </xp:view>
  
  • 3〜9行目はURLパラメータをチェックして、"?viewName=xxx"のパラメータがセットされているかチェックしてる。xxxにはNotesビューの別名を指定。
  • viewName=のURLパラメータがセットされてなければ、?viewName=vwCategoryをつけて、自分を開きなおしてる。
  • 10行目でccOneUIのカスタムコントロールを組み込んでいる。12と13行目はccOneUIの引数みたいなもので、facetLeftにccMenu.xspを、facetMiddleにccViewを組み込んでもらう。
  • ccOneUIはxpForm.cspからも参照してて、そちらではfacetMiddleにccForm.xspを指定してる。

ccOneUI

今回のサンプルのメインはこのカスタムコントロールだ。 ここでは、メニュー部とメインコンテンツ部を除く部分をくみたててる。

  01: <?xml version="1.0" encoding="UTF-8"?>
  02: <xp:view xmlns:xp="http://www.ibm.com/xsp/core"
  03:     xmlns:xe="http://www.ibm.com/xsp/coreex"
  04:     xmlns:xc="http://www.ibm.com/xsp/custom">
  05:     <xp:this.resources>
  06:         <xp:styleSheet href="/custom.css"></xp:styleSheet>
  07:         <xp:styleSheet href="/menu.css"></xp:styleSheet>
  08:     </xp:this.resources>
  09: 
  10:     <xe:applicationLayout id="applicationLayout1">
  11:         <xp:callback facetName="facetMiddle" id="facetMiddle"></xp:callback>
  12:         <xe:this.facets>
  13:             <xp:callback facetName="facetLeft" id="facetLeft" xp:key="LeftColumn"></xp:callback>
  14:         </xe:this.facets>
  15: 
  16:         <xe:this.configuration>
  17:             <xe:oneuiApplication legalLogo="/logo_blue2.gif"
  18:                 legalLogoAlt="Logo" legalText="(c) 2012 Takeshi Yoshida"
  19:                 productLogo="/sample_logo.gif"
  20:                 placeBarName="#{javascript:param.viewName}" productLogoHeight="20px"
  21:                 productLogoWidth="100px" titleBarName="#{javascript:@DbTitle()}"
  22:                 navigationPath="#{javascript:param.viewName}">
  23:                 <xe:this.footerLinks>
  24:                     <xe:basicContainerNode label="Container 1">
  25:                         <xe:this.children>
  26:                             <xe:basicLeafNode label="テクてくLotus"
  27:                                 href="https://www.ibm.com/developerworks/....-1e5e-4fe7-9a9b-fabd0b5195f1">
  28:                             </xe:basicLeafNode>
  29:                             <xe:basicLeafNode label="XPages 技術者コミュニティー"
  30:                                 href="https://www.ibm.com/developerworks/....262-0ce6-4adf-b0fa-7f5e54c6c72a">
  31:                             </xe:basicLeafNode>
  32:                             <xe:basicLeafNode label="アプリケーション開発Wiki(日本語)">
  33:                                 <xe:this.href>
                                         <![CDATA[http://www-10.lotus.com/ldd/ddw....ssionID=CV6X5LDP9I]]>
                                      </xe:this.href>
  34:                             </xe:basicLeafNode>
  35:                             <xe:basicLeafNode label="XPages 日本語サンプルアプリデモサイト"
  36:                                 href="http://xpages.info/XPagesHome.nsf/DemosJapan.xsp">
  37:                             </xe:basicLeafNode>
  38:                             <xe:basicLeafNode href="http://www.facebook.com/xpagesjapan">
  39:                                 <xe:this.label><![CDATA[IBM XPages Japan [facebook]]]></xe:this.label>
  40:                             </xe:basicLeafNode>
  41:                         </xe:this.children>
  42:                     </xe:basicContainerNode>
  43:                 </xe:this.footerLinks>
  44:                 <xe:this.placeBarActions>
  45:                     <xe:basicLeafNode label="アクション">
  46:                         <xe:this.onClick><![CDATA[alert("ここにアクションを追加できます。")]]></xe:this.onClick>
  47:                     </xe:basicLeafNode>
  48:                 </xe:this.placeBarActions>
  49:                 <xe:this.bannerUtilityLinks>
  50:                     <xe:userTreeNode></xe:userTreeNode>
  51:                     <xe:loginTreeNode></xe:loginTreeNode>
  52:                 </xe:this.bannerUtilityLinks>
  53:                 <xe:this.bannerApplicationLinks>
  54:                     <xe:pageTreeNode label="Home" page="/xpHome.xsp"></xe:pageTreeNode>
  55:                 </xe:this.bannerApplicationLinks>
  56:             </xe:oneuiApplication>
  57:         </xe:this.configuration>
  58:     </xe:applicationLayout>
  59: </xp:view>
  
  • 10行目がApplication Layoutだ。<div class="lotusTitlebar">などを並べてページ構成をくみたてていくより楽そう。もちろん、このようなサンプルがあるのが前提だけど。
  • 11〜14行目がメニュー部とコンテンツ部。callbackになっているので、外からコントロールやカスタムコントロールを指定してもらえるようになっている。これで大枠は共通で、メニューとコンテンツだけ 画面ごとに切り替える仕組みが実現できる。
  • 16行目〜のconfiguration属性で、Banner, TitleBar, PlaceBar, Footer, Regalを設定している
  • 23〜43行目で、Footerにリンクを並べてる。ここらへんも「リンクを並べるならこうやる」というパタンで覚えておくのがよさそう。
  • ここにアイコンのようなものを並べるなら、<xe:basicLeafNode>のimage属性で指定すればよさそう。どんな属性があるかは、Designerが教えてくれる
  • 50と51行目は、ログオンユーザー名を出す専用処理と、ログアウト専用処理が<xe:userTreeNode>と<xe:loginTreeNode>で呼び出せるみたいだ。 <xe:basicLeafNode>のlabelをJacaScriptで計算しても、同じようなことができる

facetsとcallbackについて

ccOneUIは、このアプリケーションの各画面共通の「枠」を規定している。 枠の構造

文書の一覧を表示する画面(Home.xsp)では、lotusMain内のlotusContentにNotesビューの内容を表示する。 文書自体を表示する画面(Form.xsp)では、lotusMain内のlotusContentにNotes文書を表示する。

このように、一つのカスタムコントロール内で一部の領域を状況によって切り替えられると便利だ。 カスタムコントロールが一種の関数のようになっていて、関数の引数で、組み込みたいカスタムコントロールを指定できると便利だ。

このような仕組みを提供しているのが、callbackとfacetsというわけだ。

まず、受け側のカスタムコントロールで、外からコントロールを指定してほしい部分に、<xp:callback facetName="xxxx">をいれておく。呼び出し側からどのcallbackか指定できるように、 facetNameというプロパティを指定しておく。

あとは、呼び出し側で、送り込みたいコントロールを指定してやる。送り込むコントロールであることを示すために、<xp:this.facets>の中にいれておくわけだ。 どこのcallbackに対応させるかを指定するために、xp:key="xxxx"で指定してやる。

この法則がわかれば、xpHome.xspの10〜15が、ccOneUIのfacetLeftとfacetMiddleという名前をもつcallbackの領域に渡されるのがわかる。

ただ、ccOneUIの13行目は<xe:this.facets>で囲まれているので、これも別のコントロールに 引き渡されるものだと予想。送り先は<xe:applicationLayout>でfacetNameはLeftColumnなんだろうか。

HTMLに展開されると、この部分はメニュー部のlotusColLeft相当のところに組み込まれる。 11行目のcallbackも<xe:applicationLayout>によって、lotusContent相当に組み込まれる。でも、どこにも 「これをlotusContentに組み込んでくださいね」という指示が見当たらないのが不思議だ。まぁ、そういうもんだと思っておこう。

ccMenu.xsp

メニュー部だ。<xe:navigator>というExtension Libraryを使って、メニューを用意している。

  01: <?xml version="1.0" encoding="UTF-8"?>
  02: <xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xe="http://www.ibm.com/xsp/coreex">
  03:     <xe:navigator id="navigator1">
  04:         <xe:this.treeNodes>
  05:             <xe:pageTreeNode label="カテゴリ別" page="/xpHome.xsp" selection="vwCategory">
  06:                 <xe:this.queryString><![CDATA[#{javascript:return "viewName=vwCategory"}]]></xe:this.queryString>
  07:             </xe:pageTreeNode>
  08:             <xe:pageTreeNode label="作成日順" page="/xpHome.xsp" selection="vwCreatedDate">
  09:                 <xe:this.queryString><![CDATA[#{javascript:return "viewName=vwCreatedDate"}]]></xe:this.queryString>
  10:             </xe:pageTreeNode>
  11:             <xe:pageTreeNode label="作成者別" selection="vwAuthor" page="/xpHome.xsp">
  12:                 <xe:this.queryString><![CDATA[#{javascript:return "viewName=vwAuthor"}]]></xe:this.queryString>
  13:             </xe:pageTreeNode>
  14:         </xe:this.treeNodes>
  15:     </xe:navigator>
  16: </xp:view>
  
  • メニュー用のリンクは、<xe:navigator>の下に<xe:treeNodes>をおいて、その下に<xe:pageTreeNode>を複数並べるというのがお約束みたいだ。
  • 05〜07行目がビューへのリンクを作っているのだが、"xpHome.xsp?viewName=vwCategory"のようなURLを作っている。URLパラメータでビューを切り替える仕組みのため
  • サンプル用にわざわざjavascriptにしているが、今回のように固定文字列なら、<xe:pageTreeNode label="カテゴリ別" page="/xpHome.xsp" selection="vwCategory" queryString="viewName=vwCreatedDate"> のように書くのと同じことではある。

応用編だが、<xe:treeNodes>の下に、<xe:basicContainerNode label="メニューの見出し">みたいな構造にすると、メニューの見出しをつけることができる。

  04:         <xe:this.treeNodes>
  05:             <xe:basicContainerNode label="見出し">
  06:                 <xe:this.children>
  07:                     <xe:pageTreeNode label="カテゴリ別" page="/xpHome.xsp"
  08:                         selection="vwCategory">
  09:                         <xe:this.queryString><![CDATA[#{javascript:return "viewName=vwCategory"}]]></xe:this.queryString>
  10:                     </xe:pageTreeNode>
  11:                     <xe:pageTreeNode label="作成日順" page="/xpHome.xsp"
  12:                         selection="vwCreatedDate">
  13:                         <xe:this.queryString><![CDATA[#{javascript:return "viewName=vwCreatedDate"}]]></xe:this.queryString>
  14:                     </xe:pageTreeNode>
  15:                     <xe:pageTreeNode label="作成者別" selection="vwAuthor"
  16:                         page="/xpHome.xsp">
  17:                         <xe:this.queryString><![CDATA[#{javascript:return "viewName=vwAuthor"}]]></xe:this.queryString>
  18:                     </xe:pageTreeNode>
  19:                 </xe:this.children>
  20:             </xe:basicContainerNode>
  21:         </xe:this.treeNodes>
  

ccView.xsp

今度は、ビューを作っている部分。dynamicViewPanelとうLibraryを使っている。

  01: <?xml version="1.0" encoding="UTF-8"?>
  02: <xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xe="http://www.ibm.com/xsp/coreex">
  03: 
  04:     <xe:pagerSaveState id="pagerSaveState1" globalRows="true" for="dynamicViewPanel1" />
  05:     <xe:dynamicViewPanel id="dynamicViewPanel1" rows="15"
  06:         viewStyle="width:100%" rowClasses="row1,row2" showUnreadMarks="true"
  07:         unreadMarksClass="xspDataTableRowUnread rowUnread">
  08:         <xe:this.pageName>
  09:             <![CDATA[#{javascript:return "/xpForm.xsp?viewName=" + param.viewName}]]>
  10:         </xe:this.pageName>
  11:         <xp:this.facets>
  12:             <xe:pagerSizes id="pagerSizes1" xp:key="footerPager" sizes="15|25|50|all" />
  13:             <xe:pagerExpand id="pagerExpand1" xp:key="viewTitle" />
  14:             <xp:pager id="pager1" xp:key="headerPager" partialRefresh="true" 
  15:                 layout="SeparatorPage Status FirstImage PreviousImage SeparatorPage Group NextImage LastImage">
  16:             </xp:pager>
  17:         </xp:this.facets>
  18:         <xe:this.data>
  19:             <xp:dominoView var="view1" expandLevel="1">
  20:                 <xp:this.viewName><![CDATA[#{javascript:
  21:                   var vwNameParam = param.viewName;
  22:                   if ( vwNameParam == null ){
  23:                       return "vwCategory";
  24:                   }else{
  25:                       return vwNameParam;
  26:                   }
  27:                 }]]></xp:this.viewName>
  28:             </xp:dominoView>
  29:         </xe:this.data>
  30:     </xe:dynamicViewPanel>
  31: </xp:view>
  
  • 4行目はXPagesのビューページの状態を保持してくれるコントロール。ビューページ→文書ページ→ビューページと移動したときに、ビューの表示位置やカテゴリの展開状況を保持してくれる
  • 5行目はDynamicViewPanelで、Notesビューの設計をそのままXPagesのビューに展開してくれる。<xp:viewPanel>で作るビューは、その時点のビューの設計を反映してくれるが、 DynamicViewPanelでは、Notesビューの設計変更がそのままはネイされる特徴を持つ。
  • 6,7行目はDynamicViewPanelのパラメータ。意味はまだ調べてない
  • 8〜10行目では、このビューを表示するときのURLを決めている。
  • 11〜17行目はdynamicViewPanelに付ける小物パーツの指定。12行目が1ページに表示するん文書数を決めるパーツ、13行目がカテゴリ表示の展開/省略パーツ、15行目がページセレクタパーツだ。 ふーん、こんな風にビューを設定できるんだなぁ。
  • 18〜29でどのビューを表示するかを決定している(データソースの指定)。URLパラメータのviewNameにNotesビューの別名を設定している。