我使用“scope”创建了一个带有绑定的指令。在某些情况下,我想绑定一个常量对象。例如,使用HTML:
<div ng-controller="Ctrl">
<greeting person="{firstName: 'Bob', lastName: 'Jones'}"></greeting>
</div>
和JavaScript:
var app = angular.module('myApp', []);
app.controller("Ctrl", function($scope) {
});
app.directive("greeting", function () {
return {
restrict: "E",
replace: true,
scope: {
person: "="
},
template:
'<p>Hello {{person.firstName}} {{person.lastName}}</p>'
};
});
虽然这有效,但它也会导致JavaScript错误:
Error: 10 $digest() iterations reached. Aborting!
(Fiddle demonstrating the problem)
在不导致错误的情况下绑定常量对象的正确方法是什么?
答案 0 :(得分:11)
这是我提出的解决方案,基于@ sh0ber的答案:
实施自定义link
功能。如果属性是有效的JSON,那么它是一个常量值,所以我们只评估它一次。否则,请正常观察并更新该值(换句话说,尝试表现为=
绑定)。 scope
需要设置为true
,以确保指定的值仅影响指令的此实例。
HTML:
<div ng-controller="Ctrl">
<greeting person='{"firstName": "Bob", "lastName": "Jones"}'></greeting>
<greeting person="jim"></greeting>
</div>
JavaScript的:
var app = angular.module('myApp', []);
app.controller("Ctrl", function($scope) {
$scope.jim = {firstName: 'Jim', lastName: "Bloggs"};
});
app.directive("greeting", function () {
return {
restrict: "E",
replace: true,
scope: true,
link: function(scope, elements, attrs) {
try {
scope.person = JSON.parse(attrs.person);
} catch (e) {
scope.$watch(function() {
return scope.$parent.$eval(attrs.person);
}, function(newValue, oldValue) {
scope.person = newValue;
});
}
},
template: '<p>Hello {{person.firstName}} {{person.lastName}}</p>'
};
});
答案 1 :(得分:7)
您收到该错误是因为Angular每次都在评估表达式。 '='用于变量名称。
以下两种方法可以在没有错误的情况下实现相同的思考。
第一个解决方案:
app.controller("Ctrl", function($scope) {
$scope.person = {firstName: 'Bob', lastName: 'Jones'};
});
app.directive("greeting", function () {
return {
restrict: "E",
replace: true,
scope: {
person: "="
},
template:
'<p>Hello {{person.firstName}} {{person.lastName}}</p>'
};
});
<greeting person="person"></greeting>
第二个解决方案:
app.directive("greeting2", function () {
return {
restrict: "E",
replace: true,
scope: {
firstName: "@",
lastName: "@"
},
template:
'<p>Hello {{firstName}} {{lastName}}</p>'
};
});
<greeting2 first-name="Bob" last-Name="Jones"></greeting2>
答案 2 :(得分:4)
另一种选择:
app.directive("greeting", function () {
return {
restrict: "E",
link: function(scope,element,attrs){
scope.person = scope.$eval(attrs.person);
},
template: '<p>Hello {{person.firstName}} {{person.lastName}}</p>'
};
});
答案 3 :(得分:2)
这是因为如果使用=
类型的范围字段链接,则会观察属性值的变化,但会测试参考相等性(使用!==
),而不是深度测试是否相等。在访问属性以获取其值时,在线指定对象文字将导致angular创建新对象 - 因此当angular进行脏检查时,将旧值与当前值进行比较始终表示更改。
克服这种情况的一种方法是修改角度来源,如下所述:
https://github.com/mgonto/angular.js/commit/09d19353a2ba0de8edcf625aa7a21464be830f02
否则,您可以在控制器中创建对象,并在元素的属性中按名称引用它:
<强> HTML 强>
<div ng-controller="Ctrl">
<greeting person="personObj"></greeting>
</div>
<强> JS 强>
app.controller("Ctrl", function($scope)
{
$scope.personObj = { firstName : 'Bob', lastName : 'Jones' };
});
另一种方法是在父元素的ng-init
指令中创建对象,然后按名称引用它(但是这个不太可读):
<div ng-controller="Ctrl" ng-init="personObj = { firstName : 'Bob', lastName : 'Jones' }">
<greeting person="personObj"></greeting>
</div>
答案 4 :(得分:0)
我不是特别喜欢使用eval()
,但是如果你真的想让它与你提供的HTML一起使用:
app.directive("greeting", function() {
return {
restrict: "E",
compile: function(element, attrs) {
eval("var person = " + attrs.person);
var htmlText = '<p>Hello ' + person.firstName + ' ' + person.lastName + '</p>';
element.replaceWith(htmlText);
}
};
});
答案 5 :(得分:0)
我遇到了同样的问题,我通过在编译步骤中解析json来解决它:
angular.module('foo', []).
directive('myDirective', function () {
return {
scope: {
myData: '@'
},
controller: function ($scope, $timeout) {
$timeout(function () {
console.log($scope.myData);
});
},
template: "{{myData | json}} a is {{myData.a}} b is {{myData.b}}",
compile: function (element, attrs) {
attrs['myData'] = angular.fromJson(attrs['myData']);
}
};
});
一个缺点是,当控制器首次运行时,$scope
最初不会填充。
这是带有此代码的JSFiddle。