在ng-repeat中编译指令时,有人可以帮我解决范围问题吗?
https://plnkr.co/edit/y6gfpe01x3ya8zZ5QQpt?p=preview
自定义指令input-by-type
可以使用基于变量类型的相应<div>
替换<input>
- 这在ng-repeat
内使用之前一直正常。
正如您在plnkr示例中看到的那样,该指令按预期工作,直到它在ng-repeat
中使用。
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.data = {};
$scope.inputs = [
{ name: 'Some Text', type: 'text', id: 1 },
{ name: 'EMail', type: 'email', id: 2 },
{ name: 'Age', type: 'number', id: 3 }
];
});
app.directive('inputByType', ['$compile', '$interpolate', function($compile, $interpolate){
return {
restrict: 'A', // [attribute]
require: '^ngModel',
scope: true,
compile: function(element, attrs, transclude){
var inputs = {
text: '<input type="text" name="'+attrs.name+'" ng-model="'+attrs.ngModel+'" ng-disabled="'+attrs.ngDisabled+'" ng-required="'+attrs.ngRequired+'" placeholder="...">',
email: '<input type="email" name="'+attrs.name+'" ng-model="'+attrs.ngModel+'" ng-disabled="'+attrs.ngDisabled+'" ng-required="'+attrs.ngRequired+'" placeholder="...@...">',
number: '<input type="number" name="'+attrs.name+'" ng-model="'+attrs.ngModel+'" ng-disabled="'+attrs.ngDisabled+'" ng-required="'+attrs.ngRequired+'" placeholder="###">',
};
return function(scope){
var type = $interpolate(attrs.inputByType)(scope); // Convert input-by-type="{{ some.type }}" into a useable value
var html = inputs[type] || inputs.text;
var e = $compile(html)(scope);
element.replaceWith(e);
console.log(type, html, element, e);
};
},
};
}]);
如果我手动引用inputs[0]
来编译input-by-type
指令,它就可以正常工作:
<label>
{{ inputs[0].name }}
<div input-by-type="{{ inputs[0].type }}" name="myInputA" ng-model="data.A" ng-required="true"></div>
</label>
但是,当我将它包装在ng-repeat
块中时,编译失败并出现一些意外的输出:
<label ng-repeat="input in inputs">
{{ input.name }}
<div input-by-type="{{ input.type }}" name="myInput{{ $index }}" ng-model="data[input.id]" ng-required="true"></div>
</label>
预期产出:
实际输出:
答案 0 :(得分:2)
postLink功能缺少element
和attrs
参数:
app.directive('inputByType', ['$compile', '$interpolate', function($compile, $interpolate){
return {
restrict: 'A', // [attribute]
require: '^ngModel',
scope: true,
// terminal: true,
compile: function(element, attrs, transclude){
var inputs = {
text: '<input type="text" name="'+attrs.name+'" ng-model="'+attrs.ngModel+'" ng-disabled="'+attrs.ngDisabled+'" ng-required="'+attrs.ngRequired+'" placeholder="...">',
email: '<input type="email" name="'+attrs.name+'" ng-model="'+attrs.ngModel+'" ng-disabled="'+attrs.ngDisabled+'" ng-required="'+attrs.ngRequired+'" placeholder="...@...">',
number: '<input type="number" name="'+attrs.name+'" ng-model="'+attrs.ngModel+'" ng-disabled="'+attrs.ngDisabled+'" ng-required="'+attrs.ngRequired+'" placeholder="###">',
// image upload (redacted)
// file upload (redacted)
// date picker (redacted)
// color picker (redacted)
// boolean (redacted)
};
//return function(scope){
//USE postLink element, attrs
return function postLinkFn(scope, element, attrs) {
var type = $interpolate(attrs.inputByType)(scope); // Convert input-by-type="{{ some.type }}" into a useable value
var html = inputs[type] || inputs.text;
var e = $compile(html)(scope);
element.replaceWith(e);
console.log(type, html, element, e);
};
},
};
}]);
通过省略element
和attrs
参数,postLink函数创建了一个闭包,并使用element
函数的attrs
和compile
参数。即使$ compile服务使用正确的参数调用postLink函数,它们也会被忽略,而是使用编译阶段版本。
这会导致ng-repeat
出现问题,因为它会克隆元素,以便将其附加到新的DOM元素。
答案 1 :(得分:0)
@ georgeawg的答案是正确的,但我遇到了第二个问题,我将在下面提出一个解决方案。
问题:ngModel不会按预期运行($pristine
/ $dirty
等属性将不可用,也不会传播到容器formCtrl
。)
为了解决这个问题,我按照了这个答案的建议:https://stackoverflow.com/a/21687744/1122851并改变了postLink
编译元素的方式,如下所示:
var type = $interpolate(attrs.inputByType)(scope);
var html = inputs[type] || inputs.text;
var template = angular.element(html);
element.replaceWith(template);
$compile(template)(scope);
然后我意识到不再需要require: 'ngModel'
,scope: true
和terminal: true
(无论如何,它们都是我各种测试中的遗物)。最终代码:
app.directive('inputByType', ['$compile', '$interpolate', function($compile, $interpolate){
return {
restrict: 'A', // [attribute]
compile: function(element, attrs, transclude){
var inputs = {
text: '<input type="text" name="'+attrs.name+'" ng-model="'+attrs.ngModel+'" ng-disabled="'+attrs.ngDisabled+'" ng-required="'+attrs.ngRequired+'" placeholder="...">',
email: '<input type="email" name="'+attrs.name+'" ng-model="'+attrs.ngModel+'" ng-disabled="'+attrs.ngDisabled+'" ng-required="'+attrs.ngRequired+'" placeholder="...@...">',
number: '<input type="number" name="'+attrs.name+'" ng-model="'+attrs.ngModel+'" ng-disabled="'+attrs.ngDisabled+'" ng-required="'+attrs.ngRequired+'" placeholder="###">',
// image upload (redacted)
// file upload (redacted)
// date picker (redacted)
// color picker (redacted)
// boolean (redacted)
};
return function postLinkFn(scope, element, attrs) {
var type = $interpolate(attrs.inputByType)(scope); // Convert input-by-type="{{ some.type }}" into a useable value
var html = inputs[type] || inputs.text;
var template = angular.element(html);
element.replaceWith(template);
$compile(template)(scope);
};
},
};
}]);