手动应用ngModel指令

Manually applying the ngModel directive

本文关键字:指令 ngModel 应用      更新时间:2023-09-26

我的指令需要使用ngModel

我需要从另一个指令中动态地做到这一点,因为我想用作用域做一些时髦的东西,并将其从编写HTML的人抽象出来。

我的第一个想法是使用link函数中attrs参数提供的$set函数,该函数用于修改HTML,但指令本身没有编译。然后,我们可以将它与$compile提供程序结合使用,它就可以工作了。

attrs.$set('ngModel', someVar);
$compile(element)(scope);

问题是,如果我不(而且我不能)在指令被无限地重新应用和重新编译时替换元素标签,这会创建无限递归。

但是我可以调整优先级并使其工作:

module.directive('input', [
  '$compile',
  function($compile) {
    return {
      restrict: 'E',
      scope: {},
      priority: 100, // Set this high enough to perform other directives
      terminal: true, // Make sure this is the last directive parsed
      link: function(scope, element, attrs) {
          var key = 'example';
          attrs.$set('ngModel', key);
          $compile(element, null, 100)(scope);
      }
    };
  }
]);

这个工作正常,但是感觉不对:

  • 我现在必须确保元素上的所有其他指令都是正确的

  • 我必须确保没有人使用更高的优先级。

所以这让我思考为什么我不能注入ngModelDirective并强制编译它针对我的元素?

module.directive('input', [
  'ngModelDirective',
  function(ngModel) {
    return {
      restrict: 'E',
      scope: {},
      priority: 100, // Set this high enough to perform other directives
      terminal: true, // Make sure this is the last directive parsed
      require: '?^form',
      link: function(scope, element, attrs, formCtrl) {
          var key = 'example';
          attrs.$set('ngModel', key);
          var ngModelFactory = ngModel[0];
          var ngModelLink = ngModelFactory.compile(element);
          ngModelLink.call(this, scope, element, attrs, [ngModelFactory.controller, formCtrl]);
      }
    };
  }
]);

见:https://github.com/angular/angular.js/blob/v1.2.x/src/ng/directive/input.js L1356

没有抛出错误,但什么也没发生。似乎这还不足以把它挂钩起来,所以我的问题是,有人能详细说明我需要做什么链接ngModelDirective到我的自定义指令没有强制重新编译?

ngModel似乎不适合你想做的事情。但你不需要它。你可以双向绑定一些变量,并将其名称传递到model指令作用域:

app.directive("myDirective", function() {
    // ...
    scope: {
        myModel = "=",
        modelName = "myModel"
        // ...
    }
    // ...
});
app.directive("ngModelDirective", function() {
    // ...
    // ...
    transclude: true,
    link: function(scope, element, attrs) {
        var modelName = scope.modelName;
        console.assert(modelName, '`modelName` must be set when using `ngModelDirective`.');
        // TODO: Check if `scope[modelName]` is actually bound
        doSomethingFancyWith(scope, modelName);
    }
});

模板示例:

<myDirective ngModelDirective my-model="..." />

请注意,doSomethingFancyWith可以读写模型变量,并绑定到外部世界。

我认为不重新编译是不可能的。

ngModel被设计成同一元素中其他指令和父表单指令之间的协作者。例如,在编译期间:

  • 其他指令(例如input、required或ng-change)可以在ngModel中添加自己的$parser$formatter
  • 如果存在,ngModel会把自己添加到父表单指令中。

因此,如果在复杂过程已经结束后以某种方式添加ngModel,则上述两个动作将缺失。

Edit:如果要分配给ng-model属性的值是在编译时已知的,则可能是这样的:

app.directive('myNgModel', function($compile) {
  return {
    restrict: 'A',
    replace: false,
    priority: 1000,
    terminal: true, // these terminal and priority will stop all other directive from being compiled.
    link: function (scope, element, attrs) {
      var key = 'example';
      attrs.$set('ngModel', key);
      attrs.$set('myNgModel', null); // remove itself to avoid a recusion
      $compile(element)(scope); // start compiling other directives
    }
  };
});

下面是柱塞的例子:http://plnkr.co/edit/S2ZkiVIyq2bOK04vAnFO?p=preview

我已经做到了。这不是最漂亮的东西,但它的工作,我可以连接我的input指令工作使用本地inputDirective,这样它就可以使用require或验证特定的输入类型。

要针对实现特定ngModel功能(如ngChange)的另一个标准指令构建此命令,只需将注入的inputDirective替换为正确的指令,例如ngChangeDirective

module.directive('input', function() {
  return {
    restrict: 'E',
    scope: {},
    require: '?ngModel',
    priority: -1,
    link: function(scope, element, attrs, ngModel) {
      var key = 'example.property';
      if (ngModel === undefined) {
        attrs.$set('ngModel', key);
        angular.injector(['ng']).invoke([
          'inputDirective',
          'ngModelDirective',
          '$controller',
          '$exceptionHandler',
          '$parse',
          '$animate',
          function(inputDirective, ngModelDirective, $controller, $exceptionHandler, $parse, $animate) {
            var ngModelFactory = ngModelDirective[0];
            var ngModelLink = ngModelFactory.compile(scope); // Get the ngModel linkage function against this scope
            ngModel = $controller(ngModelFactory.controller, {
              $scope: scope,
              $exceptionHandler: $exceptionHandler,
              $attrs: attrs,
              $element: element,
              $parse: $parse,
              $animate: $animate
            }); // Call the ngModel controller and bootstrap it's arguments
            // Call the inputDirective linkage function to set up the ngModel against this input
            inputDirective[0].link(scope, element, attrs, ngModel);
            element.data('$ngModelController', ngModel); // Allow additional directives to require ngModel on this element.
          }
        ]);
      }
    }
  };
});

注意:这对ngOptions不起作用,因为它指定terminal: true