1

I though that this might be a duplicate of this question, except that I'm not using primitives in my code. I have a directive that I'm using to place inline svgs dynamically into my view. It works like so:

app.directive('icon', ['$timeout', function($timeout) {
  return {
    restrict: "E",
    scope: {
      href:"@",
      title: "@"
    },
    replace: true,
    templateUrl: $timeout(function(elm,attrs) {
      var definition = {
        add: "add133.svg",
        alert: "warning30.svg",
        attachment: "attachment13.svg",
        call: "auricular6.svg",
        calendar: "calendar5.svg",
        notify: "church2.svg",
        compass: "circular14.svg",
        cloud: "cloud127.svg",
        addUser: "create1.svg",
        delete: "delete30.svg",
        trash: "delete48.svg",
        servicedesk: "edit26.svg",
        email: "email20.svg",
        star: "favourites7.svg",
        key: "key162.svg",
        lock: "lock27.svg",
        search: "magnifier12.svg",
        menu: "menu48.svg",
        print: "printer70.svg",
        settings: "settings21.svg",
        share: "share12.svg",
        customer: "user91.svg"
      };
      return 'svg/' + definition[attrs.name];
    },30000)
  }
}]);

Eventually I'll update the code so that I'm using a service to get the list of definitions from a json file, but I'm only testing this for now. This works as expected. But when I repeat over it the value suddenly becomes undefined. See my view below:

<div ng-repeat="link in config.mainlinks" ng-class="{true: 'active', false: 'dormant'}[config.urlCheck('{{link.name}}')]" sys-stack="2">
    <a href="{{link.href}}" title="{{link.title}}">
        <icon name="{{link.icon}}"></icon>
    </a>
</div> 

As you might infer from my code, I have a model defined in my controller called config and a property of that model is called mainlinks. Mainlinks is an object meant to provide the attributes needed for this block of code to work, it looks like this

mainlinks: {
        dash: {
          name: "dashbaord",
          href: "#dashboard",
          title: "Dashboard",
          icon: "compass"
        }

When it renders the DOM looks like this:

<div ng-repeat="link in config.mainlinks" ng-class="{true: 'active', false: 'dormant'}[config.urlCheck('calendar')]" sys-stack="2" class="ng-scope ng-isolate-scope z2 dormant">
    <a href="#calendar" title="Calendar">
        <icon name="{{link.icon}}"></icon>
    </a>
</div>

But something like this works fine

<span ng-repeat="link in config.mainlinks">
    {{link.icon}}
</span>

My guess is because my test span and the other working portions of my div block are all on the same scope, that of the controller housing them. But icon has its own isolate scope and on that scope, link.icon has no meaning. How can I get around this limitation? Or am I completely off base?

Community
  • 1
  • 1
richbai90
  • 4,994
  • 4
  • 50
  • 85
  • That is because your attibute value name would not have been bound by the time templateURL function is called. You will just get the text "{{link.icon}}" as is. SO template url returns invalid url and it fails to load the directive – PSL Aug 29 '14 at 20:24
  • Can you offer any suggestions on how to fix it, I updated my code, adding the $timeout service hoping to delay the execution, but it didn't seem to have any effect. – richbai90 Aug 29 '14 at 20:39
  • Problem is you cannot really do it in the templateURl if you have static text being loaded in the attributes you can access it, but in this case, you may have to do something tricky.. Or do it in the link function and replace the svg and compile it. – PSL Aug 29 '14 at 20:41
  • Why dont you make the entire thing a directive and let the directive take mainlinks and render it, – PSL Aug 29 '14 at 20:57
  • That is probably the best solution. Submit an answer so you can get credit for helping me work through this. Thanks – richbai90 Aug 29 '14 at 21:00

1 Answers1

1

The issue is the you can only do so much in the templateUrl function, you atleast need to have static text in the attribute that you can read inside the template url. If you use interpolated value or scope binding in the attribute it wont be available during the time templateurl is evaluated since the compilation is deferred. since you have everything inside the ng-repeat, your entire ng-repeat and the directive inside will be compile only after the templateurl of your directive is rendered. So some options could be instead of template url just place some template and replace it during the linking phase (or postLink phase) of the directive reading the attribute value (since it would be available at that time). Or just wrap the functionality into a single directive and bind the mainlinks to it.

Something like this:-

markup:-

<icons mainlinks="config.mainlinks"></icons>

directive:-

.directive('icons', [function() {
  return {
    restrict: "E",
    scope: {
      mainlinks: "="
    },
    templateUrl:'links.html', 
    replace: true,
    link:function(scope, elm, attrs){
    .....

    }
  }
});

Move config.urlCheck and definition could be moved to a service (say svg service) that can be shared (if that is something you use often in your app) and inject it in your directive. Also note that mainlinks seems to be dictionary so your ng-repeat should be ng-repeat="(k,link) in mainlinks"

Plnkr

PSL
  • 123,204
  • 21
  • 253
  • 243