0

我正在尝试围绕输入元素构建一个指令,该指令在模型被弄脏或触摸时做出响应。所需的 ngModel 似乎反映了输入模型的值和视图的变化,但没有其他属性。

我怀疑这与我在两个元素中包含 ng-model 的事实有关,但我还没有弄清楚如何只使用它一次。

理想情况下,我想要这样创建的东西:

<input test-directive label="'My Label'" type="text" ng-model="testObject.text"/>

结果如下:

<label>
    <div>My Label</div>
    <input ng-model="testObject.text" ng-blur="input.focus=false" ng-focus="input.focus=true"/>
    Focused: true (input.focus)
    Pristine: false (ngModel.$pristine)
</label>

这是我到目前为止所拥有的:小提琴

<div test-directive ng-model="testObject.text" l="'Test Input'" f="testObject.focus">
    <input type="text" ng-model="testObject.text" ng-blur="testObject.focus=false" ng-focus="testObject.focus=true" />
</div>

该指令监视 ngModel。

app.directive('testDirective', ['$compile',
    function ($compile) {
    'use strict';
    return {
        restrict: 'A',
    require: "ngModel",
    scope: {
        l: '=',
        f: '='
    },
    link: function (scope, element, attr, ngModel) {
        var input = element.find('input');
        scope.$watch(function () {
            return ngModel;
        }, function (modelView) {
            scope.modelView = modelView
        });
    },
    template:
        '<div>' +

        '<label>' +
        '{{l}}' +
        '<div class="iwc-input" ng-transclude></div>' +
        '</label>' +
        'focus: {{f}}' +
        '<pre>{{modelView|json}}</pre>' +
        '</div>',
    transclude: true,
    replace: false
    };

}]);
4

1 回答 1

2

我发现在 Angular 中,有一个指令“self-wrap”是相当复杂的,同时其他指令仍然可以正常工作。因此,下面的答案有效,我将尝试解释为什么它比应有的复杂。

有很多方法可以解决这个问题。我将使用该方法transclude: "element"- 这将包含整个元素并允许您将其放置在任何地方(包括包装)。

.directive("wrapper", function($compile){
  return {
    scope: { l: "@" },
    transclude: "element",
    require: ["ngModel"],
    link: function(scope, element, attrs, ctrls, transclude)
      scope.ngModel = ctrls[0];

      // transclude: "element" ignores the template property, so compile it manually
      var template = '<label ng-class="{dirty: ngModel.$dirty}">{{l}}: \
                        <placeholder></placeholder>\
                      </label>';

      $compile(template)(scope, function(prelinkedTemplate){        
         transclude(function (clonedElement){
            prelinkedTemplate.find("placeholder").replaceWith(clonedElement);

            // element here is only a comment after transclusion
            // so, need to use .after() - not .append()
            element.after(prelinkedTemplate);
         });
      })
  }
})

因此,上面编译模板和链接对隔离范围(哪里$scope.l$scope.ngModel可用),然后 trascludes 元素并替换<placeholder>.

这应该足够了,但是有一个问题。当 Angular 编译我们的指令时,该元素已被嵌入并且现在是一个注释<!-- wrapper -->,而不是<input>- 这是ngModel指令在其预链接函数中“看到”的内容,所以事情开始中断。

为了解决这个问题,我们的指令需要具有比ngModel(即 1)更高的优先级,事实上,对于工作之类的事情,优先级高于ngAttributeDirective(即 100)ng-maxlength。但是,如果我们这样做了,那么我们就不能只是 require: "ngModel",因为它在我们的优先级上还不能使用。

解决此问题的一种方法是进行 2 次通过 - 一次具有较高优先级,另一次具有较低优先级。较低优先级的传递会将捕获ngModel的控制器“挂起”到指令的控制器上。就是这样:

// first pass
app.directive("wrapper", function($compile) {
  return {
    priority: 101,
    scope: {
      l: "@"
    },
    transclude: "element",
    controller: angular.noop, // just a noop controller to attach properties to
    controllerAs: "ctrl", // expose controller properties as "ctrl"
    link: function(scope, element, attrs, ctrls, transclude) {

      // notice the change to "ctrl.ngModel"
      var template = '<label ng-class="{dirty: ctrl.ngModel.$dirty}">{{l}}: \
                        <placeholder></placeholder>\
                      </label>';

      $compile(template)(scope, function(prelinkedTemplate) {
        transclude(function(clonedElement) {
          prelinkedTemplate.find("placeholder").replaceWith(clonedElement);
          element.after(prelinkedTemplate);
        });
      });
    }
  };
})
// second pass
.directive("wrapper", function($compile) {
  return {
    priority: -1,
    require: ["wrapper", "ngModel"],
    link: function(scope, element, attrs, ctrls, transclude) {
      var wrapperCtrl = ctrls[0],
          ngModel = ctrls[1];

      // "hang" ngModel as a property of the controller
      wrapperCtrl.ngModel = ngModel;
    }
  };
});

演示

还有其他方法。例如,我们可以使这个指令具有非常高的优先级(比如priority: 10000)和terminal: true. 然后,我们可以获取元素,包装它,应用另一个require: "ngModel"必须实际跟踪$pristine,$touched等的指令,然后重新编译内容(不要忘记删除原始指令以避免无限循环)。

于 2015-07-29T20:57:43.603 回答