AngularJS指令动态模板

67

我正在尝试根据作用域值制作不同模板的指令。

这是我目前所做的,但我不知道为什么它不起作用:http://jsbin.com/mibeyotu/1/edit

HTML元素:

<data-type content-attr="test1"></data-type>

指令:

var app = angular.module('myApp', []);

app.directive('dataType', function ($compile) {

    var testTemplate1 = '<h1>Test1</h1>';
    var testTemplate2 = '<h1>Test2</h1>';
    var testTemplate3 = '<h1>Test3</h1>';

    var getTemplate = function(contentType){

        var template = '';

        switch(contentType){
            case 'test1':
                template = testTemplate1;
                break;
            case 'test2':
                template = testTemplate2;
                break;
            case 'test3':
                template = testTemplate3;
                break;
        }

        return template;
    }; 

    var linker = function(scope, element, attrs){
        element.html(getTemplate(scope.content)).show();
        $compile(element.contents())(scope);
    };

    return {
        restrict: "E",
        replace: true,
        link: linker,
        scope: {
            content:'='
        }
    };
});

1
似乎很多人需要/喜欢这个:http://onehungrymind.com/angularjs-dynamic-templates/,它与AngularJS动态模板有关。 - dalvarezmartinez1
4个回答

119
您可以将指令定义对象的template属性设置为一个函数,该函数将返回您的动态模板:
restrict: "E",
replace: true,
template: function(tElement, tAttrs) {
    return getTemplate(tAttrs.content);
}

请注意,此时您无法访问作用域,但可以通过tAttrs访问属性。

现在您的模板正在编译阶段之前确定,您无需手动编译它。


27
请注意,templateUrl也可以是一个函数。 - Val Redchenko
tAttrs.content并不会返回实际的内容值,而只是HTML模板值。 - Sotiris Zegiannis

22

你也可以像这样非常简单地做:

appDirectives.directive('contextualMenu', function($state) {
    return {
      restrict: 'E',
      replace: true,
      templateUrl: function(){
        var tpl = $state.current.name;
        return '/app/templates/contextual-menu/'+tpl+'.html';
      }
    };
});

你会如何将$state传递给指令? - tftd
1
如果您正在寻找一种简单的方法来根据路由控制“template”,那么这是最佳答案。 - DeBraid
@tftd 像这样: angular.module('myModule', ['ui.router']) .directive('myDirective', ['$state', function($state){ return { templateUrl: 'tpl.html' }; }]); })(window.angular);``` - eloone
templateUrl被指定为一个函数时,该函数接收两个参数,第一个是元素,第二个是作用域。因此,要获取隔离作用域更容易使用回调中的作用域:{templateUrl: function($el, $scope) {return '..'+$scope.name+'..'}} - Tiberiu C.
1
@Tiberiu C. 第二个参数不是$scope,而是属性。 - Chololoco

16

1) 您正在将内容作为属性传递到您的HTML中。请尝试以下方法:

element.html(getTemplate(attrs.content)).show();

不是:

element.html(getTemplate(scope.content)).show();

2) 指令的数据部分正在编译,因此您应该使用其他内容。例如,使用datan-type而不是data-type。

这里是链接:

http://jsbin.com/mibeyotu/6/edit


只有一个问题,如果我正在使用ng-repeat,例如:ng-repeat="p in people",我无法像这样获取值:content="p.name"。因为这个指令在ng-repeat循环内部。 - Jack
当调用“show”时,我遇到了错误。如果没有它,就能正常工作。不确定是遗留问题还是其他什么原因。 - sasklacz
@Slaven Tomac 我正在尝试动态添加表单元素。但是当使用ng-repeat和ng-options动态添加项目时,它不会显示重复的项目。只有静态HTML。这就是我的意思。http://plnkr.co/edit/JOzTWB6tuyilCJ8Rj37Q 代码有问题吗?或者ng-repeat也可以在模板中编译。 - Gary

11

如果你需要基于$scope变量加载模板,可以使用ng-include

.directive('profile', function() {
  return {
    template: '<ng-include src="getTemplateUrl()"/>',
    scope: {
      user: '=data'
    },
    restrict: 'E',
    controller: function($scope) {
      //function used on the ng-include to resolve the template
      $scope.getTemplateUrl = function() {
        //basic handling
        if ($scope.user.type == 'twitter') {
          return 'twitter.tpl.html';
        }
        if ($scope.user.type == 'facebook') {
          return 'facebook.tpl.html';
        }
      }
    }
  };
});

参考文献: https://coderwall.com/p/onjxng/angular-directives-using-a-dynamic-template


嗨, 当我实现与您相同的代码时,我遇到了错误: angular.js:13708 错误:[$compile:tpload] 无法加载模板:<ng-include src="getTemplateUrl()"/>(HTTP状态:404未找到) 您能帮我解决这个问题吗? - Shikha thakur
你能提供指令控制器代码吗?我猜测指令的作用域没有 getTemplateUrl 函数,浏览器试图加载未定义的模板。 - Karolis.sh
1
无论如何,如果您的指令正在使用“transclude”,则此方法不起作用;但是还是一个不错的方法 ;) - Nicu Surdu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接