Javascript 在指令中自定义模板
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10629238/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Customizing the template within a Directive
提问by Marty Pitt
I have a form that is using markup from Bootstrap, like the following:
我有一个使用 Bootstrap 标记的表单,如下所示:
<form class="form-horizontal">
<fieldset>
<legend>Legend text</legend>
<div class="control-group">
<label class="control-label" for="nameInput">Name</label>
<div class="controls">
<input type="text" class="input-xlarge" id="nameInput">
<p class="help-block">Supporting help text</p>
</div>
</div>
</fieldset>
</form>
There's a lot of boilerplate code in there, that I'd like to reduce to a new directive - form-input, like follows:
那里有很多样板代码,我想将它们简化为一个新指令 - 表单输入,如下所示:
<form-input label="Name" form-id="nameInput"></form-input>
generates:
产生:
<div class="control-group">
<label class="control-label" for="nameInput">Name</label>
<div class="controls">
<input type="text" class="input-xlarge" id="nameInput">
</div>
</div>
I have this much working via a simple template.
我通过一个简单的模板进行了这么多工作。
angular.module('formComponents', [])
.directive('formInput', function() {
return {
restrict: 'E',
scope: {
label: 'bind',
formId: 'bind'
},
template: '<div class="control-group">' +
'<label class="control-label" for="{{formId}}">{{label}}</label>' +
'<div class="controls">' +
'<input type="text" class="input-xlarge" id="{{formId}}" name="{{formId}}">' +
'</div>' +
'</div>'
}
})
However it's when I come to add in more advanced functionality that I'm getting stuck.
然而,当我开始添加更高级的功能时,我陷入了困境。
How can I support default values in the template?
如何支持模板中的默认值?
I'd like to expose the "type" parameter as an optional attribute on my directive, eg:
我想在我的指令中将“type”参数公开为可选属性,例如:
<form-input label="Password" form-id="password" type="password"/></form-input>
<form-input label="Email address" form-id="emailAddress" type="email" /></form-input>
However, if nothing is specified, I'd like to default to "text"
. How can I support this?
但是,如果没有指定任何内容,我想默认为"text"
. 我该如何支持?
How can I customize the template based on the presence / absence of attributes?
如何根据属性的存在/不存在自定义模板?
I'd also like to be able to support the "required" attribute, if it's present. Eg:
我还希望能够支持“必需”属性(如果存在)。例如:
<form-input label="Email address" form-id="emailAddress" type="email" required/></form-input>
If required
is present in the directive, I'd like to add it to the generated <input />
in the output, and ignore it otherwise. I'm not sure how to achieve this.
如果required
指令中存在,我想将它添加到<input />
输出中生成的,否则忽略它。我不确定如何实现这一目标。
I suspect these requirements may have moved beyond a simple template, and have to start using the pre-compile phases, but I'm at a loss where to start.
我怀疑这些要求可能已经超出了一个简单的模板,必须开始使用预编译阶段,但我不知道从哪里开始。
回答by Misko Hevery
angular.module('formComponents', [])
.directive('formInput', function() {
return {
restrict: 'E',
compile: function(element, attrs) {
var type = attrs.type || 'text';
var required = attrs.hasOwnProperty('required') ? "required='required'" : "";
var htmlText = '<div class="control-group">' +
'<label class="control-label" for="' + attrs.formId + '">' + attrs.label + '</label>' +
'<div class="controls">' +
'<input type="' + type + '" class="input-xlarge" id="' + attrs.formId + '" name="' + attrs.formId + '" ' + required + '>' +
'</div>' +
'</div>';
element.replaceWith(htmlText);
}
};
})
回答by Janusz Gryszko
Tried to use the solution proposed by Misko, but in my situation, some attributes, which needed to be merged into my template html, were themselves directives.
尝试使用 Misko 提出的解决方案,但在我的情况下,一些需要合并到我的模板 html 中的属性本身就是指令。
Unfortunately, not all of the directives referenced by the resulting template did work correctly. I did not have enough time to dive into angular code and find out the root cause, but found a workaround, which could potentially be helpful.
不幸的是,并非生成的模板引用的所有指令都能正常工作。我没有足够的时间深入研究 angular 代码并找出根本原因,但找到了一个可能有用的解决方法。
The solution was to move the code, which creates the template html, from compile to a template function. Example based on code from above:
解决方案是将创建模板 html 的代码从编译移动到模板函数。基于上述代码的示例:
angular.module('formComponents', [])
.directive('formInput', function() {
return {
restrict: 'E',
template: function(element, attrs) {
var type = attrs.type || 'text';
var required = attrs.hasOwnProperty('required') ? "required='required'" : "";
var htmlText = '<div class="control-group">' +
'<label class="control-label" for="' + attrs.formId + '">' + attrs.label + '</label>' +
'<div class="controls">' +
'<input type="' + type + '" class="input-xlarge" id="' + attrs.formId + '" name="' + attrs.formId + '" ' + required + '>' +
'</div>' +
'</div>';
return htmlText;
}
compile: function(element, attrs)
{
//do whatever else is necessary
}
}
})
回答by JoeS
The above answers unfortunately don't quite work. In particular, the compile stage does not have access to scope, so you can't customize the field based on dynamic attributes. Using the linking stage seems to offer the most flexibility (in terms of asynchronously creating dom, etc.) The below approach addresses that:
不幸的是,上述答案并不完全有效。特别是,编译阶段无法访问范围,因此您无法根据动态属性自定义字段。使用链接阶段似乎提供了最大的灵活性(在异步创建 dom 等方面)以下方法解决了这个问题:
<!-- Usage: -->
<form>
<form-field ng-model="formModel[field.attr]" field="field" ng-repeat="field in fields">
</form>
// directive
angular.module('app')
.directive('formField', function($compile, $parse) {
return {
restrict: 'E',
compile: function(element, attrs) {
var fieldGetter = $parse(attrs.field);
return function (scope, element, attrs) {
var template, field, id;
field = fieldGetter(scope);
template = '..your dom structure here...'
element.replaceWith($compile(template)(scope));
}
}
}
})
I've created a gistwith more complete code and a writeupof the approach.
回答by Marty Pitt
Here's what I ended up using.
这是我最终使用的。
I'm very new to AngularJS, so would love to see better / alternative solutions.
我对 AngularJS 很陌生,所以很想看到更好的/替代解决方案。
angular.module('formComponents', [])
.directive('formInput', function() {
return {
restrict: 'E',
scope: {},
link: function(scope, element, attrs)
{
var type = attrs.type || 'text';
var required = attrs.hasOwnProperty('required') ? "required='required'" : "";
var htmlText = '<div class="control-group">' +
'<label class="control-label" for="' + attrs.formId + '">' + attrs.label + '</label>' +
'<div class="controls">' +
'<input type="' + type + '" class="input-xlarge" id="' + attrs.formId + '" name="' + attrs.formId + '" ' + required + '>' +
'</div>' +
'</div>';
element.html(htmlText);
}
}
})
Example usage:
用法示例:
<form-input label="Application Name" form-id="appName" required/></form-input>
<form-input type="email" label="Email address" form-id="emailAddress" required/></form-input>
<form-input type="password" label="Password" form-id="password" /></form-input>