使用ngModel和普通的ngController而不是指令?

时间:2014-08-17 04:29:48

标签: angularjs angularjs-directive angularjs-scope

在我的应用程序中,我想保留对某些代码段使用普通控制器的选项 - 而不是为永远不会重复使用的一次性事物创建指令。

在这些情况下,我经常想要从控制器发布一些数据,以便在包含的部分中使用。现在,我知道我可以简单地绑定控制器范围内的项目,但是我想指定"模型"显式位置只是为了使代码更易于维护和更易于阅读。我想要使​​用的是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。

2 个答案:

答案 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相同,并且由于特定于实现的原因,ngModelngController函数执行时未注册。因此,以下内容不起作用:

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);
     });
});

Demo JSFiddle