我有一个有两个属性的指令:
1.从控制器传递的标题对象
2.从控制器传递的onchange函数。
scope: {
title: '=',
change: '&'
}
//on the link function I update the object, and call the function.
link: function(scope) {
scope.setValue = function() {
scope.title = "Yaniv"
scope.change();
};
},
不知何故,尽管我以相反的方式写了它,但似乎首先它调用了函数,然后才更新标题对象。
克服这个问题的最佳方法是什么?我已经考虑过使用setTimeout,它实际上是在解决这个问题。但是,我想知道为什么会出现这个问题,以及这里是否有更清洁的解决方案。
附上JS小提琴:http://jsfiddle.net/sz82r7pg/
答案 0 :(得分:1)
title
双向绑定在摘要的末尾的父范围中更新。在此之前,title
值更改不会从指令范围传播到父范围。
setValue
由ng-click
调用,并在摘要期间运行。这意味着应在当前摘要后使用change
或setTimeout
调用$imeout
:
scope.setValue = function () {
scope.title = "Yaniv"
$imeout(() => scope.change());
};
这表示难闻的气味和XY问题,因为title
更新已经与摘要相关联,change
是多余的。这是框架的工作。应使用
title
更改
$scope.$watch('title', (newValue, oldValue) => {
if (newValue === oldValue) return;
...
});
在父范围内。
答案 1 :(得分:1)
首先要注意的是该指令有自己的isolate scope对象,其title属性绑定到控制器作用域的标题。这个数据绑定基本上是由手表完成的,您可能知道使用手表进行的更改检测不是即时的,发生在摘要循环中,这总是在更改完成后发生,并且调用堆栈被显示(在下一个微/宏任务中)我不确切知道哪个,但这里无关紧要)。然而,调用函数是瞬时的。所以会发生以下情况:
这是完全预期的行为。在你的情况下最简单的方法是不使用你自己的变化检测,但使用内置于角度的那个:
angular.module('zippyModule', [])
.controller("Ctrl3", function ($scope) {
$scope.title = 'Ori';
$scope.$watch("title", function(newValue, oldValue) {
if (newValue !== oldValue) {
alert(newValue);
}
});
})
.directive('zippy', function() {
return {
restrict: 'AE',
scope: {
title:'=zTitle'
},
template: '<button ng-click="setValue()">What would be the value of title??</button>',
link: function(scope) {
scope.setValue = function () {
scope.title = "Yaniv";
};
}
}
});
button {
font-size:24px;
margin:40px;
padding: 10px 40px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.js"></script>
<div ng-app="zippyModule">
<div ng-controller="Ctrl3">
<div zippy z-title="title"></div>
</div>
</div>
在写这个片段时,我意识到你在小提琴中使用了古老版本的angularjs(1.0.2)。我必须做一些修改才能使用1.5.x。
如果您不想使用手表,那么您可以做的最好的事情就是将新标题传递给更改功能。
angular.module('zippyModule', [])
.controller("Ctrl3", function ($scope) {
$scope.title = 'Ori';
$scope.foo = function(newTitle) {
alert(newTitle);
};
})
.directive('zippy', function() {
return {
restrict: 'AE',
scope: {
change: '&'
},
template: '<button ng-click="setValue()">What would be the value of title??</button>',
link: function(scope) {
scope.setValue = function () {
scope.change({title:"Yaniv"});
};
}
}
});
button {
font-size:24px;
margin:40px;
padding: 10px 40px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.js"></script>
<div ng-app="zippyModule">
<div ng-controller="Ctrl3">
<div zippy change="foo(title)"></div>
</div>
</div>
<强>更新强>
在评论中你希望在控制器和指令之间建立一种更紧密的耦合。这样的事情通常被认为是一种糟糕的设计,但有时候这是唯一正确的解决方案(大多数时候涉及非角度组件)。因此,我们的想法是创建一个可以直接操作的API对象,并将其传递给指令。
angular.module('zippyModule', [])
.controller("Ctrl3", function ($scope) {
$scope.title = 'Ori';
$scope.fooApiImpl = {
title: "Ori",
change: function() {
alert($scope.fooApiImpl.title);
}
};
})
.directive('zippy', function() {
return {
restrict: 'AE',
scope: {
fooApi: '='
},
template: '<button ng-click="setValue()">What would be the value of title??</button>',
link: function(scope) {
scope.setValue = function () {
scope.fooApi.title = "Yaniv";
scope.fooApi.change();
};
}
}
});
button {
font-size:24px;
margin:40px;
padding: 10px 40px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.js"></script>
<div ng-app="zippyModule">
<div ng-controller="Ctrl3">
<div zippy foo-api="fooApiImpl"></div>
</div>
</div>