JimmyBoy
JimmyBoy

Reputation: 369

custom directive's link function is invoked before ng-repeat is replcaed

I have the following directive:

.directive("picSwitcher", ["$timeout", function($timeout){
return {
    restrict: "A",
    scope: {
        pics: "=",
        fadeTime: "@",
        timeForPic:"@"
    },
    template: '<img ng-repeat="pic in pics" src="{{pic.url}}" style="display: none"/>',
    link:   function ($scope, element){
                //some code...
                $(element.find("img")[0]).css({display: "block"});
            }
};
}])

my problem is, when my link function is invoked - ng repeat is yet to "compile" (what word should be used here instead of compiled?)

so I am trying to set css of undefined.. how can I force the link function to run after ng-repeat finished?!

for now I am solving this by replacing $(element.find("img")[0]).css({display: "block"}); with $timeout(function(){ $(element.find("img")[0]).css({display: "block"});}, 200);

but that feels 'hacky'

is there something I am missing to achieve my goal in an easier way? in general, what is the best way to manipulate ng-repeat dom element inside the link function of a custom directive?

Thanks, Jimmy.

Upvotes: 4

Views: 440

Answers (2)

gyc
gyc

Reputation: 4360

You can use JQLite's .ready() function.

    post: function postLink(scope, element) {
      element.ready(function() {
        //$(element.find("li")[0]).css({color: "red"});
        element.find("li:first-child").css({color: "red"});
      });
    }

I also changed the way you select elements in your directive. Since you have element available you can just use JQLite. However, to just change a css class, you should do it in a CSS file.

In the snippet below I replaced <img> with <ul><li> but it works the same for images.

function myDirective() {
  return {
    template : '<ul><li ng-repeat="pic in pics">{{pic.name}}</li></ul>',
    scope: {
      pics: '='
    },
    link: function(scope, element) {
        element.ready(function() {
          //$(element.find("li")[0]).css({color: "red"});
          element.find("li:first-child").css({color: "red"});
        });
    }
  }
}
function SuperController() {
	this.pics = [{name:"rob"}, {name:"jules"}, {name:"blair"}];
}
angular.module('myApp', []);
angular
    .module('myApp')
    .controller('SuperController', SuperController)
    .directive('myDirective', myDirective);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<div ng-app="myApp">
  <div ng-controller="SuperController as s">
    <my-directive pics="s.pics">
    </my-directive>
  </div>
</div>

Upvotes: 1

MMhunter
MMhunter

Reputation: 1441

you can check $scope.$evalAsync

$scope.$evalAsync(function(){
    $(element.find("img")[0]).css({display: "block"});
}

This will make the function executed after the dom rendering.

Also, using $timeout is not a bad idea if u set the delay to 0

$timeout(function(){
    $(element.find("img")[0]).css({display: "block"});}, 
0);

will also do the trick i think.

More reference http://www.bennadel.com/blog/2605-scope-evalasync-vs-timeout-in-angularjs.htm

Upvotes: 1

Related Questions