我正在研究我的第一个'正确的' AngularJS项目在我的控制器中使用transcluded指令时遇到了问题。总的来说,我希望我的transcluded指令能够包装'我看来有些表单元素。这是简化的代码......
(function () {
angular.module('testApp', [])
.directive('xyzFieldSet', function () {
return {
template: '<fieldset ng-transclude></fieldset>',
restrict: 'E',
transclude: true
};
})
.controller('testCtrl', ['$scope', function($scope) {
$scope.name = 'Fred';
$scope.changedName = '';
$scope.nameChanged = function() {
$scope.changedName = $scope.name;
};
}]);
}());
和相应的HTML ...
<div ng-app="testApp">
<div ng-controller="testCtrl">
<h2>Without 'fieldset' directive</h2>
<p>The 'Changed Name' field changes as the 'Name' is changed.</p>
<p>Name: <input ng-model="name" ng-change="nameChanged()" /></p>
<p>Changed Name: {{ changedName }}</p>
</div>
<hr />
<div ng-controller="testCtrl">
<h2>With 'fieldset' directive</h2>
<p>
With the transcluded directive 'wrapping' the content,
the 'Changed Name' field <em>does not</em> change as
the 'Name' is changed.
</p>
<xyz-field-set>
<p>Name: <input ng-model="name" ng-change="nameChanged()" /></p>
<p>Changed Name: {{ changedName }}</p>
</xyz-field-set>
</div>
</div>
如果没有transcluded指令,对输入字段的任何更改都会正确绑定到作用域,但是,当我添加transcluded指令时,数据绑定不起作用。
可以在https://jsfiddle.net/tgspwo73/1/
找到证明问题的小提琴根据我的阅读,我猜测该指令正在改变其子元素的范围。如果是这种情况,有没有办法绕过这种行为?
答案 0 :(得分:1)
这与范围原型继承以及当您不使用带点(.
)的模型时产生的相当不直观的行为有关。
这里有一个很好的详尽解释:
What are the nuances of scope prototypal / prototypical inheritance in AngularJS?
和我的次要贡献here。
上述链接中的问题/答案讨论了最常出现行为的子范围。例如,ng-if
会创建一个子范围,因此如果您这样做,您当前正在处理的方法会中断:
<p>Name: <input ng-if="true"
ng-model="name"
ng-change="nameChanged()" placeholder="Type name here" />
</p>
但类似的事情发生在一个被转换的范围(使用ng-transclude
),因为这个范围原型继承自父级(尽管它是指令的子范围,但这不是重点)。
解决此问题的方法是遵循始终绑定到对象属性的最佳做法(即使用.
中的ng-model
):
<xyz-field-set>
<p>Name: <input ng-model="form.name"
ng-change="nameChanged()" placeholder="Type name here" />
</p>
<p>Changed Name: {{ changedName }}</p>
</xyz-field-set>
这需要在控制器中进行以下更改:
.controller('testCtrl', ['$scope', function($scope) {
$scope.form = {
name: 'Fred' // optionally, set the property
};
$scope.changedName = '';
$scope.nameChanged = function() {
// this, btw, is unnecessary since you already have $scope.form.name
// and you can bind to it with {{form.name}}
// (unless you need to add more logic)
$scope.changedName = $scope.form.name;
};
}]);