web-dev-qa-db-ja.com

Ember.Component(ブロックフォーム):複数のアウトレット{{yield}}

emberには、文書化されている{{yield}}メカニズムを使用してコンポーネント内のコンテンツをラップするための非常に優れたメカニズムがあることがわかります ここ

したがって、ドキュメントの例を使用するには、次のようにblog-postコンポーネントテンプレートを定義できます。

<script type="text/x-handlebars" id="components/blog-post">
  <h1>{{title}}</h1>
  <div class="body">{{yield}}</div>
</script>

次に、次のフォームを使用して、blog-postを他のテンプレートに埋め込むことができます。

{{#blog-post title=title}}
  <p class="author">by {{author}}</p>
  {{body}}
{{/blog-post}} 

私の質問は、コンポーネントテンプレートで2つの異なる{{yield}}アウトレットを指定できますか?

このようなことは、次のようにEmber.Route#renderTemplateの-​​ Named Outlets を介して可能です。

ハンドルバー

<div class="toolbar">{{outlet toolbar}}</div>
<div class="sidebar">{{outlet sidebar}}</div>

JavaScript

App.PostsRoute = Ember.Route.extend({
  renderTemplate: function() {
    this.render({ outlet: 'sidebar' });
  }
});

どのルートのテンプレートがそれをレンダリングするかわからないコンポーネントに対して、このパスを取ることができるかどうかはわかりません。

編集1:


わかりやすくするために、Android Swipe for Action Pattern をEmberコンポーネントとして実装しようとしています。

したがって、このコンポーネントのユーザーが2つの異なるテンプレートを指定できるようにしたいと思います。

  1. 通常のリストアイテムのテンプレート、および
  2. (1)のスワイプが検出されたときに表示されるアクションのテンプレート。

これをコンポーネントにしたいのは、リストの スムーズなタッチベースのスクロール を管理しながら、かなり多くのjavascriptがtouch(start/move/end)イベントの処理に使用されるためです。ユーザーは2つのテンプレートを提供し、このコンポーネントはタッチイベントと必要なアニメーションの処理を管理します。

コンポーネントをブロック形式で機能させることができました。ブロックの内容は(1)のように扱われます。 2番目のテンプレート(2)は、アクションの partial テンプレートの名前であるパラメーター(以下のactionPartial)を介して指定されます。

コンポーネントハンドルバーテンプレート:sfa-item.handlebars

<div {{bind-attr class=":sfa-item-actions shouldRevealActions:show" }}>
    {{partial actionPartial}}
</div>

<div {{bind-attr class=":sfa-item-details isDragging:dragging shouldRevealActions:moveout"}}>
    {{yield}}
</div>

ハンドルバーテンプレートの呼び出し:

{{#each response in controller}}
    <div class="list-group-item sf-mr-item">
        {{#sfa-item actionPartial="mr-item-action"}}
            <h5>{{response.name}}</h5>
        {{/sfa-item}}
    </div>
{{/each}}

mr-item-actionハンドルバーが次のように定義されている場合:

mr-item-action.handlebars

<div class="sf-mr-item-action">
    <button class="btn btn-lg btn-primary" {{action 'sfaClickedAction'}}>Edit</button>
    <button class="btn btn-lg btn-primary">Delete</button>
</div>

問題は、上記のsfaClickedActionの部分的なユーザー指定のアクションが、コンポーネントからバブルアップされないことです。 docs パラ4で言及されている事実。

そのため、ユーザーが提供されたアクションテンプレートで定義したアクションをキャプチャする方法がわかりません。コンポーネントはそれらについても知らないため、それらのアクションをキャッチできません。

編集2


フォローアップの質問をしました ここ

25
Code Poet

1つのコンポーネント内に2つの{{yield}}ヘルパーを含めることはできないため(コンポーネントは、1つの{{yield}}のマークアップが停止し、次のマークアップが開始する場所をどのように知るのでしょうか?)、別の方法でこの問題に取り組むことができる場合があります。方向。

ネストされたコンポーネントのパターンを検討してください。ブラウザはすでにこれを行っており、大きな成功を収めています。たとえば、<ul>および<li>コンポーネントを取り上げます。 <ul>は、マークアップの多くのビットを取得し、それぞれをリストのメンバーのようにレンダリングしたいと考えています。これを実現するために、項目化されたマークアップを<li>タグに分割する必要があります。これには他にも多くの例があります。 <table><tbody><tr><td>も良い例です。

このパターンを実装できるケースに出くわしたのではないかと思います。例えば:

{{#sfa-item}}
  {{#first-thing}}
     ... some markup
  {{/first-thing}}

  {{#second-thing}}
    ... some other markup
  {{/second-thing}}
{{/sfa-item}}

明らかに、first-thingsecond-thingは、最初と2番目のテンプレートでラップしたいものを表す特殊なコンポーネントのひどい名前です。あなたはその考えを理解します。

ネストされたコンポーネントは外部コンポーネント内のプロパティにアクセスできないため、注意してください。両方で必要な場合は、値を外部コンポーネントと内部コンポーネントの両方にバインドする必要があります。

4
ahaurw01

このブログ投稿では、Ember 1.10 +: https://coderwall.com/p/qkk2zq/components-with-structured-markup-in-ember- js-v1-1

コンポーネントで、yield名を{{yield}}sに渡します。

<header>
  {{yield "header"}}
</header>

<div class="body">
  {{yield "body"}}
</div>

<footer>
  {{yield "footer"}}
</footer>

コンポーネントを呼び出すときは、yield名をブロックパラメータとして受け入れ、esleifチェーンを使用します。

{{#my-comp as |section|}}
  {{#if (eq section "header")}}
    My header
  {{else if (eq section "body")}}
    My body
  {{else if (eq section "footer")}}
    My footer
  {{/if}}
{{/my-comp}}

PS eqは、必須の ember-truth-helpers アドオンの部分式ヘルパーです。

PPS関連RFC: 提案ディスカッション

これを処理するためにCoreViewに拡張機能を追加しました(この投稿の最後に)。拡張機能は、_renderToBufferメソッドをハイジャックすることで機能します。

  1. コンポーネントインスタンスの内部コンテンツから「プレースホルダーコンテンツ」をキャプチャするための一時的なハンドルバーヘルパーを登録します。
  2. コンポーネントを一時的に破棄されたバッファにレンダリングします(基本的に、コンポーネントに代わってyieldを呼び出します)。
  3. キャプチャされた「プレースホルダーコンテンツ」を挿入するためにHandlebarsヘルパーを再登録します。
  4. コンポーネントのテンプレートを通常のようにレンダリングし、
  5. クリーンアップ(ハンドルバーヘルパーを以前の状態に復元する)。

コンポーネントのテンプレートには、次のようなプレースホルダーを使用します

<div class='modal-dialog'>
  <div class='modal-content'>
    <div class='modal-header'>
      {{header}}
    </div>
    <div class='modal-body'>
      {{body}}
    </div>
    <div class='modal-footer'>
      {{footer}}
    </div>
  </div>
</div>

コンポーネントの定義に、使用されるプレースホルダーを識別する「placeholders」プロパティを含めます。

App.BootstrapDialogComponent = Ember.Component.extend({
   placeholders: [ 'header', 'body', 'footer' ],
   ...
});

次に、コンポーネントを使用するときは、次のようにプレースホルダー名を使用します

{{#bootstrap-dialog}}
  {{#header}}
    Add Product
  {{/header}}
  {{#body}}
    ...form...
  {{/body}}
  {{#footer}}
    <button>Save</button>
  {{/footer}}
{{/bootstrap-dialog}}

拡張機能に含まれるコードは次のとおりです。

(function() {

   var _renderToBufferOriginal = Ember.CoreView.create()._renderToBuffer;

   Ember.CoreView.reopen({
      _renderToBuffer: function(buffer, bufferOperation) {
         this.transitionTo('inBuffer', false);

         var placeholders = { }
         if (this.placeholders) {
            this.placeholders.map(function(n) {
               placeholders[n] = {
                  saved: Ember.Handlebars.helpers[n],
                  buffer: null,
               }
               Ember.Handlebars.helpers[n] = function(options) {
                  var tmp = placeholders[n].buffer = placeholders[n].buffer || Ember.RenderBuffer();
                  var saved = options.data.buffer;
                  options.data.buffer = tmp;
                  options.fn(options.contexts[0], options);
                  options.data.buffer = saved;
               };
            });

            Ember.Handlebars.helpers['yield'].call(this, {
               hash: { },
               contexts: [ this.get('context') ],
               types: [ "ID" ],
               hashContexts: { },
               hashTypes: { },
               data: {
                  view: this,
                  buffer: Ember.RenderBuffer(),
                  isRenderData: true,
                  keywords: this.cloneKeywords(),
                  insideGroup: this.get('templateData.insideGroup'),
               }
            });

            this.placeholders.map(function(n) {
               Ember.Handlebars.helpers[n] = function(options) {
                  var str = ((placeholders[n].buffer)? placeholders[n].buffer.innerString(): "");
                  options.data.buffer.Push(str);
               };
            });
         }

         var result = this._renderToBufferOriginal.apply(this, arguments);

         if (this.placeholders) {
            this.placeholders.map(function(n) {
               Ember.Handlebars.helpers[n] = placeholders[n].saved;
            });
         }

         return result;
      },

      _renderToBufferOriginal: _renderToBufferOriginal,
   });

}).call(this);
6
the7thchakra