5

so i'm making a simple directive called "hover", it's a basic nav menu that when you pass a mouse over a specific aba, this aba changes the color. See my script code:

var app = angular.module('myModule', []);
app.directive('hover', function(){
    return{
        restrict: 'E',
        controller: function($scope) {
            $scope.hover    = null;
            $scope.selected = null;

            $scope.onHover = function (index){
                $scope.hover = index;
            }
            $scope.mouseLeave = function(){
                if($scope.selected)
                    $scope.hover = $scope.selected;
                else
                    $scope.hover = -1;
            }
            $scope.onClick = function(index) {
                $scope.hover    = index;
                $scope.selected = index;
            }

        },
        compile: function(el, attrs){
            el.children().attr('data-ng-mouseover', 'onHover('+attrs.index+')');
            el.children().attr('data-ng-mouseleave', 'mouseLeave()');
            el.children().attr('data-ng-click', 'onClick('+attrs.index+')');
            el.children().attr('data-ng-class', '{'+ attrs.onhover +': hover == ' + attrs.index + ' || selected == ' + attrs.index + '}');
        }
    }
});

And now my html:

<ul>
    <hover index="0" onhover="hover"><li>Home</li></hover>
    <hover index="1" onhover="hover"><li>About Us</li></hover>
    <hover index="2" onhover="hover"><li>Contact</li></hover>
    <hover index="3" onhover="hover"><li>Share with us!</li></hover>
</ul>

This is working fine, but when i put my html in this way:

<ul>
    <li hover index="0" onhover="hover">Home</li>
    <li hover index="1" onhover="hover">About Us</li>
    <li hover index="2" onhover="hover">Contact</li>
    <li hover index="3" onhover="hover">Share with us!</li>
</ul>

This doesn't work, i have to wrap my "li" with "hover" tag to make this work(and yes, i'm changing the restrict property to "A"), why? And another question, wrapping my "li" with "hover" tag is a bad trick to validation my html?

This is my html compiled:

<ul>
<li onhover="hover" index="0" hover="" data-ng-mouseover="onHover()">Home</li>
<li onhover="hover" index="1" hover="" data-ng-mouseover="onHover()">About Us</li>
<li onhover="hover" index="2" hover="" data-ng-mouseover="onHover()">Contact</li>
<li onhover="hover" index="3" hover="" data-ng-mouseover="onHover()">Share with us!</li>
</ul>

When i pass the mouse over these elements, my function: "onHover" doesn't is called.

Rodrigo Fonseca
  • 944
  • 7
  • 21
  • 2
    You are restricting the directive to the type `E` as in `Element`, you need to look into the other restriction types, classes, attributes, etc. Sounds like you may want `restrict: 'A'` – jnthnjns Jan 23 '14 at 17:35
  • Change `restrict: 'E'`, that means "restrict do element" to `restrict: 'A'`, that means "restrict to attribute" – Beterraba Jan 23 '14 at 17:36
  • Sorry, i update now, but i changed the property to "A". – Rodrigo Fonseca Jan 23 '14 at 17:37

1 Answers1

13

First a clarification..

  • I do not recommend overusing $compile, there are better ways for binding event listeners to a scope.
  • I solved this question for me to learn how compilation works and to share it with others.

what happens when manipulating template element inside a the compile function?

  • The compilation phase iterates down the DOM , from parent to children element.
  • When you manipulate children of a DOM element inside a compile function it happens before $compile got down to these children elements to collect their directives, so every change you make to the contents of the template element will be compiled and linked with the continuation of the compilation phase.
  • This is not the case when you manipulate the template element itself , then $compile will not look for more directives in that same element because it's only collecting directives once per each DOM element.
  • So these attributes you added are just not being compiled!

Lets $compile it manually:

  • I tried to add $compile(el) but my browser crashed (don't laugh at me), The reason is it got into a loop where it's infinitely compiling itself.
  • So I removed the directive attribute and then $compile again.
  • I set { priority:1001 } and { terminal:true }. This iss needed to prevent other directive compile functions to run before our directive or after out manual compiling.

Solution:

here is a plunker: http://plnkr.co/edit/x1ZeigwhQ1RAb32A4F7Q?p=preview

app.directive('hover', function($compile){
  return{
    restrict: 'A',
    controller: function($scope) {

       // all the code
    },

    priority: 1001, // we are the first

    terminal: true, // no one comes after us

    compile: function(el, attrs){

      el.removeAttr('hover'); // must remove to prevent infinite compile loop :()

      el.attr('data-ng-mouseover', 'onHover('+attrs.index+')');
      el.attr('data-ng-mouseleave', 'mouseLeave()');
      el.attr('data-ng-click', 'onClick('+attrs.index+')');
      el.attr('data-ng-class', '{'+ attrs.onhover +': hover == ' + attrs.index + ' || selected == ' + attrs.index + '}');

      var fn =  $compile(el); // compiling again

      return function(scope){
        fn(scope); //
      }
    }
  }
});
Ilan Frumer
  • 32,059
  • 8
  • 70
  • 84
  • "I set { priority:1001 } and { terminal:true }. This iss needed to prevent other directive compile functions to run before our directive or after out manual compiling." Ok, but if i want to use another directive with compile function? And you said that exists better options for binding event listeners, what are the better options? And, thanks for your explanation. – Rodrigo Fonseca Jan 23 '14 at 22:38
  • I will update my answer when I have time. I found a similar question: http://stackoverflow.com/questions/19224028/add-directives-from-directive-in-angularjs/19228302#19228302 – Ilan Frumer Jan 24 '14 at 12:06