XPagesでExcel出力

XPagesアプリでExcelファイルを出力したかったらPOI4XPagesがお勧め。

"Apache POI"というWordやExcelファイルを操作するJavaライブラリがあり、それをXPagesから利用しやすくしたのが"POI4XPages"というものらしい。「ポイ for XPages」と呼んでいいようだ。

POI4XPagesを使うには、XPagesサーバとDomino DesignerにPOI4XPagesをインストールする必要がある。 その方法は「POI4XPagesを使ったword-excelへの簡単エクスポート」というスライドがとてもわかりやすい。

最初、POI4XPagesはJavaを覚えないと使いこなせないかと思ってちょっと敬遠してたのだが、実はそんなことなくて、使い慣れたJavaScriptだけで完結できることがわかったので、ここで紹介 してみよう。興味あるのはExcel出力だけなのでWord出力は扱わない。

POI4XPagesのインストールは先ほどのスライドを参考にして済ませておいてください。

まずはサンプル

OpenNTFのPOI4XPagesのページからPOI4XPAGES_1_2_6.zipをダウンロードすると、中にサンプルDBが入ってる。 基本このサンプルを読み解けば、大体の使い方は理解できる。

あとは設計を見ながら説明しよう。 Excel出力関係は、"xlsx"で始まるXPageにまとまってる。

xlsx_view_objectlistdatasource.xsp

このXPageはJavaの自作ライブラを呼び出して、ビューから文書の一覧を取り出し、情報の配列を作って、それをExcelに出力するという大変応用の利くサンプルです。 まずはこのサンプルで基本を理解し、次にJavaに頼らずにJavaScriputだけでExcelファイルを出力する方法を説明する。

キモとなる部分を抜き出すとこんな感じ。

  01: <wgpoi:spreadsheet id="shContacts" downloadFileName="allContacts.xlsx">
  02:   <wgpoi:this.templateSource>
  03:     <wgpoi:resourcetemplate fileName="SampleExcel.xlsx" />
  04:   </wgpoi:this.templateSource>
  05:   <wgpoi:this.spreadsheets>
  06:     <wgpoi:table name="Contacts" create="false">
  07:       <wgpoi:this.cellValues>
  08:         <wgpoi:cellBookmark name="user">
  09:           <wgpoi:this.value><![CDATA[#{javascript:@Name("[CN]",@UserName())}]]></wgpoi:this.value>
  10:         </wgpoi:cellBookmark>
  11:         <wgpoi:cellBookmark name="date" value="#{javascript:@Text(@Today())}" />
  12:       </wgpoi:this.cellValues>
  13:       <wgpoi:this.exportDefinitions>
  14:         <wgpoi:data2rowExport startRow="4" stepSize="1" var="row" index="index">
  15:           <wgpoi:this.dataSource>
  16:             <wgpoi:ListObjectDataSource buildValues="#{javascript:
  17:               contactBean.getAllContacts()
  18:             }"  />
  19:           </wgpoi:this.dataSource>
  20:           <wgpoi:this.columns>
  21:             <wgpoi:columnDefinition columnNumber="0" columnTitle="FirstName" rowShift="0" />
  22:             <wgpoi:columnDefinition columnNumber="1" columnTitle="LastName" rowShift="0" />
  23:             <wgpoi:columnDefinition columnNumber="2" columnTitle="City" rowShift="0" />
  24:             <wgpoi:columnDefinition columnNumber="3" columnTitle="State" rowShift="0" />
  25:             <wgpoi:columnDefinition columnNumber="4" columnTitle="EMail" rowShift="0" />
  26:             <wgpoi:columnDefinition columnNumber="5" computeValue="#{javascript:index}" />
  27:             <wgpoi:columnDefinition columnNumber="6" computeValue="#{javascript:
  28:                  row.getDocument().getUniversalID()
  29:             }" />
  30:           </wgpoi:this.columns>
  31:         </wgpoi:data2rowExport>
  32:       </wgpoi:this.exportDefinitions>
  33:     </wgpoi:table>
  34:   </wgpoi:this.spreadsheets>
  35: </wgpoi:spreadsheet>
  36:
  37: <xp:button value="get Excel-File" id="btExport">
  38:   <xp:eventHandler event="onclick" submit="true" refreshMode="complete">
  39:     <xp:this.action>
  40:       <wgpoi:generateWorkbook workbookId="shContacts" />
  41:     </xp:this.action>
  42:   </xp:eventHandler>
  43: </xp:button>
  

とまぁこんな感じでExcelの出力を制御できる。 このサンプルではデータソースをJavaのオブジェクトから得てるし、rowもJavaのオブジェクトだしで、Javaのコードを書かないといけない。

JavaScript用に改造

POI4XPagesはデータソース定義部でJavaのオブジェクトだけでなく、JavaScriptのオブジェクトも扱うことができる。 先ほどのサンプルの一部を以下のように改造するとJavaScriptのオブジェクトを得られる。

  01:   <wgpoi:data2rowExport startRow="4"
  02:       stepSize="1" var="row" index="index">
  03:     <wgpoi:this.dataSource>
  04:       <wgpoi:ListObjectDataSource
  05:         buildValues="#{javascript:$poi.getDataList()}">
  06:       </wgpoi:ListObjectDataSource>
  07:     </wgpoi:this.dataSource>
  08:     <wgpoi:this.columns>
  09:       <wgpoi:columnDefinition columnNumber="0"
  10:         computeValue="#{javascript:row.FirstName}" rowShift="0" />
  11:       <wgpoi:columnDefinition columnNumber="1"
  12:         computeValue="#{javascript:row.LastName}" rowShift="0" />
  13:       <wgpoi:columnDefinition columnNumber="2"
  14:         computeValue="#{javascript:row.City}" rowShift="0" />
  15:       <wgpoi:columnDefinition columnNumber="3"
  16:         computeValue="#{javascript:row.State}" rowShift="0" />
  17:       <wgpoi:columnDefinition columnNumber="4"
  18:         computeValue="#{javascript:row.EMail}" rowShift="0" />
  19:       <wgpoi:columnDefinition columnNumber="5"
  20:         computeValue="#{javascript:index}" />
  21:       <wgpoi:columnDefinition columnNumber="6"
  22:         computeValue="#{javascript:row.UNID}" />
  23:     </wgpoi:this.columns>
  24:   </wgpoi:data2rowExport>
  

$poi.getDataList()はこんな感じで定義されてる。

  01:   $poi.getDataList = function() {
  02:     var all_view = database.getView("AllContacts");
  03:     var doc = all_view.getFirstDocument();
  04:     var list = [];
  05:     while(doc) {
  06:       var item = {};
  07:       item.FirstName = doc.getItemValueString("FirstName");
  08:       item.LastName  = doc.getItemValueString("LastName");
  09:       item.City      = doc.getItemValueString("City");
  10:       item.EMail     = doc.getItemValueString("EMail");
  11:       item.State     = doc.getItemValueString("State");
  12:       item.UNID      = doc.getUniversalID();
  13:       list.push(item);
  14:       var next_doc = all_view.getNextDocument(doc);
  15:       doc.recycle()
  16:       var doc = next_doc;
  17:     }
  18:     return list;
  19:   }
  

このサンプルでは、ビューの全文書をループでまわしながら必要なフィールド値を抜き出しながらlistに詰め込んでいる。 必要に応じて選別もできるが文書数が多いときは注意が必要。

Excel出力ボタンの連続押下問題

サンプルではExcel出力ボタンを押下すると、しばらくボタンの再押下ができない。 ブラウザからの通信をチェックしてみるとボタン押下でPOST要求を出したはいいけど、サーバから応答が来ないため、応答タイムアウトがおきるまで次のPOST要求が出せない。

応答が返せればいいけどそこで、POSTの応答を自由に返すのは難しい。そこでCSJSでページ遷移させてしまえばよさそう。 こんなコードをボタン押下のCSJSのイベントにいれておけばいい。

  setTimeout("location.reload()",3000);