javascript - Angular-需要ngModel并将其用于自定义指令的 Controller 中,而不是链接函数中

标签 javascript angularjs angularjs-directive angular-ngmodel angularjs-validation

谁能告诉我是否有可能在自定义Angular指令的 Controller 内要求和使用ngModel。我试图远离链接功能。我看到大多数示例都使用链接功能,但我想必须在指令 Controller 内使用某种方法来使用它?还是只能通过链接功能访问?如下所示,我所见过的一种方法给了我未定义的含义。我不确定是否还有其他方法?我正在尝试验证组件,并在错误对象上设置了无效的类。

//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)。重新阅读您的问题后,我不确定您到底在遇到什么问题(无论是如何在指令定义对象中使用required或如何使用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的最新版本,否则上述版本将无法使用。我记不清是哪一部分了(我觉得它可能正在将需求对象键自动绑定(bind)到 Controller 对象),但是我肯定遇到了一个讨厌的错误,上面的代码不起作用,我使用的是1.4 .6。

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

1).ng-invalid类将以无效的 Angular 验证形式应用于任何输入。例如,如果输入上有必填属性,而输入为空,则输入将具有ng-invalid类。此外,它将具有类.ng-invalid-required。输入上的每个验证规则都有自己的ng-invalid类。您说要在第一次模糊输入后向输入添加红色边框。执行此操作的标准方法是使用如下css规则:
.ng-invalid.ng-touched {
   border: 1px #f00 solid;
}

如果检查经过验证的输入,您将看到各种 Angular 类。其中之一是.ng风格的。触摸的元素是至少被模糊一次的元素。如果要确保仅对模糊应用验证,可以使用ng-model-options指令。

2)$ formatter用于格式化模型值。 Angular有两种方式进行数据绑定(bind)。这意味着angular正在观察模型值和 View 值。如果其中一个更改 Angular ,则执行工作流程以更新另一个。工作流程如下:

查看值更改-> $ parsers-> $ validators->更新模型值
模型值更改-> $ formatters->更新 View 值

工作流程的结果将填充到另一个值中。这意味着,如果要在 View 中显示模型值之前更改模型值(也许要格式化日期),则可以在$ formatter中执行。然后,当它返回模型时,您可以在$ parser中执行相反的操作。当然,在编写$ validators时,您应该认识到$ parsers中发生了什么,因为在将解析的 View 值发送到模型之前,它是经过验证的。

3)根据我从angular文档添加的报价,很明显,您不应使用任何逻辑,该逻辑包含的值已由$ onInit之外的bindToController绑定(bind)到 Controller 。这包括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;
}

您知道,我正在测试以查看是否在设置有效性之前对下拉列表进行了$ touch(即模糊)处理。这两种方法之间存在根本差异。使用ng-model-options基本上会将整个更新工作流程推迟到模糊为止。这意味着只有在输入模糊后,您的模型值才会更新为与 View 值匹配。第二种方法(带有$ touched)将在每次viewValue更改时进行验证,但仅在第一次模糊后才使输入无效。

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

为了获得对formCtrl的访问权限,您需要向require对象添加另一个属性(formCtrl:'^ form')

关于javascript - Angular-需要ngModel并将其用于自定义指令的 Controller 中,而不是链接函数中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35532187/

相关文章:

javascript - Angular Material md-select 不显示错误

javascript - Angular : Using $last to get last element visible using ng-show

javascript - 使用 ng-if 时,$scope.$watch 会以相同的旧值和新值触发

javascript - 如何使用 AngularJS 中的另一个输入更改密码验证?

css - 将样式与指令相关联的正确位置在哪里?

javascript - 在 jQuery 中访问私有(private)变量的问题,如可链接设计模式

angularjs - Angular UI Bootstrap Popover - 所有打开的 popover 的关闭程度

javascript - 选择 :target on document. 就绪()

javascript - 如何更改ionic 3中的后退按钮

Javascript for 循环不适用于函数 keydown