API · カスタムタグ

マウント

riot.mount(customTagSelector, [opts])

customTagSelector ページから要素を選択し、カスタムタグをそこにマウントします。選択された要素の名前は、カスタムタグの名前と一致していなければなりません。

opts タグに渡すオブジェクトを指定できます (省略可)。ただのシンプルなオブシェクトから、アプリケーションAPIまで、なんでもOKです。あるいは、Fluxストアというのもありです。これは、あなたのクライアントサイドアプリケーションをどのように構築したいかにかかっています。さらに詳しくは、Riot アプリケーションのモジュール性を参照してください。 追加メモ opts引数を介して同じ名前で指定されたものよりも、オプションとしてタグに設定された属性が優先されます。

// <pricing>要素に、カスタムタグをマウントする
var tags = riot.mount('pricing')

// .customerクラスが指定された要素にカスタムタグをマウントする
var tags = riot.mount('.customer')

// <account>をマウントし、APIオブジェクトをオプションとして渡す
var tags = riot.mount('account', api)

@returns: マウントされたタグのインスタンスの配列

メモ: ブラウザ内のコンパイルを使用している場合は、返されたタグのインスタンスを取得するために、riot.mountriot.compileで囲む必要があります。そうしないと、riot.mountundefinedを返します。

riot.compile(function() {
  // タグがコンパイルされ、同期的にriot.mountが動作
  var tags = riot.mount('*')
})

>=3.12.0

引数 opts は複数のタグインスタンス間で同じオブジェクトを共有しないようにする関数にもなりえます。riot/2613

riot.mount('my-tag', function() {
  return {
    custom: 'option'
  }
})

riot.mount(‘*’, [opts])

ページ上のすべてのカスタムタグをマウントするのに、Riot特有のセレクタとして”*“が使えます。

riot.mount('*')

@returns: マウントされたタグのインスタンスの配列

riot.mount(selector, tagName, [opts])

上記、それぞれは…

// カスタムタグ"my-tag"をdiv#mainにマウントして、オプションとしてapiを渡す
var tags = riot.mount('div#main', 'my-tag', api)

@returns: マウントされたタグのインスタンスの配列

riot.mount(domNode, tagName, [opts])

tagNamedで指定する名称のカスタムタグを、与えられたdomNodeに対して、任意のデータをoptsで渡しつつマウントします。例:

// #slideノードに"users"タグをマウントし、オプションとしてapiを渡す
riot.mount(document.getElementById('slide'), 'users', api)

@returns: マウントされたタグのインスタンスの配列

riot.unregister(tagName)

事前にコンパイラーあるいはriot.tag()から作成されたタグを登録解除します。 このメソッドはアプリケーションをテストする際、同じ名称を使って複数回 タグを作る必要があるときに、便利です。例:

// テストタグを作る
riot.tag('test-tag', '<p>{ message }</p>')

// マウントする
var tag = riot.mount(document.createElement('div'), 'test-tag')[0]
expect(tag.root.querySelector('p')).to.be.ok

// マウントを解除する
riot.unregister('test-tag')

// 異なるテンプレートで同じタグを再作成
riot.tag('test-tag', '<div>{ message }</div>')

レンダリング

riot.render(tagName, [opts])

タグをHTMLとしてレンダリング。 このメソッドは サーバサイド でのみ利用可。 例:

// "my-tag"をHTMLにレンダリング
var mytag = require('my-tag')
riot.render(mytag, { foo: 'bar' })

@returns: HTMLとしてレンダリングされたタグ

riot.require(tagPath, [opts])

実行時にRiotタグを読み込んでコンパイルします。 このメソッドは サーバサイド でのみ利用可。 基本的にはrequire('./my-tag.tag')のような形になりますが、riot-compilerのオプションを指定してコンパイルすることもできます。例えば、同時にプリプロセッサを使って読み込むことができます。

var tag = riot.require('./my-tag.jade', { template: 'jade' })

@returns: タグの名前

riot.renderAsync(tagName, [opts])

タグを非同期にHTMLにレンダリング。 このメソッドは サーバサイド でのみ利用可。 このメソッドはプロミスを返します。マウントするプロセスの中で、”ready”イベントがトリガーされた時だけ解決されます。例:

サーバ上で:

riot.renderAsync(tagName, opts)
  .then(function(html) {
    // HTMLを使って何かする
  })
  .catch(function(e) {
    // 時間がかかりすぎ!
  })

タグの中で:

<async-rendering>
  <p>{ message }</p>

  <script>
    this.message = 'hi'

    setTimeout(function() {
      // "ready"イベントをトリガーし、プロミスを解決させる
      this.trigger('ready')
    }.bind(this), 500)
  </script>
</async-rendering>

気を付けるべき重要な点として、”ready”イベントがトリガーされなかった場合、プロミスは1秒後にリジェクトされます。 タイムアウト時間はについては、riot.settings.asyncRenderTimeoutから設定できます。(デフォルトは1000ms)

@returns: プロミス

タグのインスタンス

次のプロパティが、それぞれのタグのインスタンスにセットされます:

これらの参照はHTMLとJavaScriptのコードの双方から利用できます。例:

<my-tag>
  <h3>{ opts.title }</h3>

  var title = opts.title
</my-tag>

自由に、好きなデータをインスタンスに対してセットできます (インスタンスを「コンテキスト」と言い換えても構いません)。それらは、HTML内のテンプレート変数からも利用可能です。例:

<my-tag>
  <h3>{ title }</h3>

  this.title = opts.title
</my-tag>

メモ: もしグローバル変数があれば、HTMLとJavaScriptのコードの両方でこれらの参照を使用することもできます:

window.someGlobalVariable = 'Hello!'
<my-tag>
  <h3>{ someGlobalVariable }</h3>

  var message = someGlobalVariable
</my-tag>

更新

this.update()

現在のタグインスタンス上のすべてのテンプレート変数と、子要素もすべて同様に更新します。ユーザがアプリケーションとインタラクションした際にイベントハンドラが呼ばれると、毎回このメソッドが呼ばれます。

イベントオブジェクトにpreventUpdate = trueをセットすると、Riotによる自動更新を省略することができます。例:

<my-tag>
  <button onclick={ click }>{ message }</button>

  <script>
    this.message = 'hi'

    click(e) {
      e.preventUpdate = true // タグは自動では更新されない
      this.message = 'goodbye'
    }
  </script>
</my-tag>

それ以外の場合には、RiotはUIを自動的に更新しないため、このメソッドを手動で呼ぶ必要があります。典型的には、UIに関係のないイベント、つまりsetTimeoutやAJAX呼び出し、あるいはサーバのイベントの後、などが該当します。例:

<my-tag>

  <input name="username" onblur={ validate }>
  <span class="tooltip" show={ error }>{ error }</span>

  <script>
    validate() {
      $.get('/validate/username/' + this.username.value)
        .fail(function(error_message) {
          this.error = error_message
          this.update()
        }.bind(this))
    }
  </script>
</my-tag>

上の例では、update()が呼ばれた後、エラーメッセージがUIに表示されます。(私たちのネットワークコールのfailハンドラで.bind(this)が使われているため、タグインスタンスを参照してthisを使い続けることができます。)

より細かくタグのDOMの更新をコントロールしたい場合は、カスタムのshouldUpdate関数を用意することができます。この関数がtrueを返した場合のみ、タグが更新されます。

<my-tag>
  <button onclick={ click }>{ message }</button>

  <script>
    this.message = 'hi'

    click(e) {
      this.message = 'goodbye'
    }
    // ここのデータはupdateメソッドに渡したもの
    // この場合、this.updateはundefined
    shouldUpdate(data, nextOpts) {
      // 更新しない
      if (this.message === 'goodbye') return false
      // this.messageが'goodbye'ならば、タグを更新可能
      return true
    }
  </script>
</my-tag>

shouldUpdateメソッドは常に2つの引数を受け取ります: 1つ目はtag.updateメソッドで更新したい値を含み、2つ目の引数はタグ属性を通して受け取った新しいオプションです。通常はoptsオブジェクトに格納されます。

<my-tag>
  <child-tag message={ message }></child-tag>
  <button onclick={ click }>Say goodbye</button>

  <script>
    this.message = 'hi'

    click(e) {
      this.message = 'goodbye'
    }
  </script>
</my-tag>

<child-tag>
  <p>{ opts.message }</p>

  <script>
    shouldUpdate(data, nextOpts) {
      // 受け取った新しいオプションに応じてDOMを更新
      return nextOpts.message !== 'goodbye'
    }
  </script>
</child-tag>

this.update(data)

現在のインスタンスに値をセットして、テンプレート変数を更新します。これはthis.update()と同様ですが、呼び出しと同時にコンテキストデータをセットすることを可能にします。つまり、このように書く代わりに:

self.error = error_message
self.update()

次のように書くことができます:

self.update({ error: error_message })

この方が短くてすっきりしてますね。

riot.update()

ページ上のすべてのタグとそのテンプレート変数を更新します。

@returns: ページにマウントされたタグのインスタンスの配列。

マウントの解除

this.unmount(keepTheParent)

タグとその子孫をページから取り除きます。”unmount”イベントが発火します。 もし、親タグを削除せずにマウント解除したい場合は、unmountメソッドにtrueを渡す必要があります。

DOMからタグを取り除きます:

mytag.unmount()

タグの子孫を取り除き、親タグだけ残します:

mytag.unmount(true)

入れ子のタグ

入れ子になった(ネストされた)タグのインスタンスには、tags変数からアクセスできます:

<my-tag>

  <child></child>
  <script>
    this.on('mount', function() {
      // 子タグにアクセス
      var child = this.tags.child
    })
  </script>
</my-tag>

もし、ひとつ以上の同じ子タグが使われた場合は、配列this.tags.child[n]としてアクセスされます。

入れ子のタグにref属性で別名を与えてアクセスすることもできます。

<my-tag>

  <child ref="my_nested_tag"></child>

  <script>
    this.on('mount', function() {
      // 子タグにアクセス
      var child = this.refs.my_nested_tag
    })
  </script>
</my-tag>

子タグは親タグの後に初期化されるので、メソッドとプロパティは”mount”イベントで利用できます。

<my-tag>

  <child ref="my_nested_tag"></child>

  <script>
    // 子タグのメソッドにアクセス
    this.on('mount', function() {
      this.refs.my_nested_tag.someMethod()
      // this.tags.child.someMethod() も有効
    })
  </script>
</my-tag>

YieldによるHTMLの入れ子

<yield>タグは、実行時にそのテンプレートの中にカスタムタグの内容を注入し、コンパイルすることを可能にする、特別なRiotのコア機能です。 例として、以下のRiotタグmy-postを使います

<my-post>
  <h1>{ opts.title }</h1>
  <yield/>

  <script>
    this.id = 666
  </script>
</my-post>

<my-post>はアプリケーションの好きな場所に含めることができます

<my-post title="What a great title">
  <p id="my-content-{ id }">My beautiful post is just awesome</p>
</my-post>

一度riot.mount('my-post')でマウントされると、次のようにレンダリングされます:

<my-post>
  <h1>What a great title</h1>
  <p id="my-content-666">My beautiful post is just awesome</p>
</my-post>

注意 生成された式はそれらが含まれるコンテキストから常に評価されます。例えば

<!-- このタグは生成されたDOMを継承します -->
<child-tag><yield/></child-tag>

<my-tag>
  <child-tag>
    <p>{ parent.message }</p>
  </child-tag>
  <script>
    // 生成されたマークアップがparent.messageを指すことに注意してください
    // { message }は<child-tag>コンテキストの下で評価されるため、ここでは間違いです
    this.message = 'hi'
  </script>
</my-tag>

複数のyield

<yield>タグは、テンプレートの特定のスロットにhtmlコンテンツを挿入するためのスロット機構も提供します。

例えば、次のRiotタグmy-other-postを使うとすると、

<my-other-post>
  <article>
    <h1>{ opts.title }</h1>
    <h2><yield from="summary"/></h2>
    <div>
      <yield from="content"/>
    </div>
  </article>
</my-other-post>

<my-other-post>タグはアプリケーションの好きな場所に含めることができます

<my-other-post title="What a great title">
  <yield to="summary">
    My beautiful post is just awesome
  </yield>
  <yield to="content">
    <p>And the next paragraph describes just how awesome it is</p>
    <p>Very</p>
  </yield>
</my-other-post>

一度riot.mount('my-other-post')でマウントすると、以下のようにレンダリングされます:

<my-other-post>
  <article>
    <h1>What a great title</h1>
    <h2>My beautiful post is just awesome</h2>
    <div>
      <p>And the next paragraph describes just how awesome it is</p>
      <p>Very</p>
    </div>
  </article>
</my-other-post>

Yieldとループ

<yield />タグはループや子タグの中で使うことができますが、 常に子タグのコンテキストでパースされ、コンパイルされる ことに注意してください。

以下のRiotコンポーネントblog.tagは、

<blog>
  <h1>{ title }</h1>
  <my-post each={ posts }>
    <a href={ this.parent.backToHome }>Back to home</a>
    <div onclick={ this.parent.deleteAllPosts }>Delete all the posts</div>
  </my-post>

  <script>
    this.backToHome = '/homepage'
    this.title = 'my blog title'

    this.posts = [
      { title: "post 1", description: 'my post description' },
      { title: "post 2", description: 'my post description' }
    ]

    // この場合、バインドは親コンテキストを
    // 子タグ内にも保持するために必要です
    deleteAllPosts() {
      this.posts = []

      // update関数を手動でトリガーする必要があります
      // なぜなら、この関数は子タグからトリガーされ、
      // 自動でバブルアップしないからです
      this.update()
    }
  </script>
</blog>

<my-post>
  <h2>{ title }</h2>
  <p>{ description }</p>
  <yield/>
</my-post>

次のようにコンパイルされます:

<blog>
  <h1>my blog title</h1>
  <my-post>
    <h2>post 1</h2>
    <p>my post description</p>
    <a href="/homepage">Back to home</a>
    <div>Delete all the posts</div>
  </my-post>
  <my-post>
    <h2>post 2</h2>
    <p>my post description</p>
    <a href="/homepage">Back to home</a>
    <div>Delete all the posts</div>
  </my-post>
</blog>

ミックスイン

this.mixin(mixinObject)

現在のタグをmixinObjectで利用可能な機能で拡張します。例:

var OptsMixin = {
  // initメソッドは、タグにロードされ、
  // かつミックスインしたタグからアクセスできないときに
  // ミックスインを初期化できる特別なものです
  // ここでは、`opts`はタグで受け取ったオプションオブジェクトです
  init: function(opts) {
    this.on('updated', function() { console.log('Updated!') })
  },

  getOpts: function() {
    return this.opts
  },

  setOpts: function(opts, update) {
    this.opts = opts
    if (!update) this.update()
    return this
  }
}

<my-tag>
  <h1>{ opts.title }</h1>

  this.mixin(OptsMixin)
</my-tag>

riot.mixin(mixinName, mixinObject)

共有ミックスインを登録します。どのタグからもグローバルに利用可能になります: this.mixin(mixinName)

riot.mixin(mixinObject)

グローバルミックスインを登録します。すべてのタグインスタンスに自動的に追加されます。

イベント

それぞれのタグインスタンスはオブザーバブルなので、ononeメソッドをタグで起きるイベント監視のために使えます。サポートされているイベントは次のとおり:

例:

// タグ以降のクリーンアップされたりそーすはもはやDOMの一部ではありません
this.on('unmount', function() {
  clearTimeout(timer)
})

予約語

上記のメソッド名とプロパティ名は、Riotタグの予約語です。次のいずれもインスタンス変数やメソッド名として使ってはいけません: opts, parent, tags, root, refs, update, unmount, on, off, one, trigger。またアンダースコアから始まる変数名(this._itemなど)も予約されています。ローカル変数については、自由に名前付けできます。例:

<my-tag>

  // OK
  function update() { }

  // ダメ
  this.update = function() { }

  // ダメ
  update() {

  }

</my-tag>

手動でのタグ構築

riot.tag(tagName, html, [css], [attrs], [constructor])

新しいカスタムタグを「手動」でコンパイラを使わずに作成します。

riot.tag('timer',
  '<p>Seconds Elapsed: { time }</p>',
  'timer { display: block; border: 2px }',
  'class="tic-toc"',
  function (opts) {
    var self = this
    this.time = opts.start || 0

    this.tick = function () {
      self.update({ time: ++self.time })
    }

    var timer = setInterval(this.tick, 1000)

    this.on('unmount', function () {
      clearInterval(timer)
    })

  })

詳細と 制約 については、タイマーのデモriot.tagのAPIドキュメントを参照してください。

警告 riot.tagを使うことで、コンパイラの長所や以下のような機能が使えなくなります:

  1. 自己閉じタグ
  2. コーテーションなしのテンプレート変数。attr={ val }の代わりに、attr="{ val }"と書くこと
  3. ES6のメソッドの省略記法
  4. 不正なサーバリクエストを防止するために、<img src={ src }><img riot-src={ src }>と書かなくてはならない
  5. IEの予期しない問題を避けるために、<input value={ val }><img riot-value={ val }>と書かなくてはならない
  6. スタイル属性式がIEでも動作するように、style="color: { color }"riot-style="color: { color }"のように書かなくてはならない
  7. Scoped CSSのプリコンパイル

次のように書くことで<template><script>タグの利点を生かすことはできます:

<script type="tmpl" id="my_tmpl">
  <h3>{ opts.hello }</h3>
  <p>And a paragraph</p>
</script>

<script>
riot.tag('tag-name', my_tmpl.innerHTML, function(opts) {

})
</script>

テンプレートを使用しないタグ

>=3.5.0

Riot 3.5からは、次のようなテンプレートを使用せずに”ラッパータグ”を作成することができます:

riot.tag('tag-name', false, function(opts) {
  this.message = 'hi'
})

この場合、いつでもtag-nameという名前のタグをマウントします。Riotは手付かずのタグマークアップを解析し、そのタグに含まれている式だけをレンダリングします。

<html>
<body>
  <tag-name>
    <p>I want to say { message }</p>
  </tag-name>
</body>
</html>

このテクニックは、サーバーサイドレンダリングされたテンプレートを強化するために使用されるでしょう。 また、アプリケーションロジックを処理できるラッパータグと通信する、ロジックに依存しない子コンポーネントをラップするような新しいタグの構成パターンを作成することも可能になります。

<form-validator>
  <date-picker onchange={ updateDate } />
  <color-picker onchange={ pickAColor } />
  <errors-reporter if={ errors.length } errors={ errors }/>
</form-validator>

riot.Tag(el, [opts])

riot.Tagコンストラクタを使うと、es6クラス記法でタグを作成・拡張することが可能になります。 Riotタグを作るには、riot.Tagコンストラクタを拡張する必要があります:

class MyTag extends riot.Tag {
  // mandatory in order to use and identify this component
  get name() {
    return 'my-tag'
  }
  get tmpl() {
    return `<p onclick="{ click }">{ message }, Dear user</p>`
  }
  get attrs() {
    return 'class="{ className }"'
  }
  get css() {
    return 'my-tag p{ color: blue; }'
  }
  onCreate(opts) {
    this.message = opts.message
  }
  click() {
    this.message = 'goodbye'
  }
}

new MyTag(
  document.getElementById('my-div'),
  { message: 'hi' }
).mount()

共通の祖先(クラス)からスタートして拡張することで、Riotタグを組み合わせることも可能です。

class Logger extends riot.Tag {
  get name() {
    return 'logger'
  }
  get tmpl() {
    return `<div>{ opts.log }</div>`
  }
}

class ErrorLogger extends Logger {
  get name() {
    return 'error-logger'
  }
  get css() {
    return 'error-logger div { color: red; }'
  }
}

class SuccessLogger extends Logger {
  get name() {
    return 'success-logger'
  }
  get css() {
    return 'success-logger div { color: green; }'
  }
}

new ErrorLogger(
  document.querySelector('.error'),
  { log: 'oups!!!' }
).mount()

new SuccessLogger(
  document.querySelector('.success'),
  { log: 'well done!!!' }
).mount()