2

I have a directive <my-dir></my-dir> and the directive should always have a specific functionality provided through angular attributes: <my-dir ng-show="ctrl.shown"></my-dir>. Here ctrl is the controller defined through controllerAs: 'ctrl' on the directive's definition.

I would like to simplify this directive and avoid mistakes by adding the attribute automatically when the directive is compiled. That way other users only have to type <my-dir>. Adding an attribute to a directive is simple, but these new attributes don't seem to get compiled.

app.directive("myDir", function() {
  console.log('myDir');
  return {
    restrict: 'E',
    controller: 'DirectiveController',
    controllerAs: 'ctrl',
    template: '<span>Controller Content</span>'

    // Attempt to attach the 'ng-show' attribute to the element

    link: function($scope, $element, attrs) {
      $element.attr('ng-show', 'ctrl.shown');
    }

  };
});

I've tried a bunch of different things: adding the attribute in link:, in compile: and even using $compile($element[0]['my-dir'])($scope) after adding the attribute..

Example plnkr: http://plnkr.co/edit/PREavAIn1vqUdZYLsypt?p=preview

Example plnkr using the ctrl.shown: http://plnkr.co/edit/dTWIzClZG4YpE76AMCWk?p=preview

Raven
  • 1,453
  • 3
  • 18
  • 29

3 Answers3

1

Update

so putting this logic into the compile step of the directive seems to be the right direction:

...
compile: function(tElem, tAttrs) {

  // appears that you need to remove this attribute  
  // to stop the directive from looping infinitely 
  // through the compile step
  tElem.removeAttr('my-dir'); 
  tElem.attr('ng-hide', 'true');

  return function(scope) {
    $compile(tElem)(scope);
  }
}

Here's the updated plnkr: http://plnkr.co/edit/uyfJl8b1w5esYGv7SIsx?p=preview

see this question

Community
  • 1
  • 1
paul trone
  • 702
  • 5
  • 10
  • 1
    Are you sure? Last time I tried it it looped, triggering 1000x times before my browser gave up (Maximum call stack size exceeded), just like in your updated version :S – Raven Feb 11 '16 at 16:58
  • you're right, my plnkr is blowing up, trying other things. – paul trone Feb 11 '16 at 16:59
  • Thanks for the update! I really didn't consider removing the directive.. EDIT : Aha - in your solution you move from an Element restricted directive to an Attribute directive. Now I understand why it worked/didn't work. :) Going to have to look further though – Raven Feb 12 '16 at 08:30
  • Update: Found an acceptable solution based on your suggestion, see my answer in this thread. :) – Raven Feb 12 '16 at 09:39
  • The line removing the 'my-dir' attribute is what I needed to get my code working (to prevent infinite recursion). Thanks! – rinogo Sep 08 '16 at 18:01
1

Solution by OP

Based on paul trone's answer to this topic I came up with a solution that works for me.

Basically I re-compile the element after adding angular attributes. To avoid infinite looping I also tag the element with a custom tag and stop compiling when an element is tagged.

This way I can deploy <my-dir></my-dir> multiple times, each with the implicit custom functionality at the root level of the directive. This way it is possible to use ng-show to hide the whole directive and not only the templated elements inside.

angular.module('app', []);

angular.module('app').directive("myDir", function($compile) {
  return {
    restrict: 'E',                     // I prefer this directive to be Element-only
    controller: 'DirectiveController',
    controllerAs: 'ctrl',
    template: '<span>Controller Content</span>',

    /**
     * Add the necessary angular attributes
     *   along with a 'compiled' attribute as tag
     *   such that the element doesn't infinitely compile itself
     *
     * This still allows multiple of the same directive to be
     *   compiled at the same time
     */
    compile: function(tElem, tAttrs){

      // Check if the element has been tagged
      if(! tAttrs['compiled']) {

        // Tag the element so it doesn't compile again
        tElem.attr('compiled', true)

        // Add custom angular attributes
        tElem.attr('ng-show', 'ctrl.shown');

        // Recompile
        return function(scope) { $compile(tElem)(scope); };
      }
    }
  };
});

angular.module('app').controller("DirectiveController", function() {
    var shown = false;
});

Example : http://plnkr.co/edit/wbs0vcFmz15oL2twm6AC?p=preview

Alternatively it would have been possible to attach a watcher to a variable that toggles a class simliar to how ng-show works but that would start duplicating the exact functionality expected of the ng-show attribute.

Community
  • 1
  • 1
Raven
  • 1,453
  • 3
  • 18
  • 29
-1

Not sure if I understand what you want to do correctly, but why don't you just add the attribute to the template like this?

template: '<span ng-show="ctrl.shown">Controller Content</span>'
ogugger
  • 122
  • 6
  • Not really - adding `ng-show` to the template would still leave the directive `my-dir` visible and that's what I would have to avoid. – Raven Feb 11 '16 at 16:41