我希望在指令的属性之间进行单向(非一次)绑定,但我在如何在没有attrs.$observe
的情况下表达这种情况的情况下挣扎。我现在能想到的最好的方法是通过&attr
进行绑定,并在我的模板中调用我绑定的变量,例如{{attr()}}
app.controller('MainCtrl', function($scope) {
$scope.names = ['Original'];
setTimeout(function () {
$scope.names.push('Asynchronously updated name');
$scope.$apply();
}, 1000);
});
app.directive('helloComponent', function () {
return {
scope: {
'names': '&names'
},
template: '<li ng-repeat="name in names()">Hello {{name}}</li>'
}
});
<body ng-controller="MainCtrl">
<ul>
<hello-component names="names"/>
</ul>
</body>
有没有更好的方法来保留单向绑定而不需要调用绑定属性?
我已经更新了示例代码,以澄清我想绑定到一个对象,而不仅仅是一个字符串。所以@attr
(使用字符串属性)是不是的解决方案。
答案 0 :(得分:5)
"&"
实际上是正确的做法。我反对这种方法(使用@JoeEnzminger,here和here),因为它在语义上是有问题的。但整体Joe是对的 - 这是创建与实际对象的单向绑定的方式,而"@"
绑定到字符串。
如果您不喜欢隔离范围,那么使用$parse
可以获得相同的效果:
var parsedName = $parse(attrs.name);
$scope.nameFn = function(){
return parsedName($scope);
}
并在模板中使用它:
"<p>Hello {{nameFn()}}</p>"
答案 1 :(得分:3)
我在其他答案中没有看到任何提及它,但从Angular 1.5开始,支持对象的单向绑定(see scope
section in $compile docs for Angular 1.5.9):
<
或<attr
- 在本地范围属性和通过属性attr
传递的表达式之间建立单向(单向)绑定。表达式在父作用域的上下文中计算。如果未指定attr
名称,则假定属性名称与本地名称相同。您还可以通过添加?
:<?
或<?attr
来使绑定成为可选项。例如,给定
<my-component my-attr="parentModel">
和scope: { localModel:'<myAttr' }
的指令定义,则隔离范围属性localModel
将反映父范围上parentModel
的值。对parentModel
的任何更改都会反映在localModel
中,但localModel
中的更改不会反映在parentModel
中。然而,有两个警告:
- 单向绑定不会将值从父级复制到隔离范围,它只是设置相同的值。这意味着如果绑定值是对象,则隔离范围中对其属性的更改将反映在父范围中(因为它们都引用同一对象)。
- 单向绑定监视更改为父值的标识。这意味着父值的
醇>$watch
仅在对值的引用发生更改时才会触发。在大多数情况下,这不应该引起关注,但是知道您是否单向绑定到对象,然后在隔离范围中替换该对象可能很重要。如果现在更改父作用域中对象的属性,则更改将不会传播到隔离作用域,因为父作用域上对象的标识未更改。相反,您必须分配一个新对象。如果您不打算将对隔离范围绑定的更改传播回父级,则单向绑定很有用。但是,它并不能完全不可能。
在下面的示例中,单向绑定用于将控制器范围内的对象中的更改传播到指令。
正如@Suamere所指出的,你确实可以用单向绑定来改变绑定对象的属性;但是,如果整个对象从本地模型更改,则与父模型的绑定将中断,因为父和本地范围将引用不同的对象。双向绑定可以解决这个问题。更新了代码段以突出显示差异。
angular.module('App', [])
.directive('counter', function() {
return {
templateUrl: 'counter.html',
restrict: 'E',
scope: {
obj1: '<objOneWayBinding',
obj2: '=objTwoWayBinding'
},
link: function(scope) {
scope.increment1 = function() {
scope.obj1.counter++;
};
scope.increment2 = function() {
scope.obj2.counter++;
};
scope.reset1 = function() {
scope.obj1 = {
counter: 0,
id: Math.floor(Math.random()*10000),
descr: "One-way binding",
creator: "Directive"
};
};
scope.reset2 = function() {
scope.obj2 = {
counter: 0,
id: Math.floor(Math.random()*10000),
descr: "Two-way binding",
creator: "Directive"
};
};
}
};
})
.controller('MyCtrl', ['$scope', function($scope) {
$scope.increment = function() {
$scope.obj1FromController.counter++;
$scope.obj2FromController.counter++;
};
$scope.reset = function() {
$scope.obj1FromController = {
counter: 0,
id: Math.floor(Math.random()*10000),
descr: "One-way binding",
creator: "Parent"
};
$scope.obj2FromController = {
counter: 0,
id: Math.floor(Math.random()*10000),
descr: "Two-way binding",
creator: "Parent"
};
};
$scope.reset();
}])
;
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.js"></script>
<div ng-app="App">
<script type="text/ng-template" id="counter.html">
<h3>In Directive</h3>
<pre>{{obj1 | json:0}}</pre>
<button ng-click="increment1()">
Increment obj1 from directive
</button>
<button ng-click="reset1()">
Replace obj1 from directive (breaks binding)
</button>
<pre>{{obj2 | json:0}}</pre>
<button ng-click="increment2()">
Increment obj2 from directive
</button>
<button ng-click="reset2()">
Replace obj2 from directive (maintains binding)
</button>
</script>
<div ng-controller="MyCtrl">
<counter obj-one-way-binding="obj1FromController"
obj-two-way-binding="obj2FromController">
</counter>
<h3>In Parent</h3>
<pre>{{obj1FromController | json:0}}</pre>
<pre>{{obj2FromController | json:0}}</pre>
<button ng-click="increment()">
Increment from parent
</button>
<button ng-click="reset()">
Replace from parent (maintains binding)
</button>
</div>
</div>
&#13;
答案 2 :(得分:0)
执行属性从字面上传递字符串。所以不要这样做:
<hello-component name="name"/>
你可以这样做:
<hello-component name="{{name}}"/>
答案 3 :(得分:-2)
这可能与New Dev提出的方法基本相同,但是我通过从我的隔离范围中取出一个对象并为其调用scope.$parent.$eval(attrs.myObj)
来获取一个getter函数,为自己解决了类似的问题。
在一个看起来更像你的简化版本中我改变了:
app.directive('myDir', [function() {
return {
scope : {
id : '@',
otherScopeVar : '=',
names : '='
},
template : '<li ng-repeat="name in names">{{name}}</li>'
}
}]);
到
app.directive('myDir', [function() {
return {
scope : {
id : '@',
otherScopeVar : '='
},
template : '<li ng-repeat="name in getNames()">{{name}}</li>',
link : function(scope, elem, attrs) {
scope.getNames() {
return scope.$parent.$eval(attrs.myList);
};
}
}
}]);
这样,无论何时运行摘要,您的对象都会从父作用域中拉出。对我来说,这样做的好处是我能够将指令从双向绑定更改为单向绑定(这使我的性能从无法使用到工作正常)而不更改使用该指令的视图。
修改强> 第二个想法我不确定这是否只是单向绑定,因为更新变量并运行摘要时将始终使用更新的对象,在更改时没有固有的方法来运行其他逻辑,就像使用{ {1}}。