12.13.2017

Vue.jsの`ref`属性を使ってVisualforceの要素を取得する

JavaScriptからVisualforceの要素を取得するのってめんどくさいなーって思います。

Visualforceでは設定したidの前にj_idという生成されるidが付与されるため、 既存プロジェクトでは、jQueryを使って$('[id$=name]').val();と後方一致を使って取得していました。 しかし、上記の方法でも後方の文字列が重複した場合100%欲しい要素が返ってくるとも限りません。

また、Visualforceのグローバル変数で$Componentを使った参照方法もありますが、 参照する際DOMの階層構造を辿って行かないといけないので使いやすさはないかも。

そこでVue.jsでref属性でサクッと参照する方法を書いてみます。 (Vue.jsの使い方はここ)。

IdAttributeTest.cls

public with sharing class IdAttributeTest {
  public String m_send_text { get; set; }
  public String m_receive_text { get; set; }

  public IdAttributeTest() {
    m_send_text = 'init';
  }

  public void SaveForm() {
    m_receive_text = m_send_text;
  }
}

id_attribute_test.page

<apex:page controller="IdAttributeTest">
  <div id="app">
    <apex:form id="form">
      <div>送信テキスト:<apex:inputText id="send_text" html-ref="send_text" value="{!m_send_text}" /></div>
      <div>受信テキスト:<apex:outputText value="{!m_receive_text}" /></div>

      <apex:commandButton action="{!SaveForm}" value="保存" />
      <apex:commandButton onclick="window.ClearSendText(); return false;" value="送信テキストを空にする" />
    </apex:form>
  </div>


  <script src="{!URLFOR($Resource.Vue_js_2_5_9, '/vue-2.5.9/dist/vue.min.js')}"></script>


  <script>
    var v = new Vue({
      el: '#app',
      mounted: function() {
        alert(this.$refs.send_text.value);
        this.$refs.send_text.value = 'mounted';
      }
    });

    function ClearSendText() {
      v.$refs.send_text.value = '';
    }
  </script>
</apex:page>

コントローラ側は、送信するテキストを入れるm_send_textと受け取ったテキストを入れるm_receive_textを用意します。 あとは、送信ボタン押されたときに詰め替えてページ側でテキストが送られたことを確認します(オブジェクトの保存はめんどくさいのでやらない)。

ページ側では、ref参照したい要素が必ずVueのelで指定したid要素より子要素になるようにDOMを構成します。 そして、参照したい要素にrefを指定するだけです(apexタグはhtml-を付与してパススルーする)。

画面を読み込みVueのインスタンスを生成し、DOM読み込み後にインスタンスの$refsを参照することで送信テキストを取得しています(コントローラ側のコンストラクタで文字列をセットしているのでinitが入ってくる)。 その後、送信テキストにセットしてmountedに書き換えています。

new Vue();で返ってくるインスタンスは、Vueの関数内でthisを扱うように操作できるので 好きなイベントのタイミングでVueインスタンスを参照して値を書き換えることも可能です。