Search code examples
htmlangularjsangularjs-directiveangularjs-scope

How to avoid digest cycle delay from two-way binding


Angularjs when two way data binding is triggered?

In an AngularJS application I have a parent directive and a child directive.

parentDirective

angular
  .module('myApp')
  .directive('customForm', function(customService, apiV1, Constants, $timeout) {
    return {
      restrict: 'E',
      scope: {
        param1: '=',
        param2: '=?',
        boolean1: '@?'
      },
      template,
      link: function(scope, parentController) {
      scope.data = customService.newClient;
      //some stuff...

childDirective JS:

angular
  .module('myApp')
  .directive('customToolForm', function () {
    return {
      restrict: 'E',
      scope: {
        name: '=',
        city: '=',
        postalCode: '='
      },
      template,
      controller: function ($scope, $rootScope, Constants, apiV1, customService) {

     $scope.doSomethingWithPostalCode = function() {
         $scope.$parent.doSomethingWithPostalCode();
     }
     //some stuff...

parentDirective HTML piece:

<address-client-creation name="data.client.name" city="data.client.city"
                         postal-code="data.client.postalCode">
</address-client-creation>

childDirective HTML piece :

 <input maxlength="5" type="text" data-ng-model="postalCode"
        data-ng-change="doSomethingWithPostalCode();">

The issue I have is that :

When the method doSomethingWithPostalCode is triggered from the childDirective, the value of the postalCode in the child is not the same as the client.postalCode of the parent, but at the end of the method it is.

it seems that the two way binding event that update the parent value is happening after the function call

So my question is what is the best way to ensure that the $parent scope is updated before calling the method?


Solution

  • How to avoid digest cycle delay from two-way binding

    The AngularJS framework implements two-way ('=') binding by adding a watcher to the child scope which transfer the data from the child scope to the parent scope. The watcher requires a digest cycle to detect the change and do the transfer.

    A more modern approach is to use one-way ("<") binding for inputs and expression ("&") binding for outputs:

    app.directive('customToolForm', function () {
        return {
          restrict: 'E',
          scope: {
            name: '<',
            city: '<',
            ̶p̶o̶s̶t̶a̶l̶C̶o̶d̶e̶:̶ ̶'̶=̶'̶
            postalCode: '<',
            postalCodeChange: '&',
          },
          template: `
              <input maxlength="5" type="text" data-ng-model="postalCode"
                     data-ng-change="doSomethingWithPostalCode(postalCode);">
          `,
          controller: function ($scope, $rootScope, Constants, apiV1, customService) {
    
         $scope.doSomethingWithPostalCode = function(postalCode) {
             ̶$̶s̶c̶o̶p̶e̶.̶$̶p̶a̶r̶e̶n̶t̶.̶d̶o̶S̶o̶m̶e̶t̶h̶i̶n̶g̶W̶i̶t̶h̶P̶o̶s̶t̶a̶l̶C̶o̶d̶e̶(̶)̶;̶
             $scope.postalCodeChange({$event: postalCode});
         }
         //some stuff...
    

    Usage:

    <custom-form-tool 
        name="data.client.name" city="data.client.city"
        postal-code="data.client.postalCode"
        postal-code-change="data.client.postalCode=$event; doSomething($event)"
    >
    </custom-form-tool>
    

    Using expression ("&") binding immediately makes the event data available to the parent controller.

    It also makes the migration path to Angular 2+ easier.

    For more information, see