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

时间:2014-01-07 20:20:03

标签: ember.js handlebars.js ember.js-view

我看到ember有一个非常好的机制,可以使用记录here{{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}} 

我的问题是,我可以在组件模板中指定两个不同的{{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组件。

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

  1. 正常列表项的模板,以及
  2. 检测到滑动(1)时显示的操作的模板。
  3. 我想把它变成一个组件,因为相当多的javascript用于处理触摸(开始/移动/结束)事件,同时仍然管理列表的smooth touch based scrolling。用户将提供两个模板,该组件将管理触摸事件和必要动画的处理。

    我设法让组件以块形式工作,块的内容被视为(1)。第二个模板(2)通过参数(下面的actionPartial)指定,该参数是操作的partial模板的名称:

    组件句柄模板: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-项-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以上的操作不会从组件冒出来。第4段docs中提到的一个事实。

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

    编辑2


    我提出了一个跟进问题here

3 个答案:

答案 0 :(得分:29)

此博客文章介绍了Ember 1.10+最优雅的解决方案:https://coderwall.com/p/qkk2zq/components-with-structured-markup-in-ember-js-v1-10

在您的组件中,您将产量名称传递到qsub -V -b n -cwd /root/remotescript.sh s:

{{yield}}

当您调用组件时,您接受yield name作为块参数...并使用esleif链!

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

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

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

PS {{#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}} 是来自必备ember-truth-helpers插件的子表达式助手。

PPS相关RFC:proposaldiscussion

答案 1 :(得分:6)

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

来工作
  1. 注册临时Handlebars助手以捕获&#39;占位符内容&#39;从组件实例的内部内容
  2. 将组件渲染到临时的丢弃缓冲区(基本上代表组件调用yield),
  3. 重新注册Handlebars助手以注入已捕获的占位符内容&#39;,
  4. 正常渲染组件的模板,
  5. 清理(将Handlebars助手恢复到以前的状态)。
  6. 对于组件的模板,请使用占位符,例如

    <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>
    

    在组件的定义中,包含一个&#39;占位符&#39;标识将使用的占位符的属性:

    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);
    

答案 2 :(得分:4)

由于在一个组件中不可能有两个{{yield}}帮助器(组件如何知道一个{{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是您的专用组件的可怕名称,它们代表您要用第一个和第二个模板包装的内容。你明白了。

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