javascript – Angular – 需要ngModel并在自定义指令的控制器中使用它,而不是链接功能

任何人都可以告诉我是否可以在自定义Angular指令的控制器中要求和使用ngModel.我试图远离链接功能.我看到大多数例子使用链接函数,但我认为必须有一些方法在指令控制器中使用它?或者它只能在链接功能中访问?我所看到的一种方式,如下所示,给了我一些未定义的方法.我不确定是否还有其他方法?我正在尝试验证组件并在错误对象上设置了无效的类.

//directive
angular.module('myApp', [])
  .directive('validator', function (){
    return {
      restrict: 'E',
      require: {
           ngModelCtrl: 'ngModel',
           formCtrl: '?^form'
      },
      replace: true,
      templateUrl: 'view.html',
      scope: {},
      controllerAs: 'ctrl',
      bindToController: {
         rows: '=',
         onSelected: '&?' //passsed selected row outside component
         typedText: '&?' //text typed into input passed outside so developer can create a custom filter, overriding the auto
         textFiltered: '@?' //text return from the custom filter
         ngRequired: "=?" //default false, when set to true the component needs to validate that something was selected on blur. The selection is not put into the input element all the time so it can't validate based on whether or not something is in the input element itself. I need to validate inside the controller where I can see if 'this.ngModel' (selectedRow - not passed through scope) is undefined or not.
      },
      controller: ["$scope", "$element", function ($scope, $element){
         var ctrl = this;
         ctrl.rowWasSelected;

         //called when a user clicks the dropdown to select an item
          ctrl.rowSelected = function (row){
               ctrl.rowWasSelected = true;
               ctrl.searchText = row.name; //place the name property of the dropdown data into ng-model in the input element
          }

         ctrl.$onInit = $onInit;
         function $onInit (){
             ctrl.ngModelCtrl.$validators.invalidInput = validate;            
          }

        function validate (modelValue, viewValue) {
             var inputField = ctrl.formCtrl.name;
             var ddField = ctrl.formCtrl.listData;

             inputField.$setValidity('invalidInput', ddField.$touched && ctrl.rowWasSelected);

            return true;
          }          
       }];
   }
});

//template
<form name="validatorForm" novalidate>
  <div class="form-group" ng-class="{ng-invalid:validatorForm.name.$error.invalid}">
     <label for="name">Names</label>
     <input type="name" class="form-control" name="name" placeholder="Your name" ng-change="typedText(text)" ng-model="ctrl.textFiltered" ng-blur="ctrl.validate()" ng-required="ctrl.ngRequired">
  </div>
  <ul ng-show="show list as toggled on and off" name="listData" required>
    <li ng-repeat="row in ctrl.rows" ng-click="ctrl.rowSelected({selected: row}) filterBy:'ctrl.textFiltered' ng-class="{'active':row === ctrl.ngModel}">{{row}}<li>
  </ul>
</form>

//html
<validator
   rows="[{name:'tim', city:'town', state:'state', zip: 34343}]"
   on-selected="ctrl.doSomethingWithSelectedRow(selected)"
   typed-text="ctrl.manualFilter(text)"
   text-filtered="ctrl.textReturnedFromManualFilter"
   ng-required="true">
</validator>

最佳答案 这是重构的代码(注意:你需要使用最新的Angular来实现其中一些).在重新阅读您的问题之后,我不确定您究竟遇到了什么问题(无论是如何在指令定义对象中使用必需或如何使用ngRequired属性或其他内容).请注意,使用下面的代码,您不需要$scope:

angular.module('myApp', []);
angular.module('myApp').directive('validator', validator);

function validator (){
    return {
        restrict: 'E',
        require: {
            ngModelCtrl: 'ngModel'
        },
        replace: true,
        templateUrl: 'view.html',
        scope: {}, //this controls the kind of scope. Only use {} if you want an isolated scope.
        controllerAs: 'ctrl',
        bindToController: {
            rows: '=',
            onSelected: '&?', //passsed selected row outside component
            typedText: '&?', //text typed into input passed outside so developer can create a custom filter, overriding the auto
            textFiltered: '@?', //text return from the custom filter
            ngRequired: "=?" //default false, when set to true the component needs to validate that something was selected on blur. The selection is not put into the input element all the time so it can't validate based on whether or not something is in the input element itself. I need to validate inside the controller where I can see if 'this.ngModel' (selectedRow - not passed through scope) is undefined or not.
        },
        controller: 'validatorController'
    }
}

//usually do this in a new file

angular.module('myApp').controller('validatorController', validatorController);
validatorController.$inject = ['$element'];

function validatorController($element){
    var ctrl = this;

    //controller methods
    ctrl.validate = validate;

    ctrl.$onInit = $onInit; //angular will execute this after all conrollers have been initialized, only safe to use bound values (through bindToController) in the $onInit function.

    function $onInit() {
        if(ctrl.ngRequired)
            ctrl.ngModelCtrl.$validators.myCustomRequiredValidator = validate;
    }



    //don't worry about setting the invalid class etc. Angular will do that for you if one if the functions on its $validators object fails
    function validate (modelValue, viewValue){
        //validate the input element, if invalid add the class ng-invalid to the .form-group in the template
        //return true or false depending on if row was selected from dropdown
        return rowWasSelected !== undefined
    }
}   

以下是来自Angular的$compile文档的几个片段:

If the require property is an object and bindToController is truthy,
then the required controllers are bound to the controller using the
keys of the require property. This binding occurs after all the
controllers have been constructed but before $onInit is called.

Deprecation warning: although bindings for non-ES6 class controllers
are currently bound to this before the controller constructor is
called, this use is now deprecated. Please place initialization code
that relies upon bindings inside a $onInit method on the controller,
instead.

再次确保您使用的是最新版本的Angular,或者上述版本无法使用.我不记得确切的哪个部分(我觉得它可能会让需求对象键自动绑定到控制器对象),但我肯定遇到了一个令人讨厌的错误,上面的内容不起作用,我使用的是1.4 0.6.

第二次编辑:只想清理一些事情:

1).ng-invalid类将应用于无效的角度验证表单中的任何输入.例如,如果输入上有必需属性且输入为空,则输入将具有ng-invalid类.此外,它将具有类.ng-invalid-required.输入上的每个验证规则都有自己的ng-invalid类.您说您想在第一次模糊后为输入添加红色边框.执行此操作的标准方法是使用如下css规则:

.ng-invalid.ng-touched {
   border: 1px #f00 solid;
}

如果您检查已验证的输入,您将看到各种角度类.其中一个是感动的.被触摸的元素是至少被模糊一次的元素.如果您想确保仅对模糊应用验证,可以使用ng-model-options指令.

2)$formatters用于格式化模型值. Angular有双向数据绑定.这意味着angular是$观看模型值和视图值.如果其中一个更改角度执行工作流程以更新另一个.工作流程如下:

查看值更改 – > $parsers – > $validators – >更新模型值
模型值变化 – > $formatters – >更新视图值

工作流程的结果填充到另一个值中.这意味着如果您想在视图中显示模型值之前更改模型值(可能您想格式化日期),那么您可以在$formatter中执行此操作.然后,您可以在$parser中执行相反的操作,因为它返回到模型.当然,您应该在编写$validators时了解$parsers中发生的情况,因为它是在被发送到模型之前得到验证的已解析视图值.

3)根据我从angular docs添加的引用,很明显你不应该使用任何包含$onInit之外的bindToController绑定到控制器的值的逻辑.这包括ngModelCtrl.请注意,只要您确定其他函数将在$onInit之后执行,您就可以将逻辑放在另一个函数中.

4)这里要考虑两件事:哪个控件出现错误,以及从哪里触发验证.听起来你想从下拉列表的工作流程中触发它(即在它被模糊一次之后).所以,我建议在下拉列表中添加验证器.现在,您说要验证输入而不是下拉列表.因此,您可以在验证器中使用$setValidity.要确保下拉列表始终“有效”,您只需从验证程序返回true即可.你说你只想在模糊后验证.有两种方法可以做到这一点(在我的头顶).一种是使用我上面提到的ng-model-options,另一种是测试验证器中是否触摸了下拉菜单.以下是使用第二种方法的一些代码:

function validate (modelValue, viewValue) {
    var inputField = ctrl.formCtrl.inputName, ddField = ctrl.formCtrl.ddName;

    inputField.$setValidity('validationName', ddField.$touched && rowSelectedCondition);
    return true;
}

你看,我正在测试在设置有效性之前是否已经触摸下拉(即模糊).这两种方法之间存在着根本的差异.使用ng-model-options基本上会推迟整个更新工作流程直到模糊.这意味着您的模型值只会在输入模糊后更新以匹配视图值.第二种方式(使用$touch)将在每次viewValue更改时进行验证,但仅在第一次模糊后呈现输入无效.

‘validationName’参数将仅指定在输入无效时添加的类,因此在这种情况下,它将添加两个类.ng-invalid(添加到任何无效控件)和.ng-invalid-validation-name.

要访问formCtrl,您需要向require对象添加另一个属性(formCtrl:’^ form’)

点赞