Ember.Component(块形式):多个插座{{yield}}

Cod*_*oet 25 handlebars.js ember.js ember.js-view

我看到ember有一个非常好的机制,可以使用此处{{yield}}记录的机制将内容包装到组件中.

因此,要使用文档中的示例,我可以blog-post像这样定义组件模板:

<script type="text/x-handlebars" id="components/blog-post">
  <h1>{{title}}</h1>
  <div class="body">{{yield}}</div>
</script>
Run Code Online (Sandbox Code Playgroud)

然后我可以blog-post使用以下格式嵌入任何其他模板:

{{#blog-post title=title}}
  <p class="author">by {{author}}</p>
  {{body}}
{{/blog-post}} 
Run Code Online (Sandbox Code Playgroud)

我的问题是,我可以{{yield}}在组件模板中指定两个不同的插座吗?

像这样的东西可以通过Named Outlets来实现,Ember.Route#renderTemplate如下所示:

把手:

<div class="toolbar">{{outlet toolbar}}</div>
<div class="sidebar">{{outlet sidebar}}</div>
Run Code Online (Sandbox Code Playgroud)

JavaScript:

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

我不确定我是否可以将这条路径用于一个不知道路由模板将呈现它的组件.

编辑1:


为了清楚起见,我正在尝试将Android Swipe for Action Pattern实现为Ember组件.

所以,我希望这个组件的用户能够指定两个不同的模板:

  1. 正常列表项的模板,和
  2. 检测到滑动(1)时显示的操作的模板.

我想把它变成一个组件,因为相当多的javascript用于处理触摸(开始/移动/结束)事件,同时仍然管理基于平滑触摸的列表滚动.用户将提供两个模板,该组件将管理触摸事件和必要动画的处理.

我已经设法让组件以块形式工作,块的内容被视为(1).第二个模板(2)通过参数(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>
Run Code Online (Sandbox Code Playgroud)

调用把手模板:

{{#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}}
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

问题是,来自用户提供的部分动作的动作sfaClickedAction不会从组件冒出来.第4段的文件中提到了一个事实.

所以,现在我不知道用户如何捕获他在提供的操作模板中定义的操作.组件无法捕获这些操作,因为它也不知道它们.

编辑2


在这里提出一个跟进问题

And*_*aus 29

这篇博文描述了Ember 1.10+最优雅的解决方案:https://coderwall.com/p/qkk2zq/components-with-structured-markup-in-ember-js-v1-10

在组件中,将yield名称传递给{{yield}}s:

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

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

<footer>
  {{yield "footer"}}
</footer>
Run Code Online (Sandbox Code Playgroud)

当您调用组件时,您接受yield name作为块参数...并使用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}}
Run Code Online (Sandbox Code Playgroud)

PS eq是来自必备的ember-truth-helpers插件的子表达助手.

PPS相关RFC:提案,讨论.


小智 6

我添加了一个CoreView扩展来处理这个(在这篇文章的最后).扩展通过劫持_renderToBuffer方法和

  1. 注册临时Handlebars助手以从组件实例的内部内容中捕获"占位符内容",
  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>
Run Code Online (Sandbox Code Playgroud)

在组件的定义中,包含一个'占位符'属性,用于标识将使用的占位符:

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

然后,在使用组件时,请使用占位符名称,如下所示

{{#bootstrap-dialog}}
  {{#header}}
    Add Product
  {{/header}}
  {{#body}}
    ...form...
  {{/body}}
  {{#footer}}
    <button>Save</button>
  {{/footer}}
{{/bootstrap-dialog}}
Run Code Online (Sandbox Code Playgroud)

这是推广中的代码:

(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);
Run Code Online (Sandbox Code Playgroud)


aha*_*w01 3

由于一个组件中不可能有两个{{yield}}助手(组件如何知道一个{{yield}}标记在哪里停止以及下一个标记在哪里开始?),您也许可以从不同的方向解决这个问题。

考虑嵌套组件的模式。浏览器在这方面已经取得了巨大成功。<ul>以和组件为例<li>。A<ul>想要获取许多标记位并将每个标记像列表的成员一样呈现。为了实现这一点,它强制您将逐项标记分成<li>标签。这方面还有很多其他例子。<table>, <tbody>, <tr>,<td>是另一个很好的例子。

我认为您可能偶然发现了可以实现此模式的情况。例如:

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

  {{#second-thing}}
    ... some other markup
  {{/second-thing}}
{{/sfa-item}}
Run Code Online (Sandbox Code Playgroud)

显然,对于您的专用组件来说,first-thingsecond-thing是糟糕的名称,它们代表您想要用第一个和第二个模板包装的内容。你明白了。

请务必小心,因为嵌套组件无法访问外部组件内的属性。如果外部和内部组件都需要值,则必须将值与外部组件和内部组件绑定。