Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing a parent directive attribute to a child directive attribute

I'm creating directives for a library that customers can use. I need to let the customers create their own templates for a directive and pass the absolute url value of that template into my directives. One of my directives will have another custom directive inside of it, and it's template will be figured out based upon the value of one of the parent directive's attributes. Here's an example:

<parent-dir menu-template="this.html" item-template="that.html"></parent-dir>

I have a template for this directive that looks like this:

<ul style="list: none" ng-repeat="item in menu">
    <child-dir template="{{itemTemplate}}"></child-dir>
</ul>

My directives look like this:

angular.module('myApp')
.directive('parentDir', function () {        
    return {
        restrict: 'E',
        scope: {
            menuTemplate: '@',
            itemTemplate: '@',
            menuType: '@',
            menuName: '@',
            menuId: '@',
        },
        templateUrl: function (element, attrs) {
            alert('Scope: ' + attrs.menuTemplate);
            return attrs.menuTemplate;
        },
        controller: function ($scope, $element, $attrs) {
            $scope.defaultSubmit = false;
            alert('Menu: '+$attrs.menuTemplate);
            alert('Item: ' + $attrs.itemTemplate);
            $scope.itemTemplate = $attrs.itemTemplate;
            if ($attrs.$attr.hasOwnProperty('defaultSubmit')) {
                alert('It does');
                $scope.defaultSubmit = true;   
            }               
        }
    };
})
.directive('childDir', function () {
    return {
        restrict: 'E',
        require: '^parentDir',
        templateUrl: function (element, attrs) {
            alert('Item Template: ' + attrs.template);
            return attrs.template;
        },
        controller: function ($scope, $element, $attrs) {                
            $scope.job;
            alert('Under job: ' + $scope.itemTemplate);
        }
    };
});

I'm not showing all of the code but this is the main piece of my problem. When I run this, I keep getting undefined for the template on the childDir.

What is the best practice in perpetuating the value of itemTemplate from the parentDir so that the childDir can use it as it's template?

like image 295
Michael Sheely Avatar asked Aug 04 '15 16:08

Michael Sheely


1 Answers

The reason you're running into problems is because the function that generates the templateUrl is running before a scope has been assigned to your directive - something that has to be done before interpolated data can be replaced.

In other words: at the point that the templateUrl function runs, the value of the template attribute is still "{{itemTemplate}}". This will remain the case until the directive's link (preLink to be precise) function runs.

I created a plunker to demonstrate the point here. Be sure to open the console. You'll see that templateUrl runs before both the parent and child linking functions.

So what do you do instead?

Fortunately, angular provides a $templateRequest service which allows you to request the template in the same way it would using templateUrl (it also uses the $templateCache which is handy).

put this code in your link function:

    $templateRequest(attrs.template)
    .then(function (tplString){
      // compile the template then link the result with the scope.
      contents = $compile(tplString)(scope);
      // Insert the compiled, linked element into the DOM
      elem.append(contents);
    })

You can then remove any reference to the template in the directive definition object, and this will safely run once the attribute has been interpolated.

like image 80
Ed_ Avatar answered Nov 15 '22 06:11

Ed_