我正在实现自定义输入窗口小部件。真正的代码更复杂,但通常看起来像这样:
app.directive('inputWidget', function () {
return {
replace:true,
restrict: 'E',
templateUrl:"inputWidget.html",
compile: function (tElement, tAttributes){
//flow the bindings from the parent.
//I can do it dynamically, this is just a demo for the idea
tElement.find("input").attr("placeholder", tAttributes.placeholder);
tElement.find("input").attr("ng-model", tElement.attr("ng-model"));
}
};
});
inputWidget.html:
<div>
<input />
<span>
</span>
</div>
使用它:
<input-widget placeholder="{{name}}" ng-model="someProperty"></input-widget>
使用上面的代码正确显示占位符,因为它使用了父级的相同范围:http://plnkr.co/edit/uhUEGBUCB8BcwxqvKRI9?p=preview
我想知道是否应该使用隔离范围,如下所示:
app.directive('inputWidget', function () {
return {
replace:true,
restrict: 'E',
templateUrl:"inputWidget.html",
scope : {
placeholder: "@"
//more properties for ng-model,...
}
};
});
有了这个,该指令不与父级共享相同的范围,这可能是一个好的设计。但问题是这个隔离范围定义会很快变得混乱,因为我们在其上添加与DOM相关的属性(占位符,类型,必需,......)以及每次我们需要申请时一个新指令(输入窗口小部件上的自定义验证),我们需要在隔离范围上定义一个属性,以充当中间人。
我想知道始终在指令组件上定义隔离范围是否是个好主意。
在这种情况下,我有3个选项:
请建议,谢谢。
答案 0 :(得分:2)
如果输入窗口小部件配置很复杂,我会使用options属性,并使用一个独立的范围来使属性显式和强制:
<input-widget options="{ placeholder: name, max-length: 5, etc }"
ng-model="name"></input-widget>
如果您有选项模型和ngModel,则无需流动任何DOM属性:
app.directive('inputWidget', function () {
return {
replace:true,
restrict: 'E',
templateUrl:"inputWidget.html",
scope: { options:'=', ngModel: '='}
};
});
在您的模板中,您可以像往常一样将属性绑定到$ scope视图模型:
<div>
<input placeholder="{{options.placeholder}}" ng-model="ngModel"/>
<span>
{{options}}
</span>
</div>
就个人而言,在开发重用时,我更喜欢使用属性作为配置指令的方法,并使用隔离范围使其更具模块性和可读性。它的行为更像是一个组件,通常不需要外部环境。
但是,有时候我发现有子/继承范围的指令很有用。在这种情况下,我通常会要求&#39;用于提供上下文的父指令。这对指令一起工作,因此较少的属性必须流向子指令。
答案 1 :(得分:1)
这不是一个非常微不足道的问题。这是因为人们可能对模板化元素有任意指令,这些指令可能是<input>
,并且正确的解决方案应该确保:1)这些指令只编译和链接一次,2)编译实际{{1 } - 不是<input>
。
出于这个原因,我建议使用实际的<input-widget>
元素,并添加<input>
指令作为属性 - 该指令将应用模板,而实际的inputWidget
元素将承载其他指令(如<input>
,ng-model
,自定义验证器等)可以对其进行操作。
ng-required
和<input input-widget
ng-model="someProp" placeholder="{{placeholder}}"
ng-required="isRequired"
p1="{{name}}" p2="name">
将使用两个编译过程(在inputWidget
之后建模):
ngInclude
模板(app.directive("inputWidget", function($templateRequest) {
return {
priority: 400,
terminal: true,
transclude: "element",
controller: angular.noop,
link: function(scope, element, attrs, ctrl, transclude) {
$templateRequest("inputWidget.template.html").then(function(templateHtml) {
ctrl.template = templateHtml;
transclude(scope, function(clone) {
element.after(clone);
});
});
}
};
});
app.directive("inputWidget", function($compile) {
return {
priority: -400,
require: "inputWidget",
scope: {
p1: "@", // variables used by the directive itself
p2: "=?" // for example, to augment the template
},
link: function(scope, element, attrs, ctrl, transclude) {
var templateEl = angular.element(ctrl.template);
element.after(templateEl);
$compile(templateEl)(scope);
templateEl.find("placeholder").replaceWith(element);
}
};
});
)有一个inputWidget.template.html
元素,用于标记原始<placeholder>
元素的放置位置:
<input>
(编辑)为什么2次编辑通过:
上面的解决方案是一个“解决方法”,它避免了Angular中的错误,该错误是在注释元素上设置了插值,这是使用<div>
<pre>p1: {{p1}}</pre>
<div>
<placeholder></placeholder>
</div>
<pre>p2: {{p2}}</pre>
</div>
时剩下的内容。这在v1.4.0-beta.6中得到修复,通过修复,解决方案可以简化为:
transclude: element