使用ng include时避免使用额外的DOM节点

时间:2013-07-11 09:06:59

标签: angularjs angularjs-ng-repeat angularjs-ng-include

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

这是实际的代码:

<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>

我需要&lt; section&gt; 作为重复元素,但有自己的逻辑和不同的内容。内容和重复次数都取决于业务逻辑。如您所见,将ng-controller和ng-repeat放在&lt; section&gt; 元素上将不起作用。但是,插入一个新的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 :解决方案需要重复使用。 =)

5 个答案:

答案 0 :(得分:25)

其他一些答案建议使用replace:true,但请注意,模板中的replace:truemarked for deprecation

相反,在an answer to a similar question中,我们找到了另一种选择:它允许您编写:

<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());
        }
    };
});

(来自其他答案的cut'n'paste)

答案 1 :(得分: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中访问 - 请参阅{{3只有一个字符串可以传递(就像上面的html片段一样)。为了绕过这个特定的问题,这段代码应该可以解决问题(感谢this issue):

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>

答案 2 :(得分:6)

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

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

    }
  }
});

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

答案 3 :(得分:1)

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

从角度1.1.4+开始,您可以使用templateURL中的函数使其动态化。

查看其他答案here

答案 4 :(得分:0)

使用正确的设置,您可以定义自己的ngInclude指令,而不是Angular.js提供的指令,并防止内置指令执行。

防止Angular-built-in指令执行对于将指令的优先级设置为高于内置指令的优先级(400 ngInclude并将terminal属性设置为true

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

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

另外:这可能不适合动画效果。原始ngInclude - 指令的代码更长,因此如果您在应用程序中使用动画,请使用原始代码并将`$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);