Why does the link function of my directive never g

2020-07-17 06:03发布

问题:

I have this directive:

    hpDsat.directive('ngElementReady', [function() {
        return {
            restrict: "A",
            link: function($scope, $element, $attributes) {
                // put watches here.
                console.log(" WHAT THE @#%*%$??? ");
                $scope.$eval($attributes.ngElementReady);
            }
        };
    }]);

I never see the output of the console.log. I'm declaring an element like this:

<div data-ng-element-ready="console.log(' ------------------------------- COMPILED! ')" data-ng-if="analysis.type" data-ng-show="showBasicHtml" data-ng-include="analysis.type+'Content.html'"></div>

Yes, I am declaring the directive before I declare the controller under which the div element exists. The element appears, ngShow and ngInclude works, and anything in the loaded template works just fine too (more directives, controllers, {{expressions}}, etc).

If I execute it with a compile function, the compile function does work, but still not the link function:

    hpDsat.directive('ngElementReady', [function() {
        return {
            restrict: "A",
            compile: function($element, $attributes) {
                console.log("This I do see."); // THIS WORKS!!
                return function($scope) {
                    // put watches here.
                    console.log("But not this. Why???"); // DOESN'T WORK!!
                    $scope.$eval($attributes.ngElementReady);
                };
            }
        };
    }]);

The console.log of the compile function works fine, but the returned link function still never gets executed.

Any idea why the link function might not get fired?

回答1:

The error might be somewhere else. I tried it in a jsfiddle and the first version works.

eval()

In any case, you might have a missunderstanding about what $scope.$eval() does:

$scope.$eval() evaluates angularjs code against a scope. JavaScript's eval() function to run arbitrary js code in angularjs is $window.eval(). More about this here: Angular.js: How does $eval work and why is it different from vanilla eval?

I tested the directive isolated from the controller:

<div data-ng-element-ready="console.log('COMPILED!')"></div>

and the directive:

app.directive('ngElementReady', ['$window', function($window) {
return {
    restrict: "A",
    link: function($scope, $element, $attributes) {
        console.log("Reached the link fn", $attributes);
        $window.eval($attributes.ngElementReady);
    }
};
}]);

I do get the value "reached the link fn" and $attributes is correct:

Reached the link fn Object { $$element={...}, $attr={...},  
ngElementReady="console.log('COMPILED!')", more...}

And $window.eval() returns COMPILED!

and here with a controller.

in any case, using eval() to execute code written in an attribute looks dangerous. Anybody can modify the DOM to run code there. At least make sure that it won't affect any other users or server-side.

Reproducing the problem with ng-if

Edited after first comment: I tried making the ng-if expression evaluate to false here This time it doesn't show the message. This probably happens because in order to evaluate ng-if, yuo must compile first the directive. otherwise, it's just code that anuglarjs isn't aware of. However, because it is removed from the DOM, it never reaches the link function.

Execution order of AngularJS functions

In general, the order of execution goes like this (Josh David Miller explains it:

<div directive1>
  <div directive2>
    <!-- ... -->
  </div>
</div>

Now AngularJS will create the directives by running directive functions in a certain order:

directive1: compile
  directive2: compile
directive1: controller
directive1: pre-link
  directive2: controller
  directive2: pre-link
  directive2: post-link
directive1: post-link