【问题标题】:Use ngModel with plain ngController instead of directive?使用带有普通ngController而不是指令的ngModel?
【发布时间】:2014-08-17 04:29:47
【问题描述】:

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

在这些情况下,我经常希望从控制器发布一些数据以在包含的部分中使用。现在,我知道我可以简单地绑定控制器范围内的项目,但是我想明确指定“模型”位置,以使代码更易于维护和阅读。我想使用的是 ng-model,因为它将用于自定义指令,但就在我的普通控制器旁边:

<div ng-controller="AppController" ng-model='fooModel'>
  {{fooModel}}
</div>

但是,如果不使用指令和 'require' 注入,我无法获得对生成的 ngModelController 的引用。

我知道我可以通过将 $attr 注入我的控制器并执行以下操作来相当轻松地创建自己的属性:

<div ng-controller="AppController" my-model='fooModel'>
  {{fooModel}}
</div>

在这种情况下,我只需手动获取或解析 myModel 值并将我的模型粘贴到该名称下的 $scope 中。然而,在这种情况下感觉不对 - 我真的只需要一个控制器的“模型”,并且当 ngModel 存在时,我不想将此样板添加到每个控制器。 (这就是事情的原理!)

我的问题是:

1) 有什么方法可以使用 ngModel 和 plain 控制器来达到上述效果吗?

2) 我一直试图找出 ngModelControllers 的存储位置,以便我可以查看调试器中的情况,但无法找到它们。使用 ngModel 指令时,我应该在作用域或父作用域中看到这些吗? (他们住在哪里?!?)

更新:正如下面的答案中所建议的,$element.controller() 可用于获取控制器。这行得通 (http://plnkr.co/edit/bZzdLpacmAyKy239tNAO?p=preview) 但是它有点不令人满意,因为它需要使用 $evalAsync。

【问题讨论】:

    标签: angularjs angularjs-directive angularjs-scope


    【解决方案1】:

    2) 我一直试图找出 ngModelControllers 的存储位置,以便我可以查看调试器中的情况,但无法找到它们。使用 ngModel 指令时,我应该在作用域或父作用域中看到这些吗? (他们住在哪里?!?)

    答案取决于您想从哪里访问控制器。

    从元素外部用ng-model

    它需要具有ng-model 属性的元素和父表单(或ngForm)的“名称”属性。因此,假设您有名称为myForm 的表单和名称为myInput 的属性ng-model 的元素,那么您可以从父作用域以myForm.myInput 访问ngModelControllermyFoo。例如,出于调试目的:

    <p>myFoo: {{myForm.myInput.$modelValue}}<p>
    <form name="myForm">
      <div ng-controller="InnerController" name="myInput" ng-model="model.foo"></div>
    </form>
    

    可以在http://plnkr.co/edit/IVTtvIXlBWXGytOEHYbn?p=preview看到

    从带有ng-model的元素内部

    类似于answer from @pixelbits,由于控制器创建的顺序需要使用$evalAsync,但您也可以使用angular.element.controller函数来检索它:

    app.controller('InnerController', function($scope, $element) {
      $scope.$evalAsync(function() {
        $scope.myModelController = $element.controller('ngModel');
      });
    });
    

    用于,在控制器内部查看它,用于调试目的,如:

    <div ng-controller="InnerController" ng-model="model.foo">
      <p>myFoo: {{myModelController.$modelValue}}<p>
    </div>
    

    可以在http://plnkr.co/edit/C7ykMHmd8Be1N1Gl1Auc?p=preview 看到。


    1) 有没有办法使用 ngModel 和普通控制器来获得上述效果?

    在指令中添加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 上的观察者。这是故意的,以便仅在用户与元素交互时调用$setViewValue,而不是响应父范围对模型的更改。

    这可以在http://plnkr.co/edit/uknixs6RhXtrqK4ZWLuC?p=preview看到


    编辑:如果你想避免$evalAsync,你可以使用观察者来代替。

    $scope.$watch(function() {
      return $element.controller('ngModel');
    }, function(ngModelController) {
      $scope.myModelController = ngModelController;
    });
    

    http://plnkr.co/edit/gJonpzLoVsgc8zB6tsZ1?p=preview所见


    作为旁注,到目前为止,我似乎避免像这样嵌套普通控制器。我认为如果模板角色的某个部分是通过ngModel 控制变量,那么它是编写一个小指令的主要候选者,通常具有一个孤立的范围,以确保没有由于范围继承而产生的意外影响,它有一个清晰的 API,并使用require 访问ngModelController。是的,它可能不会被重用,但它确实有助于实现代码各部分之间的职责分离。

    【讨论】:

    • 谢谢!这个 plunker 使用了你的技术,但符合我原来的问题:plnkr.co/edit/bZzdLpacmAyKy239tNAO?p=preview 神秘的 evalAsync 有什么替代方法吗?我可以为控制器使用 $watch 吗?
    • @PatNiemeyer 你可以。我添加了一个部分来说明如何。
    【解决方案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);
         });
    });
    

    Demo JSFiddle

    【讨论】:

    • 谢谢 - 这就是答案。但是,除了神秘的 $evalAsync 之外,还有其他选择吗?也许可以 $watched 的东西?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多