Search code examples
javascriptangularjsangularjs-ng-click

ng-click in directive seems to trigger only once


I am trying to implement a directive that replace a tag by a with ng-click in it. The ng-click call a function that replace the content of a div (always the same) with the content of another element, the id of which is passed as parameter. I managed to make it work, but only for the first click. After that, the page is reloaded and it seems that it is because ng-click isn't triggered, but the (empty) href attribute is.

the html code :

  <!-- language: lang-html -->
  <div id="uiMain" class="container">
    Go to <go target="block01">01</go>, <go target="block02">02</go> or <go target="block03">03</go>
  </div>
  <div id="block01" class="block">
    Go to <go target="block02">02</go> or <go target="block03">03</go>
  </div>
  <div id="block02" class="block">
    Go to <go target="block01">01</go> or <go target="block03">03</go>
  </div>
  <div id="block03" class="block">
    Go to <go target="block01">01</go> or <go target="block02">02</go>
  </div>

and my directive :

.directive('go', function() {
    return {
        restrict: "E",
        scope: {
            target: "@"
        },
        transclude: true,
        template: '<a href="" ng-click="displayBlock(\'{target}\')"><ng-transclude></ng-transclude></a>',
        link: function (scope, element, attrs) {
            scope.displayBlock = function() {
                // Get the content of the target block
                var content = angular.element(document.querySelector("#"+attrs.target));
                // Get the main container
                var mainContainer = angular.element(document.querySelector("#uiMain"));
                // then replace the link with it
                if (content !== null) { 
                    //mainContainer.html($compile(content.innerHTML)($scope)); 
                    mainContainer.html(content.html()); 
                }
            };
        }
    };
});

I have tried putting my function back into the controller but he behavior is the same. I also tried to use $compile or $apply to make sure that the scope is up to date with the pasted html content : this doesn't work because the directives in my target block were already compiled.

Here is my plnkr for the whole code.

I want to be able to click the links to go back and forth, but for now there is something that stop the ng-click from firing more than once.


EDIT: thanks to Mosh-Feu, i managed to make it work by adding ng-non-bindable to all target element and changing my directive as follow :

.directive('go', function($compile) {
return {
    restrict: "E",
    scope: true,
    transclude: true,
    template: '<a href="" ng-click="displayBlock(\'{target}\')"><ng-transclude></ng-transclude></a>',
    link: function (scope, element, attrs) {
        scope.displayBlock = function() {
            // Get the content of the target block
            var content = angular.element(document.querySelector("#"+attrs.target));
            // Get the main container
            var mainContainer = angular.element(document.querySelector("#uiMain"));
            // then replace the link with it
            if (content != null) { 
                mainContainer.html(content.html()); 
                $compile(mainContainer)(scope);
            };
        };
    }
};

});

But Aditya Bhave solution is cleaner, so I will be using it. Thanks a lots guys !


Solution

  • The issue is, when we replace innerHTML of div with html(), it does not compile it as an angularjs code. The correct way is to use ng-templates.

    Take a look at this Plunkr - http://next.plnkr.co/edit/x0TiUkka5nJwqB8K

    1. I am using a div with ng-include attribute set to mainBlock.html template initially.
    2. On Click event is changing the template URL of the parent controller.

    Note - We can have more cleaner code, if we pass temmplateURL as a attribute to directive and then change it in onclick instead of using scope.$parent.