23

我正在努力思考如何让 ng-include 不使用额外的 DOM 元素,因为我正在从纯 HTML 演示构建角度应用程序。我正在使用带有完全开发、紧密 DOM 耦合的 CSS(由 SASS 构建)的非常纤薄的 HTML,并且我想不惜一切代价避免重构。

这是实际的代码:

<div id="wrapper">
    <header
        ng-controller="HeaderController"
        data-ng-class="headerType"
        data-ng-include="'/templates/base/header.html'">
    </header>
    <section
        ng-controller="SubheaderController"
        data-ng-class="subheaderClass"
        ng-repeat="subheader in subheaders"
        data-ng-include="'/templates/base/subheader.html'">
    </section>
    <div
        class="main"
        data-ng-class="mainClass"
        data-ng-view>
    </div>
</div>

我需要<section>是一个重复元素,但有自己的逻辑和不同的内容。内容和重复次数都取决于业务逻辑。如您所见,将 ng-controller 和 ng-repeat 放在<section>元素上是行不通的。然而,插入一个新的 DOM 节点,这是我试图避免的。

我错过了什么?这是最佳做法还是有更好的方法?


编辑:只是为了澄清评论中的要求,我试图生成的最终 HTML 将是:

<div id="wrapper">
    <header>...</header>
    <section class="submenuX">
        some content from controller A and template B (e.g. <ul>...</ul>)
    </section>
    <section class="submenuY">
        different content from same controller A and template B (e.g. <div>...</div>)
    </section>
    <section class="submenuZ">
        ... (number of repetitions is defined in controller A e.g. through some service)
    </section>

    <div>...</div>
</div>

我想使用相同的模板 B (subheader.html) 的原因是为了代码整洁。我认为 subheader.html 有某种 ng-switch 以返回动态内容。

但基本上,潜在的问题是:有没有办法透明地包含模板的内容,而不使用 DOM 节点?


EDIT2:解决方案需要可重复使用。=)

4

5 回答 5

27

其他一些答案建议replace:true,但请记住,replace:true在模板中标记为弃用

相反,在对类似问题的回答中,我们找到了另一种选择:它允许您编写:

<div ng-include src="dynamicTemplatePath" include-replace></div>

自定义指令:

app.directive('includeReplace', function () {
    return {
        require: 'ngInclude',
        restrict: 'A', /* optional */
        link: function (scope, el, attrs) {
            el.replaceWith(el.children());
        }
    };
});

(从另一个答案中剪切'n'paste)

于 2014-07-23T21:37:21.200 回答
20

编辑:经过一些研究并为了完整起见,我添加了一些信息。自 1.1.4 起,以下工作:

app.directive('include',
    function () {
        return {
            replace: true,
            restrict: 'A',
            templateUrl: function (element, attr) {
                return attr.pfInclude;
            }
        };
    }
);

用法:

<div include="'path/to/my/template.html'"></div>

但是,有一个问题:模板不能是动态的(例如,通过范围传递变量,因为 $scope 或任何与此相关的DI在 templateUrl 中不可访问 - 请参阅此问题),只能传递一个字符串(就像上面的 html 片段一样)。为了绕过这个特定问题,这段代码应该可以解决问题(对这个 plunker 表示赞赏):

app.directive("include", function ($http, $templateCache, $compile) {
    return {
        restrict: 'A',
        link: function (scope, element, attributes) {
            var templateUrl = scope.$eval(attributes.include);
            $http.get(templateUrl, {cache: $templateCache}).success(
                function (tplContent) {
                    element.replaceWith($compile(tplContent.data)(scope));
                }
            );
        }
    };
});

用法:

<div include="myTplVariable"></div>
于 2013-07-11T16:20:35.340 回答
6

您可以创建自定义指令,使用属性链接到模板templateUrl,并设置replacetrue

app.directive('myDirective', function() {
  return {
    templateUrl: 'url/to/template',
    replace: true,
    link: function(scope, elem, attrs) {

    }
  }
});

这将包括模板原样,没有任何包装元素,没有任何包装范围。

于 2013-07-11T12:39:49.023 回答
1

对于碰巧访问此问题的任何人:

从 Angular 1.1.4+ 开始,您可以在 templateURL 中使用一个函数来使其动态化。

在这里查看其他答案

于 2013-08-14T22:58:46.140 回答
0

通过正确的设置,您可以定义自己的ngInclude指令来运行,而不是 Angular.js 提供的指令,并防止内置指令永远执行。

为了防止 Angular 内置指令执行,将指令的优先级设置为高于内置指令的优先级(400ngInclude并将terminal属性设置为true.

之后,您需要提供一个 post-link 函数来获取模板并将元素的 DOM 节点替换为已编译的模板 HTML。

一句警告:这是相当严厉的,你重新定义了ngInclude整个应用程序的行为。因此,我将下面的指令设置在我自己的指令之一而不是在myApp我自己的指令中以限制其范围。如果您想在应用程序范围内使用它,您可能希望使其行为可配置,例如,仅在 HTML 中设置了属性时才替换元素,replace并且默认回退到设置 innerHtml。

另外:这可能不适用于动画。原始指令的代码要ngInclude长得多,因此如果您在应用程序中使用动画,请 c&p 原始代码并将其`$element.replaceWith()插入其中。

var includeDirective = ['$http', '$templateCache', '$sce', '$compile',
                        function($http, $templateCache, $sce, $compile) {
    return {
        restrict: 'ECA',
        priority: 600,
        terminal: true,
        link: function(scope, $element, $attr) {
            scope.$watch($sce.parseAsResourceUrl($attr.src), function ngIncludeWatchAction(src) {    
                if (src) {
                    $http.get(src, {cache: $templateCache}).success(function(response) {
                        var e =$compile(response)(scope);
                        $element.replaceWith(e);
                    });       
                }
            }); 
        }
    };
}];

myApp.directive('ngInclude', includeDirective);
于 2014-02-24T13:15:38.187 回答