コンボボックスの選択に応じてコントロールの表示/非表示を切り替える

コンボボックスでキーワードを選択するときに、「その他」を選んだときにその他のキーワードを入力できるテキストボックスを表示してみよう。

基本的な考え方

従来のNotesなら、行の表示式で別のフィールドの値に応じて隠すかどうかを決める式を書いておき、キーワードのプロパティに「選択時にフィールドを更新」とか設定しておけばよかった。

XPagesにも同じように、コントロールごとに表示/非表示を切り替えるJavaScriptをかけるようになっている。ここに選択肢フィールドの値に応じて表示を切り替える式を書いておけばいいような気がする。 ただし、XPagesでコントロールの表示/非表示を切り替えるには、ページ全体もしくはページの一部を更新=サーバからHTMLを生成しなおして送りなおしてもらう必要がある。

基本の設計

フォーム

フォームには、キーワードを格納するフィールドと、キーワードが「その他」の場合に、手入力の値を格納するフィールドを用意する。

  フォーム名: KeyForm
  件名: [subject]
  主選択肢: [keyword]
  その他: [other]
  

ビュー

ビューも用意しておこう。

  ビュー名: [keywordlist]
  1列目: [subject]
  2列目: [keyword]
  3列目: [other]
  

ビュー用のXPage

ビュー用のXPage:xplistはこんな感じ。

  <?xml version="1.0" encoding="UTF-8"?>
  <xp:view xmlns:xp="http://www.ibm.com/xsp/core">
  
    <xp:button id="button1" value="新規作成">
      <xp:eventHandler event="onclick" submit="true"
        refreshMode="complete">
        <xp:this.action>
          <xp:openPage name="/keydoc.xsp" target="newDocument"></xp:openPage>
        </xp:this.action>
      </xp:eventHandler>
    </xp:button>
    <xp:br></xp:br>
    <xp:viewPanel rows="30" id="viewPanel1" viewStyle="width:90%">
      <xp:this.facets>
        <xp:pager partialRefresh="true" layout="Previous Group Next"
          xp:key="headerPager" id="pager1">
        </xp:pager>
      </xp:this.facets>
      <xp:this.data>
        <xp:dominoView var="keywordlist" viewName="keywordlist"></xp:dominoView>
      </xp:this.data>
      <xp:viewColumn columnName="件名" id="viewColumn1"
        displayAs="link" openDocAsReadonly="true">
        <xp:viewColumnHeader value="件名" id="viewColumnHeader1"></xp:viewColumnHeader>
        <xp:eventHandler event="onclick" submit="true"
          refreshMode="complete">
          <xp:this.action>
            <xp:openPage name="/keydoc.xsp" target="openDocument"></xp:openPage>
          </xp:this.action>
        </xp:eventHandler>
      </xp:viewColumn>
      <xp:viewColumn columnName="キーワード" id="viewColumn2">
        <xp:viewColumnHeader value="キーワード" id="viewColumnHeader2"></xp:viewColumnHeader>
      </xp:viewColumn>
      <xp:viewColumn columnName="その他" id="viewColumn3">
        <xp:viewColumnHeader value="その他" id="viewColumnHeader3"></xp:viewColumnHeader>
      </xp:viewColumn>
    </xp:viewPanel>
  </xp:view>
  

文書用XPage

文書編集/表示用のXPage:keydocはこんな感じ。まだその他用の編集ボックスを隠すようなことはしてない。

  <?xml version="1.0" encoding="UTF-8"?>
  <xp:view xmlns:xp="http://www.ibm.com/xsp/core">
    <xp:this.data>
      <xp:dominoDocument var="dominoDocument1" formName="KeyForm"
        action="openDocument">
      </xp:dominoDocument>
    </xp:this.data>
  
    <xp:table>
      <xp:tr>
        <xp:td>
          <xp:label value="Subject:" id="subject_Label1" for="subject1">
          </xp:label>
        </xp:td>
        <xp:td>
          <xp:inputText value="#{dominoDocument1.subject}" id="subject1">
          </xp:inputText>
        </xp:td>
      </xp:tr>
      <xp:tr>
        <xp:td>
          <xp:label value="Keyword:" id="keyword_Label1" for="keyword1">
          </xp:label>
        </xp:td>
        <xp:td>
          <xp:comboBox value="#{dominoDocument1.keyword}" id="keyword1">
            <xp:selectItem itemLabel="赤" itemValue="赤"></xp:selectItem>
            <xp:selectItem itemLabel="青" itemValue="青"></xp:selectItem>
            <xp:selectItem itemLabel="その他" itemValue="その他"></xp:selectItem>
          </xp:comboBox>
        </xp:td>
      </xp:tr>
      <xp:tr>
        <xp:td>
          <xp:label value="Other:" id="other_Label1" for="other1">
          </xp:label>
        </xp:td>
        <xp:td>
          <xp:inputText value="#{dominoDocument1.other}" id="other1">
          </xp:inputText>
        </xp:td>
      </xp:tr>
    </xp:table>
    <xp:button id="button1" value="編集">
      <xp:eventHandler event="onclick" submit="true"
        refreshMode="complete">
        <xp:this.action>
          <xp:changeDocumentMode mode="edit"></xp:changeDocumentMode>
        </xp:this.action>
      </xp:eventHandler>
    </xp:button>
    <xp:button id="button2" value="保存">
      <xp:eventHandler event="onclick" submit="true"
        refreshMode="complete">
        <xp:this.action>
          <xp:actionGroup>
            <xp:saveDocument></xp:saveDocument>
            <xp:openPage name="/xplist.xsp" target="openDocument"></xp:openPage>
          </xp:actionGroup>
        </xp:this.action>
      </xp:eventHandler>
    </xp:button>
    <xp:button value="取消" id="button3">
      <xp:eventHandler event="onclick" submit="true"
        refreshMode="complete">
        <xp:this.action>
          <xp:openPage name="/xplist.xsp"></xp:openPage>
        </xp:this.action>
      </xp:eventHandler>
    </xp:button>
  </xp:view>
  

1回目の失敗

keywordで選択した結果はdominoDocument1.keywordに入るようになっているのだから、other用の<xp:inputText>の表示制御式にdominoDocument1.keyword をチェックする式をかけばいいように思える。

そこで、以下のコードを追加してみた。

  01: <xp:inputText value="#{dominoDocument1.other}" id="other1">
  02:   <xp:this.rendered><![CDATA[#{javascript:
  03:     if (dominoDocument1.getItemValue('keyword')[0] == "その他") {
  04:       return true;
  05:     } else {
  06:       return false;
  07:     }
  08:   }]]></xp:this.rendered>
  09: </xp:inputText>
  

実行結果

なんとなく、うまくいきそうだが、keywordをどんなに変更しても、otherを入力するフィールドは非表示のまま。

keywordを変更しても、dominoDocument1.keywordは変化しないのかと思い、以下のようなコードを書いてみた。これは、編集コントロールの値に、dominoDocument1.keywordの値を表示してみようという試みだ。

  <xp:inputText id="other1" value="#{javascript:dominoDocument1.getItemValue('keyword')}">
  </xp:inputText>
  

これを実行すると、keywordを変更するごとに、その下の編集コントロールが赤、青、その他と切り替わる。つまり、keywordを変更すればdominoDocument1.keywordの値はちゃんと変更されているのは確かみたい。 そうでなきゃ、保存時に選択した結果が文書内に保存されるわけもないし。

ただ、<xp:this.rendered>でdominoDocument1.keywordの値を参照しても、表示/非表示はまったく切り替わらないのも事実。

と、なると、

ということか。

正解

どうやら、コントロールの表示/非表示を制御する式を再評価するには、画面全体を更新してやらないといけないみたい。 それには、以下の部分を設定する。

onchangeイベントの処理

その結果、comboBox部のコードは以下のようになる。

  01: <xp:comboBox value="#{dominoDocument1.keyword}" id="keyword1">
  02:   <xp:selectItem itemLabel="赤" itemValue="赤"></xp:selectItem>
  03:   <xp:selectItem itemLabel="青" itemValue="青"></xp:selectItem>
  04:   <xp:selectItem itemLabel="その他" itemValue="その他"></xp:selectItem>
  05:   <xp:eventHandler event="onchange" submit="true"
  06:     refreshMode="complete">
  07:   </xp:eventHandler>
  08: </xp:comboBox>
  09: ---- ちょっと省略 ----
  11: <xp:inputText value="#{dominoDocument1.other}" id="other1">
  12:   <xp:this.rendered><![CDATA[#{javascript:
  13:     if (dominoDocument1.getItemValue('keyword')[0] == "その他") {
  14:       return true;
  15:     } else {
  16:       return false;
  17:     }
  18:   }]]></xp:this.rendered>
  19: </xp:inputText>