在我的应用程序中,我想保留对某些代码段使用普通控制器的选项 - 而不是为永远不会重复使用的一次性事物创建指令。
在这些情况下,我经常想要从控制器发布一些数据,以便在包含的部分中使用。现在,我知道我可以简单地绑定控制器范围内的项目,但是我想指定"模型"显式位置只是为了使代码更易于维护和更易于阅读。我想要使用的是ng-model,因为它将用于自定义指令,但只是在我的普通控制器旁边:
<div ng-controller="AppController" ng-model='fooModel'>
{{fooModel}}
</div>
但是我没有办法在没有使用指令的情况下获得对生成的ngModelController的引用,并且&#39;要求&#39;注射。
我知道通过将$ attr注入我的控制器并执行以下操作,我可以非常轻松地创建自己的属性:
<div ng-controller="AppController" my-model='fooModel'>
{{fooModel}}
</div>
在这种情况下,我只需手动获取或解析myModel值,并将我的模型粘贴到该名称下的$ scope中。然而,在这种情况下感觉不对 - 我真的只需要一个&#34;模型&#34;对于控制器而言,当ngModel存在时,我不希望不必将此样板添加到每个控制器。 (这是事情的原则!)
我的问题是:
1)有没有办法使用ngModel和 plain 控制器来获得上述效果?
2)我一直在试图找出存储ngModelControllers的位置,这样我就可以查看调试器中的情况但却找不到它们。在使用ngModel指令时,我应该在范围或父范围内看到这些吗? (他们住在哪里?!?)
更新:如下面的答案所示,$ element.controller()可用于获取控制器。这有效(http://plnkr.co/edit/bZzdLpacmAyKy239tNAO?p=preview)但是它有点不满意,因为它需要使用$ evalAsync。
答案 0 :(得分:4)
2)我一直在试图找出存储ngModelControllers的位置,这样我就可以查看调试器中的情况但却找不到它们。在使用ngModel指令时,我应该在范围或父范围内看到这些吗? (他们住在哪里?!?)
答案略微取决于您要从哪里访问控制器。
来自ng-model
它需要&#34; name&#34;具有ng-model
属性的元素和父表单(或ngForm
)的属性。因此,假设您有名称为myForm
的表单和名称为ng-model
的{{1}}属性元素,那么您可以从父级访问myInput
ngModelController
范围为myFoo
。例如,出于调试目的:
myForm.myInput
可以在http://plnkr.co/edit/IVTtvIXlBWXGytOEHYbn?p=preview
看到 来自<p>myFoo: {{myForm.myInput.$modelValue}}<p>
<form name="myForm">
<div ng-controller="InnerController" name="myInput" ng-model="model.foo"></div>
</form>
与answer from @pixelbits类似,由于控制器创建的顺序,需要使用ng-model
,但您也可以使用angular.element.controller函数来检索它:
$evalAsync
在控制器内部使用,以便进行调试,如下所示:
app.controller('InnerController', function($scope, $element) {
$scope.$evalAsync(function() {
$scope.myModelController = $element.controller('ngModel');
});
});
可以在http://plnkr.co/edit/C7ykMHmd8Be1N1Gl1Auc?p=preview看到。
1)有没有办法使用ngModel和普通控制器来获得上述效果?
在指令中有<div ng-controller="InnerController" ng-model="model.foo">
<p>myFoo: {{myModelController.$modelValue}}<p>
</div>
后,您可以使用ngModelController
函数更改其值,就像使用自定义指令访问ngModelController
一样:
$setViewValue
例如,您可以执行此操作,以响应触发myModelController.$setViewValue('my-new-model-value');
处理程序的用户操作。
ngChange
请注意app.controller('InnerController', function($scope, $element) {
$scope.$evalAsync(function() {
$scope.myModelController = $element.controller('ngModel');
});
$scope.$watch('myModelController.$modelValue', function(externalModel) {
$scope.localModel = externalModel;
});
$scope.changed = function() {
$scope.myModelController.$setViewValue($scope.localModel);
};
});
上的额外观察者获取模型的初始值,以及对以后的任何更改作出反应。
它可以与以下模板一起使用:
$modelValue
请注意,这会使用{{model.foo}}
<div ng-controller="InnerController" ng-model="model.foo">
<p><input type="text" ng-model="localModel" ng-change="changed()"></p>
</div>
而不是ngChange
上的观察者。这是故意的,因此只有当用户与元素交互时才会调用localModel
,而不是响应来自父作用域的模型更改。
可以在http://plnkr.co/edit/uknixs6RhXtrqK4ZWLuC?p=preview
看到编辑:如果您想避免使用$setViewValue
,则可以使用观察者。
$evalAsync
见http://plnkr.co/edit/gJonpzLoVsgc8zB6tsZ1?p=preview
作为旁注,到目前为止,我似乎已经避免像这样嵌套普通控制器。我认为如果模板的某个部分角色是通过$scope.$watch(function() {
return $element.controller('ngModel');
}, function(ngModelController) {
$scope.myModelController = ngModelController;
});
来控制变量,那么它是编写小指令的主要候选者,通常具有独立的范围以确保没有意外的影响到期范围继承,具有明确的API,并使用ngModel
访问require
。是的,它可能不会被重用,但它确实有助于在代码的各个部分之间强制分离责任。
答案 1 :(得分:2)
在元素上声明指令时:
<div ng-controller="AppController" ng-model='fooModel'>
{{fooModel}}
</div>
您可以通过调用jQlite / jQuery $element.data(nameOfController)来检索任何指令的控制器实例,其中nameOfController是带有$
前缀和Controller
后缀的指令的规范化名称。
例如,要检索ngModel
指令的控制器实例,您可以执行以下操作:
var ngModelController = $element.data('$ngModelController');
只要ngModel
指令已经注册,就可以正常工作。
不幸的是,ngController
执行的优先级与ngModel
相同,并且由于特定于实现的原因,ngModel
在ngController
函数执行时未注册。因此,以下内容不起作用:
app.controller('ctrl', function ($scope, $element) {
var ngModelController = $element.data('$ngModelController');
// this alerts undefined because ngModel has not been registered yet
alert(ngModelController);
});
要解决此问题,您可以将代码包装在$scope.$evalAsync中,这样可以保证在执行回调函数之前已经注册了指令:
app.controller('ctrl', function ($scope, $element) {
$scope.$evalAsync(function() {
var ngModelController = $element.data('$ngModelController');
alert(ngModelController);
});
});