【问题标题】:Avoid using extra DOM nodes when using nginclude使用 nginclude 时避免使用额外的 DOM 节点
【发布时间】: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>

我需要

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

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


编辑:只是按照 cmets 的要求澄清,我要生成的最终 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:解决方案需要可重复使用。 =)

【问题讨论】:

  • 您能否为变体添加一个示例,现在确定您要的是什么?
  • 对不起。编辑澄清。
  • 您可以使用 ng-include 作为标签&lt;ng-include src='url'&gt;&lt;/ng-include&gt;,而不是发出url 内容以外的标签。
  • 对,但我试图避免触摸与 DOM 树(带有层次结构选择器)紧密耦合的样式表,并且使用 nginclude 会迫使我这样做(因为生成的包含模板变成了ng-include 的子节点)。

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


【解决方案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: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());
              }
          };
      });
      

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

      【讨论】:

      猜你喜欢
      相关资源
      最近更新 更多
      热门标签