Here's a Plunker - 我希望“被替换”替换为“替换”。
我的问题如下。我有两个指令myTabs和myPane在myTabs的link
函数中,我在其隔离范围中添加了一个名为setValue
的方法。现在,我希望myPane能够设置值,所以我向myTabs的控制器添加了一个方法setValue
,它基本上调用了scope的方法。但是,由于控制器功能在链接功能之前运行,因此无法访问范围的方法。
有关完整代码,请参阅Plunker,但以下是相关部分:
指令:
angular.module('docsTabsExample', [])
.directive('myTabs', function() {
return {
transclude: true,
scope: {},
controller: function($scope) {
this.setValue = $scope.setValue;
},
link: function(scope, element, attrs){
scope.value = 'To be replaced';
scope.setValue = function(value){
scope.value = value;
};
},
templateUrl: 'my-tabs.html'
};
})
.directive('myPane', function() {
return {
require: '^myTabs',
transclude: true,
scope: {
},
link: function(scope, element, attrs, tabsCtrl) {
tabsCtrl.setValue('Replacement');
}
};
});
HTML:
<body ng-app="docsTabsExample">
<my-tabs>
<my-pane>
One pane
</my-pane>
<my-pane title="World">
Another pane
</my-pane>
</my-tabs>
</body>
当run时,您可以在控制台中看到Error: tabsCtrl.setValue is not a function
。
现在,为了确保我没有朝着错误的方向前进,我应该描述我的实际问题。我正在写一个require
s ngModel,和的指令,它有一个嵌套指令。由于我只能在链接函数中访问ngModel的控制器,所以setValue()方法只能添加到那里的范围,但是我需要控制器将API暴露给嵌套指令,这就是为什么我要尝试{ {1}}。
答案 0 :(得分:1)
你真的只需要让指令控制器管理对$ scope的访问。
当创建控制器时,它将具有与指令相同的范围,因此您可以将控制器代码重新编写为如下所示:
controller: function($scope) {
//Snip...
this.setValue = function(value){
$scope.value = value;
};
}
将逻辑移动到控制器后,您可以安全地从指令中删除范围函数。 但是,您需要解决一个操作顺序问题。
默认情况下,link: function(){}
是一个后置链接功能,它将执行 AFTER 所有子指令的链接。您可以通过将逻辑移动到预链接功能来解决此问题,因为它已经链接了 BEFORE 子指令。
link: {
pre: function(scope, element, attrs){
scope.value = 'To be replaced';
//No need for function here
}
}
现在你的嵌套指令可以简单地在控制器上执行方法,而控制器又会在范围上设置适当的值。没有什么需要改变。
以下是包含这些更改的Plunker:http://plnkr.co/edit/ERf24t93rm1pmr8avyV1?p=preview
好的,这里有一个完整的示例,说明如何在子指令中使用ngModel
以影响实际的绑定模型。您可以看到更改已同时更新<input>
绑定到progressValue
以及实际自定义控件。
如果你想自己玩这个,这里有一个Plunker:http://plnkr.co/edit/zdzm0l4THlCDTvdP4ne3?p=preview
(function() {
var template = '<div>' +
'<div ng-transclude></div>' +
'<div class="progress">' +
'<div class="progress-bar progress-bar-success progress-bar-striped"' +
' role="progressbar" aria-valuenow="0"' +
' aria-valuemin="0" aria-valuemax="100">' +
'<span></span>' +
'</div>' +
'</div>' +
'</div>';
function ParentWithModelDirective() {
return {
restrict: 'E',
require: 'ngModel',
template: template,
transclude: true,
replace: true,
scope: {},
link: {
pre: function(scope, elem, attrs, ngModel) {
var progressBar = elem.find('div').find('div'),
label = progressBar.find('span');
ngModel.$parsers.push(function(viewValue) {
var numValue = 0;
if (angular.isString(viewValue)) {
viewValue = viewValue.replace('%', '');
}
numValue = parseInt(viewValue);
return isNaN(numValue) ? 0 : numValue;
});
ngModel.$formatters.push(function(value) {
return ngModel.$isEmpty(value) ? 'N/A' : value.toString() + '%';
});
ngModel.$render = function() {
progressBar.css('width', ngModel.$viewValue);
progressBar.attr('aria-valuenow',
ngModel.$isEmpty(ngModel.$modelValue) ?
0 :
ngModel.$modelValue);
label.text(ngModel.$viewValue);
};
}
}
};
}
function ChildOfParentDirective() {
return {
restrict: 'E',
require: '^ngModel',
template: "<button type='button' class='btn btn-success'>Complete</button>",
scope: {},
link: function(scope, elem, attrs, ngModel) {
function listener() {
ngModel.$setViewValue('100%', 'click');
ngModel.$render();
}
elem.on('click', listener);
}
};
}
function AnotherChildOfParentDirective() {
return {
restrict: 'E',
require: '^ngModel',
template: "<button type='button' class='btn btn-warning'>Reset</button>",
scope: {},
link: function(scope, elem, attrs, ngModel) {
function listener() {
ngModel.$setViewValue('0%', 'click');
ngModel.$render();
}
elem.on('click', listener);
}
};
}
angular.module('sample-app', [])
.directive('parentWithModel', ParentWithModelDirective)
.directive('childOfParent', ChildOfParentDirective)
.directive('anotherChildOfParent', AnotherChildOfParentDirective)
}());
&#13;
<script src="http://code.angularjs.org/1.3.0/angular.js"></script>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"
rel="stylesheet" />
<div class="container" ng-app='sample-app' ng-init="progressValue = 30">
<div class="masthead">
<h3 class="text-muted">Sample App
</h3>
</div>
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label>Progress Value</label>
<input type="text" class="form-control" ng-model="progressValue" />
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<parent-with-model ng-model="progressValue">
<h1><code>progressValue = {{progressValue}}</code></h1>
<child-of-parent></child-of-parent>
<another-child-of-parent></another-child-of-parent>
<br />
<br />
</parent-with-model>
</div>
</div>
<!-- Site footer -->
<div class="footer">
<p>by <a href="http://www.technofattie.com">Techno Fattie</a>
</p>
</div>
</div>
&#13;
答案 1 :(得分:0)
为什么ngModel
上需要myTabs
?如果您不能直接将setValue
功能移至控制器以解决问题。