在运行时使用 Ember + Handlebars 动态选择视图



我正在使用Ember,Ember Data和Handlebars来显示具有许多不同类型模型的时间线。 我目前的实现虽然运行正常,但似乎可以通过约定和帮助程序进行大幅改进。 但是,我不知道如何使用已经定义的模板。

这是我所拥有的:

{{#view App.AccountSelectedView contentBinding="App.selectedAccountController.everythingSorted"}}
  {{#with content}}
    <ol class="timeline">
      {{#each this}}
        {{#is constructor="App.Design"}}
        ... stuff about the design
        {{/is}}
        {{#is constructor="App.Order"}}
        ... stuff about the order
        {{/is}}
        {{#is constructor="App.Message"}}
        ... stuff about the message
        {{/is}}
      {{/each}}
    </ol>
  {{/with}}
{{/view}}

。还有一个帮手...

Handlebars.registerHelper('is', function(options) {
  if (this.constructor == options.hash["constructor"]) {
    return options.fn(this);
  }
});

我宁愿依靠一些约定来弄清楚要呈现的视图。 例如:

<script type="text/x-handlebars-template" data-model="App.Design" id="design-view">
... stuff about the design
</script>
<script type="text/x-handlebars-template" data-model="App.Order" id="order-view">
... stuff about the order
</script>

也许数据模型属性可用于确定对象的呈现方式。

{{#view App.SelectedAccountView contentBinding="App.selectedAccountController.everythingSorted"}}
  {{#with content}}
    <ol class="timeline">
      {{#each this}}
        {{viewish this}}
      {{/each}}
    </ol>
  {{/with}}
{{/view}}

唉,我不知道如何从助手访问模板。

Handlebars.registerHelper('viewish', function(options) {
   // Were I able to access the templates this question
   // would be unnecessary.
   // Handlebars.TEMPLATES is undefined...
});

另外,这是我应该想用车把做的事情吗?

请使用 ViewState,请参阅以下示例:

http://jsfiddle.net/rsaccon/AD2RY/

我通过使用mixin建立自己的约定来解决这个问题。 模型对应于具有相似名称的视图。 例如,App.Design 模型实例对应于视图 App.DesignView。

App.ViewTypeConvention = Ember.Mixin.create({
  viewType: function() {
    return Em.getPath(this.get('constructor') + 'View');
  }.property().cacheable()
});

我把它混合到我的模型中...

App.Design.reopen(App.ViewTypeConvention);
App.Order.reopen(App.ViewTypeConvention);

。并像这样迭代混合集合:

{{#each content}}
  {{view item.viewType tagName="li" contentBinding="this"}}
{{/each}}

这样,我就可以避免在我的模型中显式定义约定。 多亏了 Gordon,我意识到视图可以通过在对象上使用属性来指定。 我仍然非常想听听解决这个问题的"正确"方法。

这只是我的头顶:我会为每个模型类型创建一个单独的模板/视图。 例如,会有DesignViewOrderView等。其中每个都将指定要与 templateName 一起使用的模板(所有代码 coffeescript(:

App.DesignView = Em.View.extend
  templateName: 'design'
App.OrderView = Em.View.extend
  templateName: 'order'

每种类型的所有自定义呈现都将在视图/模板中完成。

此时,我们需要一些模板逻辑来决定为每个项目显示哪个视图。最简单的方法是将视图类型存储在模型上。

App.Design = Em.Model.extend
  viewType: App.DesignView
App.Order = Em.Model.extend
  viewType: App.OrderView

然后模板可能如下所示:

{{#collection contentBinding="App.selectedAccountController.everythingSorted"}}
  {{view content.viewType contentBinding="content"}}
{{/collection}}

但是,这并不理想,因为我们不希望模型知道视图层。相反,我们可以创建一些工厂逻辑来为模型创建视图。然后我们可以在控制器上创建一个计算属性,其中包含模型数组及其相应的视图:

App.selectedAccountController = Em.ArrayController.create
  ..
  viewForModel: (model) ->
    # if model is instance of Design return DesignView, Order return OrderView etc.
  everythingSortedWithViews: ( ->
    everythingSorted.map (model) ->
      {model: model, viewType: @viewForModel(model)}
  ).property('everythingSorted')

然后,模板将如下所示:

{{#collection contentBinding="App.selectedAccountController.everythingSortedWithView"}}
  {{view content.viewType contentBinding="content.model"}}
{{/collection}}

可能有更好的方法可以做到这一点。我很想听到更接近余烬核心的人给出解决方案。

这是我在类似场景中使用的。

模型"页面"有许多"活动"。

// App.PageModel
export default DS.Model.extend({
    index     : DS.attr('number'),
    activity  : DS.hasMany('activity',  { async: true })
});
模型"活动"具有属性">

类型",该属性引用要用于另一个属性"配置"中内容的模板。

// App.ActivityModel
export default DS.Model.extend({
    activityId    : DS.attr('string'),
    type          : DS.attr('string'),
    page          : DS.belongsTo('page', { async: true }),
    configuration : DS.attr()
});

请注意缺少用于配置的属性类型。这提供了存储随机结构对象集合的方法。对于一致的结构化对象,我建议使用 Ember-Data.Model-Fragments。

主模板:

{{! page.hbs }}
{{#with activity}}
    {{#each}}
        {{partial type}}
    {{/each}}
{{/with}}

对于类型:"静态",它使用 {{{3 mustache 选项}}} 来呈现 html 字符串。

{{! static.hbs }}
{{{configuration.content}}}

其他选项要复杂得多,但仍使用"with"进行简化。 即:对于类型:"多项选择",

{{! multiplechoice.hbs }}
{{#with configuration}}
    {{#each options}}
    <label {{bind-attr class=":label selected:checked:unchecked"}}>
        {{view Ember.Checkbox checkedBinding="selected" }}
        {{#if text.content}}
            {{{text.content}}}
        {{else}}
            {{text}}
        {{/if}}
    </label>
    {{/each}}
    {{ ...etc... }}
{{/with}}

对于部分,请记住根据您的环境考虑命名法和/或文件夹结构,即"_partialname.hbs"或"viewname/partialname.hbs">

最新更新