HUMCOM ItemList

要素数が可変で、構造がちょっと複雑なリストを扱うオブジェクトを用意する。

本当は継承を使いたいんだけど、まだいまいちJavaScriptで基本オブジェクトを継承して機能を拡張する方法がよくわかってない。 しょうがないので、基本型を説明するので、直接変更してほしい。

要素

一つ一つの要素はOneItemで表現している。どんな属性(フィールド)を管理させたいかをthis._fieldsの部分で定義する。

  getFields : function() {
    fields = [
      "type",
      "title",
      "comment"
    ];
    return fields;
  },
  

わかりやすい名前を定義しておけば、item.type, item.titleのようにアクセスできる。

要素を示すオブジェクトには、serizize()とdeserialize()という2つのメソッドだけを用意している。これはリストを管理するItemListが文書にデータを保存/読み出しする時に 使用する。

基本これだけでいいけど、値の取得や設定に特別なことをしたいければ、それなりのメソッドを定義しておく。 メソッド定義は、既存のメソッドの続きに追加していけばいい。一番最後のメソッド以外には終端にカンマをつけるのを忘れずに。

  OneItem.prototype = {
    ....
    serialize : function() {
      ....
    },
  
    deserialize : function(obj) {
      .....
    }
  }
  

要素のリスト

要素のリストはItemListオブジェクトで管理する。

Notes文書のフィールドへの書き出し、読み出し、リストの操作用メソッドを用意している。

後は用途に応じて、検索用や変更のメソッドなどを整備するといい。

loadField(xsp_doc, fieldname)
writeField(xsp_doc, fieldname)

自分が管理してるリスト情報をxsp_docのfieldnameで示すフィールドから読み出し、保存する。 データ形式はJSON。

loadRichField(xsp_doc, fieldname)
writeRichField(xsp_doc, fieldname)

リスト情報が多い場合はフィールドの32KBの壁を越えてしまうので、リッチテキストフィールドに保存しないといけない。 その場合はこちらを使う。

getLength()

保存データ数を返す。

at(index)

index番目のアイテム(OneItemオブジェクト)を返す。

push(oneitem)

リストにアイテムを追加する。

remove(index)

index番めのアイテムを削除する。

使い方

基本的には、要素数が可変の情報を扱うときに使う。

XPage(xsp_doc)を開くときの処理に、以下のようなコードを書いておく。

  viewScope.PartsList = new ItemList();
  viewScope.PartsList.loadField(xsp_doc, "リスト情報");
  

こうしておくと、viewScope.PartsList(内容を示すいい名前をつける)にItemListオブジェクトが保持される。

新規作成時には空のオブジェクトをいれておくだけでいい。

保存時には以下のようにして、文書のフィールドに書き戻す。

  viewScope.PartsList.writeField(xsp_doc, "リスト情報");
  

画面に表示するときには、<xp:repeat>のデータソースにviewScope.PartsList.list を指定して、各要素の名前で情報にバインドすればいい。

  01:  <xp:repeat rows="30" value="#{javascript:viewScope.PartsList.list}"
  02:      indexVar="index" var="parts">
  03:      <xp:tr>
  04:          <xp:td style="text-align:center">
  05:              <xp:label value="#{index+1}" />
  06:          </xp:td>
  07:          <xp:td styleClass="comboSelect">
  08:              <xp:comboBox value="#{parts.type}" >
  09:                  <xp:selectItems>
  10:                      <xp:this.value><![CDATA[#{javascript:["ハード", "ソフト"]}]]></xp:this.value>
  11:                  </xp:selectItems>
  12:              </xp:comboBox>
  13:          </xp:td>
  14:          <xp:td>
  15:              <xp:inputText value="#{parts.title}" />
  16:          </xp:td>
  17:          <xp:td>
  18:              <xp:inputText value="#{parts.comment}" />
  19:          </xp:td>
  20:          <xp:td>
  21:              <xp:button value="DEL" rendered="#{javascript:document1.isEditable()}">
  22:                  <xp:eventHandler event="onclick" submit="true" refreshMode="complete">
  23:                      <xp:this.action><![CDATA[#{javascript:viewScope.PartsList.remove(index)}]]></xp:this.action>
  24:                  </xp:eventHandler>
  25:              </xp:button>
  26:          </xp:td>
  27:      </xp:tr>
  28:  </xp:repeat>
  
  • 1〜2行目が<xp:repeat>のデータソースを指定してる。"parts"が各要素を指すようにしている
  • 繰り返し部のバインド先をvalue="#{parts.title}"のようにすれば、対応するフィールドの表示や入力が可能になる
  • 21〜25行目は要素を削除する処理の書き方。indexが何個目の要素か指すので、remove()メソッドを作用させれば、指定行の要素を削除できる
  • 追加処理はもっと簡単でこんな感じ。

      var parts = new Parts();
      viewScope.PartsList.push(parts);
      

リッチテキストフィールドへの保存

OneItem内のフィールド数が多くなり、保持する情報が増えてくると、そのうちJSON文字列をテキストフィールドに保存できなくなる。というのも、テキスト フィールドには32KBの壁があり、データはそれほどたくさん保持できるわけではない。

そういうときには、保存先をリッチテキストフィールドにする。こうすればデータ量の上限はまぁ気にすることはない。 その際には、ItemList.writeRichField()とItemList.loadRichField()を使うようにすればいい。

ItemList.loadRichField()のコードを見てもらうと、以下のように書かれている。

  01:   loadRichField : function(xsp_doc:NotesXspDocument, fieldname) {
  02:     this.list = [];
  03:     var back_doc = xsp_doc.getDocument();
  04:     var field = back_doc.getFirstItem(fieldname);
  05:     var json_text = field.getText();
  06:     json_text = json_text.replace(/\r|\n/g, "");
  07:     var obj_list = $hc.fromJson(json_text);
  08:     for(var index=0; index < obj_list.length; index++) {
  09:         var obj = obj_list[index];
  10:         var oneitem = new OneItem();
  11:         oneitem.deserialize(obj);
  12:         this.list.push(oneitem);
  13:     }
  14:   },  
  
  • Dominoの仕様なのか、リッチテキストフィールドから文字列を取り出すと、ところどころに改行コード('\n')が入り込む
  • そのため、06行目で改行コードを除去している
  • そのため、元のJSONデータに改行コードがふくまれててはいけない。というより、改行コードが含まれていると、あとでJSON文字列→Object変換時にエラーになる
  • そのため、OneItemのserialize()では、以下のように改行コードを文字列に変換している

      01:   serialize : function() {
      02:     var obj = {};
      03:     var fields = this.getFields();
      04:     for(var index=0; index < fields.length; index++) {
      05:       var fieldname = fields[index];
      06:       obj[fieldname] = this[fieldname].replace('\r\n', '\\r\\n');
      07:     }
      08:     return obj;
      09:   },
      
  • 逆にdeserialize()では、以下のように文字列化した改行コードを元に戻している

      01:   deserialize : function(obj) {
      02:     var fields = this.getFields();
      03:     for(var index=0; index < fields.length; index++) {
      04:       var fieldname = fields[index];
      05:       this[fieldname] = (obj[fieldname] || "").replace('\\r\\n', '\r\n');
      06:     }
      07:   }
      
  • こうすることで、可変長項目の中にxp:inputTextareaがあっても、うまく改行コード付きでデータを保持できる。
  • ただ、可変長項目のテキスト入力枠で""ujauja\r\nhogehoge"と打ち込んでしまうと、改行コードに変換されてしまうという副作用がある。なんかいい手はないだろうか。