从Handlebars动态设置视图的属性



我试图通过为常见布局元素创建视图来DRY我的模板。我定义了以下视图

App.SplitListView = Em.View.extend({
    tagName: 'div',
    classNames: [ 'panel', 'panel-default' ]
});
App.SplitListHeaderView = Em.View.extend({
    classNames: [ 'panel-heading' ],
    templateName: 'split-list-header-view-layout'
});

SplitListView的模板是一个简单的{{yield}}

SplitListHeaderView模板为

<span class="panel-title">{{view.headerText}}</span>
{{#if view.showCreateButton}}
    <span class="pull-right">
        <button type="button" class="btn btn-primary btn-sm">
            <i class="fa fa-plus fa-lg" style="padding-right: 10px;"></i>{{view.createButtonText}}
        </button>
    </span>
{{/if}}

子模块的模板:

{{#view App.SplitListView}}
    {{view App.SplitListHeaderView headerTextBinding="Sandwiches" showCreateButtonBinding=true createButtonTextBinding="Make me a sandwich!"}}
{{/view}}

期望的最终结果是,我想在我的应用程序中使用SplitListViewSplitListHeaderView,我使用该布局,只是通过控制器设置相关的文本位。但到目前为止,它还不起作用。我觉得这应该很容易做,我只是遗漏了一些东西。我发现这个问题看起来和我的问题一样,但是解决方案对我不起作用。

有人有这样的经验吗?还是我在尝试以这种方式使用视图时失去了理智?

我相信你有三个选择:

1)使用组件而不是视图。组件是可重用的、自包含的"模块",它允许你从组件的上下文中绑定属性,就像你在视图中绑定属性一样。

2)如果你在当前非dry模板之间重用的唯一东西是html/把手,你应该使用{{partial}}而不是视图,因为它不会在视图层次结构中创建任何作用域,并且允许你将部分中的把手直接绑定到路由的视图/控制器属性,而无需指定额外的属性绑定或作用域。

3)使用Em.Handlebars.helper接受三个参数(headerText, buttonText和showCreateButton),并返回new Handlebars.SafeString('some html string');或沿着这些行。

<<h2> 解决方案/strong>

如果我是你,我会把方法二和方法三结合起来,如下所示:

首先,使用一个帮助器(我使用一个全局可访问的方法的帮助器)代替App.SplitListView在打开和关闭把手内部的缓冲区(即内容)周围包装一些html:

// Handy method you can use across many different areas of the app
// to wrap content in simple html
Utils.wrapBuffer = function(open, close, options, context) {
  options.data.buffer.push('n' + open);
  if (options.fn(context)) {
    options.data.buffer.push('n' + options.fn(context));
  }
  options.data.buffer.push('n' + close);
}

那么实际的助手

App.Handlebars.helper('split-list', function(options) {
  var open = '<div class="panel panel-default">;
  var close = '</div>';
  Utils.wrapBuffer(open, close, options, this);
});
现在,你的模板看起来像:
{{#split-list}}
  // This is where our content will go
{{/split-list}}

这样做的好处是可以在不增加或改变作用域的情况下,用html包装在车把之间的开始和结束标记。因此,属性绑定可以无缝地工作。

现在,用类似方式设置的组件替换App.SplitListheaderView:

App.SplitListHeaderComponent = Em.Component.extend({
  classNames: ['panel-heading'],
  headerText: null,
  createButtonText: null,
  showCreateButton: false,
});

布局(组件使用布局,而不是模板)将位于templates/components/split-list-header.hbs。它看起来像这样:

<span class="panel-title">{{headerText}}</span>
{{#if showCreateButton}}
    <span class="pull-right">
        <button type="button" class="btn btn-primary btn-sm">
            <i class="fa fa-plus fa-lg" style="padding-right: 10px;"></i>{{createButtonText}}
        </button>
    </span>
{{/if}}

注意,属性是something而不是view.something,并且每个属性的声明允许它在handlebars帮助器中被覆盖。现在你的子模块的模板看起来像:

{{#split-list}}
  {{split-list-header
    headerText='Sandwiches'
    showCreateButton=true
    createButtonText='Make me a sandwich!'
  }}
{{/split-list}}

如果你愿意,你可以将这些属性绑定到控制器或视图的属性中,而不是每次都在模板中编写它们。

另一个改进

您可以更进一步,将包装帮助器全部丢弃,因为它除了添加HTML之外没有做任何事情。在本例中,该组件看起来像{{split-list headerText=blah...}},并且具有以下模板:

<div class="panel-heading">
  <span class="panel-title">{{view.headerText}}</span>
  {{#if view.showCreateButton}}
    <span class="pull-right">
      <button type="button" class="btn btn-primary btn-sm">
        <i class="fa fa-plus fa-lg" style="padding-right: 10px;"></i>{{view.createButtonText}}
      </button>
    </span>
  {{/if}}
</div>

最新更新