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つ

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

このサンプルは、"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>
  

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>
  

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: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>