Help us understand the problem. What is going on with this article?

Ractive.jsのComponentsの使い方

More than 5 years have passed since last update.

Componentsの使いどころ

Ractive.jsはテンプレートとその展開先が必要である関係上、必ずテンプレートの外に「囲み要素」が必要になります。
そうなるとこういった場合に問題が生じます。

1.1_Template
<ul>
  <li>
    <select name="viewer">
      <option value="1">表示領域Aを表示</option>
      <option value="2">表示領域Bを表示</option>
      <option value="3">表示領域Cを表示</option>
    </select>
  </li>
  <li>表示領域A</li>
  <li>表示領域B</li>
  <li>表示領域C</li>
</ul>

セレクトボックスviewerの値の変化によって表示領域ABCの表示を切り替えたいとします。
この場合、どのようにRactiveオブジェクトを構成すればいいのでしょうか。
<li>を囲むとこうなります。

1.2_Template
<ul>
  <div id="container">
    <script id="template" type="text/ractive">
      <li>
        <select name="viewer">
          <option value="1">表示領域Aを表示</option>
          <option value="2">表示領域Bを表示</option>
          <option value="3">表示領域Cを表示</option>
        </select>
      </li>
    </sctipt>
  </div>

  <li>表示領域A</li>
  <li>表示領域B</li>
  <li>表示領域C</li>
</ul>

このテンプレートをRactiveオブジェクトに結び付けて実行するとどうなるか、もう分かると思います。

1.3_Template
<ul>
  <div id="container">
    <li>
      <select name="viewer">
          <option value="1">表示領域Aを表示</option>
          <option value="2">表示領域Bを表示</option>
          <option value="3">表示領域Cを表示</option>
        </select>
    </li>
  </div>

  <li>表示領域A</li>
  <li>表示領域B</li>
  <li>表示領域C</li>
</ul>

<div> が挟まってしまい絶望に包まれることになる。
ここで<ul>全体をRactiveオブジェクトにすることで<select>も表示領域も一括で扱う方法もありますが、そうするとDomの挙動と意味合いを明確に分離できていない、大きくて密結合なモデルになってしまいます。
そこで登場するのがComponentsの概念です。

Componentsの組み方

先ほどのテンプレートを以下の形に分割します。
セレクトボックスと表示領域という2つの概念を固まりに纏めています。

2.1_Template
<ul id="listContainer">
  <script id="listTemplate" type="text/ractive">
    <li>表示領域A</li>
    <li>表示領域B</li>
    <li>表示領域C</li>
  </script>
</ul>

<script id="selectTemplate" type="text/ractive">
  <li>
    <select name="viewer" value="{{selected}}">
      <option value="1">表示領域Aを表示</option>
      <option value="2">表示領域Bを表示</option>
      <option value="3">表示領域Cを表示</option>
    </select>
  </li>
</script>

今回はセレクトボックスの領域をComponentsとして扱うことにします。
というわけでまずはセレクトボックスをComponentsとして定義します。
こちらの操作にはRactiveオブジェクトの拡張を定義するRactive.extend()を利用します。

2.2_SelectComponents
var SelectComponents = Ractive.extend({
  template: '#selectTemplate',
  data: {
    selected: null
  }
});

これでセレクトボックスをコンポーネント化できました。
コンポーネントは展開先が他のRactiveテンプレート内になるので、elの指定は必要ありません。
次に<ul>全体をRactiveオブジェクトとして起動し、セレクトコンポーネントを埋め込みます。

2.3_ListModel
var ListModel = new Ractive({
  el: '#listContainer',
  template: '#listTemplate',
  data: {
    activate: {
      A: false,
      B: false,
      C: false
    }
  },
  components: {
    // ここに埋め込む
    exselect: SelectComponents
  }
});

さて後は仕上げです。
Componentsの展開先はテンプレート内に<コンポーネント名 />と記述することで定義します。
今回の場合は<exselect/>となります。
ということで一気にモデルの関連性まで組み上げてしまいましょう。

サンプルコード

2.4_SampleTemplate
<ul id="listContainer">
  <script id="listTemplate" type="text/ractive">

    // Componentsを展開
    <exselect />

    {{#if activate.A}}
      <li>表示領域A</li>
    {{/if}}

    {{#if activate.B}}
      <li>表示領域B</li>
    {{/if}}

    {{#if activate.C}}
      <li>表示領域C</li>
    {{/if}}
  </script>
</ul>

<script id="selectTemplate" type="text/ractive">
  <li>
    <select name="viewer" value="{{selected}}">
      <option value="1">表示領域Aを表示</option>
      <option value="2">表示領域Bを表示</option>
      <option value="3">表示領域Cを表示</option>
    </select>
  </li>
</script>
2.5_SampleModel
var SelectComponents = Ractive.extend({
  template: '#selectTemplate',
  data: {
    selected: null
  },
  onrender: function(){
     this.observe('selected', function(v){

        // 一旦全部リセット
        this._parent.set('activate.A', false);
        this._parent.set('activate.B', false);
        this._parent.set('activate.C', false);

        // 対応する表示領域をONに
        if (v === '1') {
          this._parent.set('activate.A', true);
        }

        else if (v === '2') {
          this._parent.set('activate.B', true);
        }

        else if (v === '3') {
          this._parent.set('activate.C', true);
        }
     });
  }
});

var ListModel = new Ractive({
  el: '#listContainer',
  template: '#listTemplate',
  data: {
    activate: {
      A: false,
      B: false,
      C: false
    }
  },
  components: {
    // ここに埋め込む
    exselect: SelectComponents
  }
});

これでセレクトボックスの選択に合わせて選択領域がそれぞれ表示されるようになります。
注目するべきはSelectComponentsのobserve内で使用しているthis._parentです。
これで「親」であるListModelにアクセスすることが出来ます。

ただこちら、アンダーバーの付いているプロパティであることから伺うに、もしかするともっと良い親オブジェクトへのアクセス方法があるのかもしれません。
コンポーネントから親Ractiveオブジェクトへアクセスする正しい方法をご存知の方がいらっしゃったら教えてください。

morisuke
Vue.js / Laravelが得意。ご連絡はメールかTwitterでどうぞ。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした