tinyBIGideas
tinyBIGideas

Reputation: 314

AngularJS directive scope and $compile - variable returning blank

Having a little issue with angular that I can't figure out. I suspect it may have something to do with scope, but I'm not 100% exactly what.

Here's my HTML:

<i class="icon-question-sign" popover data-placement="top" data-trigger="hover" data-title="Top 10 clients" data-content-compile="<ul><li ng-repeat='client in user.clients | limitTo: 10'>{{client}}</li></ul>"></i>

Here's my directive:

app.directive('popover', function($timeout, $compile) {
var linker = function (scope, element, attrs) {
    $timeout(function() {
        var content = $compile(element.data('content-compile'))(scope);
        element.popover({
            'html': true,
            'content': content
        });
    }, 200);
}

return {
    restrict: 'A',
    link: linker
}

});

The result correctly repeats li's to the correct length of {{user.clients}} but doesn't render {{client}}. For some reason, that comes through as blank, yet, it has a string value and works when added directly to the HTML and not compiled through the directive. How it currently looks in the DOM:

<ul class="ng-scope"><!-- ngRepeat: client in user.clients | limitTo: 10 --><li ng-repeat="client in user.clients | limitTo: 10" class="ng-scope"></li><li ng-repeat="client in user.clients | limitTo: 10" class="ng-scope"></li><li ng-repeat="client in user.clients | limitTo: 10" class="ng-scope"></li><li ng-repeat="client in user.clients | limitTo: 10" class="ng-scope"></li><li ng-repeat="client in user.clients | limitTo: 10" class="ng-scope"></li><li ng-repeat="client in user.clients | limitTo: 10" class="ng-scope"></li><li ng-repeat="client in user.clients | limitTo: 10" class="ng-scope"></li><li ng-repeat="client in user.clients | limitTo: 10" class="ng-scope"></li><li ng-repeat="client in user.clients | limitTo: 10" class="ng-scope"></li><li ng-repeat="client in user.clients | limitTo: 10" class="ng-scope"></li></ul>

If I replace {{client}} with say {{user.email}} it lists correctly.

Not sure what's up here - I'm probably missing something obvious!

Upvotes: 3

Views: 3957

Answers (2)

tinyBIGideas
tinyBIGideas

Reputation: 314

Seems the answer was a little more straight forward than I expected, changing my directive to:

// popover
app.directive('popover', function($timeout, $compile) {
    var linker = function (scope, element, attrs) {
        var content = $compile(element.data('content-compile'))(scope);
        $timeout(function() {
            element.popover({
                'html': true,
                'content': content
            });
        }, 400);
    }

    return {
        restrict: 'A',
        link: linker
    }

});

The only difference is that I doubled the $timeout period, and moved the compiled variable outside of it. Ensuring the data is compiled before it's called.

Not 100% sure if this is the right way to do it, but under testing its returning correctly on all accounts. Although, if you have a similar problem - take this solution with a pinch of salt.

Upvotes: 0

The key to your problem is it's cause: {{client}} in data-content-compile gets compiled before directive kicks in, so client would be undefined in controller context. Currently, the string you send into $compile is:

<ul><li ng-repeat='client in user.clients | limitTo: 10'></li></ul>

In order to prevent this, template has to be sent into directive some other way. Here are some:

  1. Store template in controller as as string. Rather ugly, in my opinion.
  2. Store template in template and retrieve it from $templateCache.

I did an example using second approach:

<div ng-controller="ctrl">
    <script type="text/ng-template" id="client.html">
        <div><ul><li ng-repeat='client in user.clients'>{{client.name}}</li></ul></div>
    </script>
    <i
        popover
        class="glyphicon glyphicon-user"
        data-placement="bottom"
        data-trigger="hover"
        data-title="Top 10 clients"
        data-content-template="client.html"
    ></i>
</div>
angular.module("app", [])
.controller("ctrl", function($scope, User) {
    $scope.user = User;
})
.directive("popover", function($compile, $timeout, $templateCache) {
    return function(scope, el, attr) {
        $timeout(function() {
            el
            .popover({
                html: true,
                content: $compile($templateCache.get(attr.contentTemplate))(scope)
            })
            .popover("show")
        });    
    };
})
.value("User", {
    clients: [
        { name: "John", age: 22},
        { name: "Ann", age: 13},
        { name: "Maria", age: 62},
        { name: "Ivan", age: 44}
    ]
});

Upvotes: 2

Related Questions