Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Load Angular Directive Template Async

Tags:

angularjs

I want to be able to load the directive's template from a promise. e.g.

template: templateRepo.get('myTemplate')

templateRepo.get returns a promise, that when resolved has the content of the template in a string.

Any ideas?

like image 367
Sam Avatar asked Dec 08 '22 10:12

Sam


2 Answers

You could load your html inside your directive apply it to your element and compile.

.directive('myDirective', function ($compile) {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            //Some arbitrary promise.
            fetchHtml()
             .then(function(result){
                 element.html(result);
                 $compile(element.contents())(scope); 
              }, function(error){

              });
        }
    }
});
like image 63
Rob Avatar answered Dec 10 '22 22:12

Rob


This is really interesting question with several answers of different complexity. As others have already suggested, you can put loading image inside directive and when template is loaded it'll be replaced.

Seeing as you want more generic loading indicator solution that should be suitable for other things, I propose to:

  1. Create generic service to control indicator with.
  2. Manually load template inside link function, show indicator on request send and hide on response.

Here's very simplified example you can start with:

<button ng-click="more()">more</button>
<div test="item" ng-repeat="item in items"></div>
.throbber {
    position: absolute;
    top: calc(50% - 16px);
    left: calc(50% - 16px);
}
angular
.module("app", [])
.run(function ($rootScope) {
    $rootScope.items = ["One", "Two"];
    $rootScope.more = function () {
        $rootScope.items.push(Math.random());
    };
})
.factory("throbber", function () {
    var visible = false;
    var throbber = document.createElement("img");
    throbber.src = "http://upload.wikimedia.org/wikipedia/en/2/29/Throbber-Loadinfo-292929-ffffff.gif";
    throbber.classList.add("throbber");
    function show () {
        document.body.appendChild(throbber);
    }
    function hide () {
        document.body.removeChild(throbber);
    }
    return {
        show: show,
        hide: hide
    };
})
.directive("test", function ($templateCache, $timeout, $compile, $q, throbber) {
    var template = "<div>{{text}}</div>";
    var templateUrl = "templateUrl";

    return {
        link: function (scope, el, attr) {
            var tmpl = $templateCache.get(templateUrl);

            if (!tmpl) {
                throbber.show();
                tmpl = $timeout(function () {
                    return template;
                }, 1000);
            }

            $q.when(tmpl).then(function (value) {
                $templateCache.put(templateUrl, value);
                el.html(value);
                $compile(el.contents())(scope);
                throbber.hide();
            });
        },
        scope: {
            text: "=test"
        }
    };
});

JSBin example.

In live code you'll have to replace $timeout with $http.get(templateUrl), I've used the former to illustrate async loading.

How template loading works in my example:

  1. Check if there's our template in $templateCache.
  2. If no, fetch it from URL and show indicator.
  3. Manually put template inside element and [$compile][2] it.
  4. Hide indicator.

If you wonder what $templateCache is, read the docs. AngularJS uses it with templateUrl by default, so I did the same.

Template loading can probably be moved to decorator, but I lack relevant experience here. This would separate concerns even further, since directives don't need to know about indicator, and get rid of boilerplate code.

I've also added ng-repeat and run stuff to demonstrate that template doesn't trigger indicator if it was already loaded.

like image 44
Klaster_1 Avatar answered Dec 10 '22 22:12

Klaster_1