加载角度指令模板异步

时间:2014-01-17 21:29:16

标签: angularjs

我希望能够从promise加载指令的模板。 e.g。

template: templateRepo.get('myTemplate')

templateRepo.get返回一个promise,当解析时,该字符串中包含模板的内容。

有什么想法吗?

4 个答案:

答案 0 :(得分:13)

你可以在你的指令中加载你的html,将它应用到你的元素并编译。

.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){

              });
        }
    }
});

答案 1 :(得分:2)

这是一个非常有趣的问题,有几个不同复杂性的答案。正如其他人已经建议的那样,您可以将加载图像放在指令中,当模板加载时,它将被替换。

看到你想要更适合其他事情的通用加载指标解决方案,我建议:

  1. 使用。
  2. 创建控制指标的通用服务
  3. 在链接功能中手动加载模板,在请求时显示指示器发送并隐藏响应。
  4. 这是一个非常简单的示例,您可以从以下开始:

    <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

    在实时代码中,您必须将$timeout替换为$http.get(templateUrl),我使用前者来说明异步加载。

    模板加载如何在我的示例中起作用:

    1. 检查$templateCache中是否有我们的模板。
    2. 如果不是,请从URL中获取并显示指示符。
    3. 手动将模板放在元素内,[$compile][2]
    4. 隐藏指示器。
    5. 如果您想知道$templateCache是什么,read the docs。 AngularJS默认使用templateUrl,所以我做了同样的事情。

      模板加载可能会移动到装饰器,但我缺乏相关的经验。这将进一步分离关注点,因为指令不需要知道指标,并摆脱样板代码。

      我还添加了ng-repeatrun内容,以证明模板如果已经加载则不会触发指示符。

答案 2 :(得分:0)

我要做的是在我的指令中添加ng-include以有选择地加载我需要的内容

从角度页面检查此演示。它可能有所帮助:

http://docs.angularjs.org/api/ng.directive:ngInclude

答案 3 :(得分:-1)

````
/**
 * async load template 
 * eg : 
 * <div class="ui-header">
 *     {{data.name}}
 *     <ng-transclude></ng-transclude>
 * </div>
 */
Spa.Service.factory("RequireTpl", [
    '$q',
    '$templateCache',
    'DataRequest',
    'TplConfig',
 function(
    $q,
    $templateCache,
    DataRequest,
    TplConfig
) {
    function getTemplate(tplName) {
        var name = TplConfig[tplName];
        var tpl = "";
        if(!name) {
            return $q.reject(tpl);
        } else {
            tpl = $templateCache.get(name) || "";
        }
        if(!!tpl) {
            return $q.resolve(tpl);
        }
        //加载还未获得的模板
        return new $q(function(resolve, reject) {
            DataRequest.get({
                url : "/template/",
                action : "components",
                responseType : "text",
                components : name
            }).success(function(tpl) {
                $templateCache.put(name, tpl);
                resolve(tpl);
            }).error(function() {
                reject(null);
            });
        });
    }
    return getTemplate;
}]);
/**
 * usage:
 * <component template="table" data="info">
 *     <span>{{info.name}}{{name}}</span>
 * </component>
 */
Spa.Directive.directive("component", [
    "$compile",
    "RequireTpl",
function(
    $compile,
    RequireTpl
) {
    var directive = {
        restrict : 'E',
        scope : {
            data : '='
        },
        transclude : true,
        link: function ($scope, element, attrs, $controller, $transclude) {
            var linkFn = $compile(element.contents());
            element.empty();
            var tpl = attrs.template || "";
            RequireTpl(tpl)
            .then(function(rs) {
                var tplElem = angular.element(rs);
                element.replaceWith(tplElem);
                $transclude(function(clone, transcludedScope) {
                    if(clone.length) {
                        tplElem.find("ng-transclude").replaceWith(clone);
                        linkFn($scope);
                    } else {
                        transcludedScope.$destroy()
                    }
                    $compile(tplElem.contents())($scope);
                }, null, "");
            })
            .catch(function() {
                element.remove();
                console.log("%c component tpl isn't exist : " + tpl, "color:red")
            });
        }
    };
    return directive;
}]);
````