使用 nginclude 时避免使用额外的 DOM 节点

Posted

技术标签:

【中文标题】使用 nginclude 时避免使用额外的 DOM 节点【英文标题】:Avoid using extra DOM nodes when using nginclude 【发布时间】:2013-07-09 11:42:14 【问题描述】:

我正在努力思考如何让 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>

我需要

【参考方案1】:

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

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

    
  
);

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

【讨论】:

有没有办法使 templateUrl 动态化,以便我可以重用指令? 暂时没有。至少不是开箱即用。我听说过一些我不推荐的解决方法。 @filiptc 我猜你指的是这个:github.com/angular/angular.js/issues/1039 似乎可以在 angularjs >= 1.1.4 据我所知,“1.1.4+ 中的可能”部分指的是templateUrl 配置中的templateUrl。但也许您可以在该线程之后找到一些有趣的东西。 @filiptc【参考方案2】:

编辑:经过一些研究并为了完整起见,我添加了一些信息。自 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 中无法访问 - 请参阅this issue),只能传递一个字符串(就像上面的 html sn-p 一样)。为了绕过这个特定问题,这段代码应该可以解决问题(感谢this 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>

【讨论】:

我不得不争辩说,我的解决方案对于实际问题仍然更好。如果这对你有用,那很好。 你的回答非常好,让我朝着正确的方向前进(赞!)。我只是绝对需要这个功能可以在我的整个模板结构中重复使用。我不能为每个包含声明一个新指令。 ...这完全是另一回事(没有提到——或者我没读好?):) 既然你基本上用这个重新发明了ng-include,请检查它的来源如果您在此过程中遇到问题。干杯:) 非常好的(第二个例子)解决方案!它甚至适用于 元素,并且它们不会被扔出桌子:)! 已编辑:在第二个示例中将 .data 添加到 tplContenttplContent 是一个 HTTP 响应对象,而不是字符串,因此 $compile 会阻塞它。请在发布前测试代码! 【参考方案3】:

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

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

查看其他答案here

【讨论】:

【参考方案4】:

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

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

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

警告:这是相当严厉的,您为整个应用程序重新定义了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);

【讨论】:

【参考方案5】:

其他一些答案建议replace:true,但请记住,模板中的replace:true 是marked 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());
        
    ;
);

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

【讨论】:

不适用于动态内容(ng-repeat,...)。请更新并使用这个对我有用的首选答案:***.com/a/33508032/704246

以上是关于使用 nginclude 时避免使用额外的 DOM 节点的主要内容,如果未能解决你的问题,请参考以下文章

AngularJS:ngInclude vs 指令

如何在 AngularJS 中为 ngInclude 指令指定模型?

使用带有文本的 glyphicon 时如何避免额外的空间?

ngInclude 在 Android 的 PhoneGap 构建中失败

使用 react-router-dom 时如何避免“功能组件不能被赋予 refs”?

在PreferenceAcitity中使用Fragement时避免额外的Left和RightPadding