我试图通过为常见布局元素创建视图来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}}
期望的最终结果是,我想在我的应用程序中使用SplitListView
和SplitListHeaderView
,我使用该布局,只是通过控制器设置相关的文本位。但到目前为止,它还不起作用。我觉得这应该很容易做,我只是遗漏了一些东西。我发现这个问题看起来和我的问题一样,但是解决方案对我不起作用。
有人有这样的经验吗?还是我在尝试以这种方式使用视图时失去了理智?
我相信你有三个选择:
1)使用组件而不是视图。组件是可重用的、自包含的"模块",它允许你从组件的上下文中绑定属性,就像你在视图中绑定属性一样。
2)如果你在当前非dry模板之间重用的唯一东西是html/把手,你应该使用{{partial}}
而不是视图,因为它不会在视图层次结构中创建任何作用域,并且允许你将部分中的把手直接绑定到路由的视图/控制器属性,而无需指定额外的属性绑定或作用域。
3)使用Em.Handlebars.helper
接受三个参数(headerText, buttonText和showCreateButton),并返回new Handlebars.SafeString('some html string');
或沿着这些行。
如果我是你,我会把方法二和方法三结合起来,如下所示:
首先,使用一个帮助器(我使用一个全局可访问的方法的帮助器)代替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>