web-dev-qa-db-ja.com

ngincludeを使用するときは、余分なDOMノードを使用しないでください

私はangularアプリをプレーンHTMLデモから作成しているので、ng-includeが余分なDOM要素を使用しないようにする方法に頭を悩ませています。私は働いています。完全に開発された、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ノードを挿入するとどうなるでしょうか。これは、回避しようとしていることです。

私は何を逃していますか?これはベストプラクティスですか、それとももっと良い方法がありますか?


[〜#〜] edit [〜#〜]:コメントで尋ねられたように明確にするために、私が生成しようとしている最終的な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:ソリューションは再利用可能である必要があります。 =)

23
Phil Thomas

他の回答のいくつかは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());
        }
    };
});

(他の答えからカットアンドペースト)

27
Peter V. Mørch

編集:いくつかの調査の後、完全を期すために、いくつかの情報を追加しました。 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>

ただし、1つの落とし穴があります。つまり、テンプレートを動的にすることはできません($ scopeなどのスコープを介して変数を渡すなど) [〜#〜] di [〜#〜] templateUrlではアクセスできません- この問題 を参照)、渡せるのは文字列だけです(上記のhtmlスニペットのように)。この特定の問題を回避するために、このコードはトリックを実行する必要があります(kudos to 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>
20
Phil Thomas

カスタムディレクティブを作成し、templateUrlプロパティを使用してテンプレートにリンクし、replacetrueに設定できます。

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

    }
  }
});

これには、ラッパー要素なし、ラッパースコープなしのテンプレートがそのまま含まれます。

この質問をたまたま訪れた人のために:

angular 1.1.4+以降では、templateURLで関数を使用して動的にすることができます。

この他の答えをチェックしてください ここ

1
ssobus

適切な設定で、Angular.jsによって提供されるものの代わりに実行できる独自のngIncludeディレクティブを定義して、組み込みディレクティブが実行されるのを防ぐことができます。

Angular-built-inディレクティブが実行されないようにするには、ディレクティブの優先度を組み込みディレクティブよりも高く設定することが重要です(ngIncludeの場合は400、terminalプロパティをtrueに設定します。

その後、テンプレートをフェッチし、要素のDOMノードをコンパイルされたテンプレートHTMLで置き換えるポストリンク関数を提供する必要があります。

警告の言葉:これはかなり厳しいです。アプリケーション全体に対してngIncludeの動作を再定義します。したがって、以下のディレクティブをmyAppではなく、自分のディレクティブの1つに設定して、スコープを制限します。アプリケーション全体で使用する場合は、その動作を構成可能にすることができます。のみreplacereplace属性がHTMLに設定されている場合、デフォルトではinnerHtmlの設定にフォールバックします。

また、これはアニメーションではうまく機能しない可能性があります。元のngInclude- directiveのコードははるかに長いため、アプリケーションでアニメーションを使用する場合は、元のコードを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);
0
Johannes Jander