LJW
LJW

Reputation: 2388

Cascading angular directives

I have the following Angular binding setup:

<div ng-repeat="app in vm.Apps">
    <div ng-style="{ background: app.bgColour }">           
        <p app-shadow="large"></p>
    </div>      
</div>

As you can see, I'm binding to a list of items, binding the inner div background, and also have a custom directive 'app-shadow'.

The code for my directive is:

function addShadowDirective($document) {
    return function (scope, element, attr) {
        $(element).iluminate(
            { size: 64, textSize: 30, alpha: 0.5, textAlpha: 0.2, fade: 0, fadeText: 0, includeText: false, textOnly: true, direction: "bottomRight" });
    };
}

The appShadow directive depends on an existing js library (jQuery Illuminate) which uses the parent background colour of the given element. It uses JQuery.css("background-color") to determine the parent element's bg colour (line 22 of the source link above).

My problem seems to be that when parent bgcolour is evaluated, it's not what Angular has bound. I'm guessing there's a race condition between the two directives.

Any ideas for what I can do to ensure the ng-style directive is evaluated before my custom directive?

Thanks in advance.

Upvotes: 2

Views: 1030

Answers (2)

m59
m59

Reputation: 43795

I made a demo in which you can check the console log to see the order that compile, postLink, and preLink will run between parent and children. In this example, you can see that both have access to scope, but the parent logic is running before the child logic, which seems to be your goal.

Live demo (click).

I would use the directive to establish a watch, which seems natural and is more expandable. Otherwise, I would make use of the post/pre execution order like so:

<div parent>Should be red.          
  <p child>Should be green.</p>
</div>


var app = angular.module('myApp', []);

app.controller('myCtrl', function($scope) {
  $scope.bkgr = 'red';
  $scope.bkgr2 = 'green';
});

app.directive('parent', function() {
  return {
    compile: function(element, attrs) {
      console.log('parent compile');
      return {
        pre: function(scope, element, attrs) {
          console.log('parent pre');
          element.css('background-color', scope.bkgr);
        },
        post: function(scope, element, attrs) {
          console.log('parent post');
        }
      };
    }
  };
});

app.directive('child', function() {
  return {
    compile: function compile(element, attrs) {
      console.log('child compile');
      return {
        pre: function preLink(scope, element, attrs) {
          console.log('child pre');
        },
        post: function postLink(scope, element, attrs) {
          element.css('background-color', scope.bkgr2);
        }
      };
    }
  };
});

Upvotes: 1

musically_ut
musically_ut

Reputation: 34288

This is indeed happening because probably of a race condition and putting your code inside a $timeout(..., 0) might solve it by forcefully delaying it by one $digest loop.

However, this might continue to be a problem if app.bgColour changes after your app-shadow directive has initialised as well.

The best solution in such cases is usually to setup a $watch on the property you depend on:

link: function (scope, elem, attrs) {
      // ...
      scope.$watch(attrs.color, function (newVal) { 
          if (typeof newVal !== 'undefined') {
              var color = newVal;
              // ... 
          }
      });
 }

Template:

<p app-shadow="large" color="app.bgColor"></p>

Upvotes: 3

Related Questions