letmejustfixthat
letmejustfixthat

Reputation: 3459

Triggering ngChange from inside a custom directive manually

I want to build custom directives having the same ng-change syntax as a normal input field. Let's say I have a directive like this:

;(function() {
  'use strict';
  angular.module('myMod', []);
  angular.module('myMod').directive('myDir', myDirDirective);

  function myDirDirective() {
    return {
      restrict: 'E',
      template: '
        <input ng-change="checkInput1()" type=...>
        <input ng-change="checkInput2()" type=...>
      ',
      controllerAs: 'myDirDirective',
      controller: myDirController,
      require: 'ngModel',
      scope: {},
      bindToController: {
        model: '='
      }
    };
  }

// Controller goes here...

})(); // EOF

Now I want to define the input check methods like

function checkInput1() {
  ...
  if( <changes in input 1 are legit> ) {
    fireOuterNgChange();
  }
}
function checkInput2() {
  ...
  if( <changes in input 2 are legit> ) {
    fireOuterNgChange();
  }
}

And finally I want to be able to use my custom directive like:

<myDir ng-change="doSomethingAfterSomethingChanged()">
  ...
</myDir>

A simple use case for this would be a time picker with several input fields for hours : minutes : seconds : milliseconds. Just to give an example. I tried different approaches without success; How can I do this?

Upvotes: 0

Views: 2395

Answers (2)

letmejustfixthat
letmejustfixthat

Reputation: 3459

@Neozaru answer works perfect. But to be complete I'm posting a complete code example for easier understanding. Following the John Papa's Style Guide and using the controllerAs Syntax instead of $scope (example use case: having a re-usable userform):

Implement you custom directive with your custom ng-change events

First, the template

 // my-dir-template.html
 Username: <input type="text" name="username" ng-change="passwordForm.changeHandler()" ng-model="passwordForm.model">
 Password: <input type="text" name="password" ng-change="passwordForm.changeHandler()" ng-model="passwordForm.model">

The directive and the controller

;(function() {
  'use strict';
  angular.module('myMod', []);
  angular.module('myMod').directive('passwordForm', passwordFormDirective);

  function passwordFormDirective() {
    return {
      restrict: 'E',
      templateUrl: 'password-form.html',
      controllerAs: 'passwordForm',
      controller: passwordFormController,
      require: 'ngModel',
      scope: {},
      bindToController: {
        model: '=',
        ngChange: '&'                  // << bind ng-change to controller (not scope)
      }
    };
  }

  function passwordFormController() {  // no scope injected
    // Pseudo this
    var vm = this;
    // implement controller
    vm.changeHandler = changeHandler ;


    // // // Method implementations
    ...
    function changeHandler() {
      // we could do validation here, altough I'm not sure if this would be a good idea here. 
      // for now, we only do the change listener notification
      if(vm.ngChange === 'function') {
         vm.ngChange();
      }
    }
  }
})(); // EOF

Now we can use our directive with the normal ng-change listener. Maybe for registration of new users:

<password-form ng-model="the-model" ng-change="checkIfUsernameExists()">

Upvotes: 2

Neozaru
Neozaru

Reputation: 1130

So here you need to perform two things :

  1. Intercepting changes on your internal directive <input> elements.
  2. Forwarding these changes to the parent of your custom <my-dir>.

For (1), you'll simply do as your said : You register callbacks in your directive's scope (scope.checkInput1 = function() { ... }).

Then, to forward the event to the parent (finally imitating the <input ng-change> behavior), you will need to declare an expression binding in the Directive's isolated scope like this :

scope: {
  onDateChange: '&'
}

On the parent Controller, assuming that you declared some $scope.onDirectiveDateChange = function() { ... } in the scope, you just pass the callback into your custom Directive like this :

<my-dir on-date-change="onDirectiveDateChange()"></my-dir>

Then you call it from your directive's checkInput2 :

scope.onDateChange(); // This will call parent's "onDirectiveDateChange()" if defined

Upvotes: 2

Related Questions