Angular - Requiring ngModel and using it in a custom directive controller, not a link function

Can someone tell me if it is possible to require and use ngModel inside the controller of the Angular custom directive. I try to stay away from the communication function. I see that most examples use the link function, but I think there must be some way to use it inside the directory controller? Or is it only available in the communication function? One of the ways I've seen to do this, as shown below, gives me undefined. I'm not sure if there is another way? I am trying to test a component and set an invalid class on an error object.

//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>
+4
2

, ngModel , - false. , , . , , .

:

.directive('myValidator', function (){
return {
  restrict: 'E',
  replace: true,
  templateUrl: 'view.html',
  scope: false
  };
}

. , ng-model = "ctrl.name" - , . . , ..

. , html. ngModel , ctrl.user , . ngModel , .

0

, (: 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 docs $:

require bindToController , , . , , $onInit.

: ES6 , . $onInit , .

, , Angular . , ( , , ), , , 1.4 0,6.

Second Edit: :

1) .ng-invalid Angular, . , , , ng-invalid. , .ng-invalid-required. ng-invalid. , , . - css :

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

, Angular. . - , . , , ng-model-options.

2) $formatters . Angular . , Angular $, . Angular, . :

→ $parsers → $validators → → $formatters →

. , , (, ), $formatter. $, . , , $, $validators, , ​​.

3). , Angular, , - , , bindToController $onInit. ngModelCtrl. , , , AFTER $onInit.

4) : . , (.. ). . , , . , $setValidity . , "", true . , . ( ). ng-model, , - , . :

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

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

, , , $touched (.. ) , . . ng-model-options . , . ( $) , viewValue, .

validationName , , , .ng-invalid ( ) .ng-invalid-validation-name.

formCtrl, require (formCtrl: '^ form')

0

Source: https://habr.com/ru/post/1629738/


All Articles