【问题标题】:Dynamically choosing a view at runtime with Ember + Handlebars使用 Ember + Handlebars 在运行时动态选择视图
【发布时间】:2012-01-11 18:31:23
【问题描述】:

我正在使用 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>

也许 data-model 属性可用于确定对象的呈现方式。

{{#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...
});

另外,这是我应该对 Handlebars 做的事情吗?

【问题讨论】:

    标签: ember.js handlebars.js


    【解决方案1】:

    使用 ViewStates,参见示例:

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

    【讨论】:

      【解决方案2】:

      我通过使用 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,我意识到可以通过使用对象上的属性来指定视图。我仍然很想听听解决这个问题的“正确”方法。

      【讨论】:

        【解决方案3】:

        这只是我的想法:我将为每种模型类型创建一个单独的模板/视图。例如。会有DesignViewOrderView 等。每一个都将指定要与 templateName 一起使用的模板(所有代码咖啡脚本):

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

        每种类型的所有自定义渲染都将在视图/模板内完成。

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

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

        可能有更好的方法来做到这一点。我很想听听更接近 Ember 核心的人提供解决方案。

        【讨论】:

          【解决方案4】:

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

          模型“页面”有很多“活动”。

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

          对于 type: 'static',它使用 {{{3 mustache option}}} 来呈现一个 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”

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-09-14
            • 1970-01-01
            • 1970-01-01
            • 2012-08-16
            • 2011-06-29
            • 1970-01-01
            • 2014-01-31
            • 1970-01-01
            相关资源
            最近更新 更多