自定义指令无法在 ng-repeat 中编译
Custom Directive fails to compile within ng-repeat
谁能帮我解决在 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>
预期输出:
实际输出:
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 函数创建了一个闭包并使用了 compile
函数的 element
和 attrs
参数。即使 $compile 服务使用正确的参数调用 postLink 函数,它们也会被忽略,而是使用编译阶段版本。
这会给 ng-repeat
带来问题,因为它会克隆元素以便将其附加到新的 DOM 元素。
@georgeawg 的回答是正确的,但是我遇到了第二个问题,我将在下面列出解决方案。
问题:ngModel 不会按预期运行($pristine
/ $dirty
等属性将不可用,它们也不会传播到容器 formCtrl
)。
为了解决这个问题,我遵循了这个答案的建议: 并更改了 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);
};
},
};
}]);
谁能帮我解决在 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>
预期输出:
实际输出:
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 函数创建了一个闭包并使用了 compile
函数的 element
和 attrs
参数。即使 $compile 服务使用正确的参数调用 postLink 函数,它们也会被忽略,而是使用编译阶段版本。
这会给 ng-repeat
带来问题,因为它会克隆元素以便将其附加到新的 DOM 元素。
@georgeawg 的回答是正确的,但是我遇到了第二个问题,我将在下面列出解决方案。
问题:ngModel 不会按预期运行($pristine
/ $dirty
等属性将不可用,它们也不会传播到容器 formCtrl
)。
为了解决这个问题,我遵循了这个答案的建议: 并更改了 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);
};
},
};
}]);