85

Kinda new to angular. Is it possible to replace the ng-include node with the contents of the included template? For example, with:

<div ng-app>
    <script type="text/ng-template" id="test.html">
        <p>Test</p>
    </script>
    <div ng-include src="'test.html'"></div>
</div>

The generated html is:

<div ng-app>
    <script type="text/ng-template" id="test.html">
        <p>Test</p>
    </script>
    <div ng-include src="'test.html'">
        <span class="ng-scope"> </span>
        <p>Test</p>
        <span class="ng-scope"> </span>
    </div>
</div>

But what I want is:

<div ng-app>
    <script type="text/ng-template" id="test.html">
        <p>Test</p>
    </script>
    <p>Test</p>
</div>
3
  • 1
    remove single quotes from 'test.html' to use template rather than url Commented May 11, 2013 at 18:58
  • 1
    reading the comments of the doc for ng-include, it seems it's surround the string with single quotes for template and without for url. Also, related stackoverflow question Commented May 11, 2013 at 22:22
  • you are reading docs and post you linked to backwards Commented May 12, 2013 at 5:06

7 Answers 7

134

I had this same issue and still wanted the features of ng-include to include a dynamic template. I was building a dynamic Bootstrap toolbar and I needed the cleaner markup for the CSS styles to be applied properly.

Here is the solution that I came up with for those who are interested:

HTML:

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

Custom Directive:

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

If this solution were used in the example above, setting scope.dynamicTemplatePath to 'test.html' would result in the desired markup.

Sign up to request clarification or add additional context in comments.

11 Comments

Requires angular v1.2+
This works for angular 1.2.5+. For 1.2.4, if you have one ng-include that ng-includes another, it fails. I'm guessing because of #5247, but I'm not sure. See Changelog for yourself. Here is a Plunkr demonstrating this problem with 1.2.4 (change to angular 1.2.5 and see it works! :-)
Take a note that such DOM manipulation pretty hacky.There is problem if root element of included template uses something like ng-repeat. It will not able to insert results in DOM.
Please see my answer to this, this will fail because the prelink function will already have run in child elements.
|
28

So thanks to @user1737909, I've realized that ng-include is not the way to go. Directives are the better approach and more explicit.

var App = angular.module('app', []);

App.directive('blah', function() {
    return {
        replace: true,
        restrict: 'E',  
        templateUrl: "test.html"
    };
});

In html:

<blah></blah>

3 Comments

thanks! was looking for an ng-include solution but this helped me realize directives are better
Keep in mind that replace:true in templates is marked for deprecation. I'd avoid using this solution due to the deprecation status.
@PeterV.Mørch Thanks. For those interested, this is the commit: github.com/angular/angular.js/commit/…. It seems like it was deprecated because of it's complexity (and perhaps other reasons).
15

I had the same problem, my 3rd party css stylesheet didn't like the extra DOM-element.

My solution was super-simple. Just move the ng-include 1 up. So instead of

<md-sidenav flex class="md-whiteframe-z3" md-component-id="left" md-is-locked-open="$media('gt-md')">
  <div ng-include="myService.template"></span>
</md-sidenav>

I simply did:

<md-sidenav flex class="md-whiteframe-z3" md-component-id="left" md-is-locked-open="$media('gt-md')" ng-include="myService.template">
</md-sidenav>

I bet this will work in most situations, even tho it technically isn't what the question is asking.

Comments

10

Another alternative is to write your own simple replace/include directive e.g.

    .directive('myReplace', function () {
               return {
                   replace: true,
                   restrict: 'A',
                   templateUrl: function (iElement, iAttrs) {
                       if (!iAttrs.myReplace) throw new Error("my-replace: template url must be provided");
                       return iAttrs.myReplace;
                   }
               };
           });

This would then be used as follows:

<div my-replace="test.html"></div>

Comments

9

This is the correct way of replacing the children

angular.module('common').directive('includeReplace', function () {
    return {
        require: 'ngInclude',
        restrict: 'A',
        compile: function (tElement, tAttrs) {
            tElement.replaceWith(tElement.children());
            return {
                post : angular.noop
            };
        }
    };
});

2 Comments

My included partial html got some ng-repeat, and this is the only answer resolving them ! Thanks a lot.
I had to move the function content from compile to link, because my element was empty during the compile stage.
3

Following directive extends ng-include native directive functionality.

It adds an event listener to replace the original element when content is ready and loaded.

Use it in the original way, just add "replace" attribute:

<ng-include src="'src.html'" replace></ng-include>

or with attribute notation:

<div ng-include="'src.html'" replace></div>

Here is the directive (remember to include 'include-replace' module as dependency):

angular.module('include-replace', []).directive('ngInclude', function () {
    return {
        priority: 1000,
        link: function($scope, $element, $attrs){

            if($attrs.replace !== undefined){
                var src = $scope.$eval($attrs.ngInclude || $attrs.src);

                var unbind = $scope.$on('$includeContentLoaded', function($event, loaded_src){
                    if(src === loaded_src){
                        $element.next().replaceWith($element.next().children());
                        unbind();
                    };
                });
            }
        }
    };
});

Comments

2

I would go with a safer solution than the one provided by @Brady Isom.

I prefer to rely on the onload option given by ng-include to make sure the template is loaded before trying to remove it.

.directive('foo', [function () {
    return {
        restrict: 'E', //Or whatever you need
        scope: true,
        template: '<ng-include src="someTemplate.html" onload="replace()"></ng-include>',
        link: function (scope, elem) {
            scope.replace = function () {
                elem.replaceWith(elem.children());
            };
        }
    };
}])

No need for a second directive since everything is handled within the first one.

1 Comment

beware that with angular 1.5 the first child in the directive element is a comment. So I made sure I get the ng-include element and then replace it with its children: let ngInclude = angular.element( element[ 0 ].querySelector( 'ng-include' ) ); ngInclude.replaceWith( ngInclude.children() );

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.