Logan Wlv
Logan Wlv

Reputation: 3734

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?

Upvotes: 1

Views: 606

Answers (2)

georgeawg
georgeawg

Reputation: 48948

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

Upvotes: 2

Logan Wlv
Logan Wlv

Reputation: 3734

A solution I found was to use $watch in the childDirective:

    /**
     * Using $watch instead of data-ng-change ensure that bindings are updated
     */
    $scope.$watch('postalCode', function() {
       $scope.$parent.doSomethingWithPostalCode();
    });

And so removing the data-ng-change on the input of the child directive :

 <input maxlength="5" type="text" data-ng-model="postalCode">

When debugging inside the $watch method, I could verify that the parent $scope was already updated.

Not sure if it is a real solution or more like a hack.

Upvotes: 0

Related Questions