Replace ng-include node with template?

Angularjs

Angularjs Problem Overview


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>

Angularjs Solutions


Solution 1 - Angularjs

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.

Solution 2 - Angularjs

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>

Solution 3 - Angularjs

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.

Solution 4 - Angularjs

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>

Solution 5 - Angularjs

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

Solution 6 - Angularjs

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

Solution 7 - Angularjs

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSunnySydeUpView Question on Stackoverflow
Solution 1 - AngularjsBrady IsomView Answer on Stackoverflow
Solution 2 - AngularjsSunnySydeUpView Answer on Stackoverflow
Solution 3 - AngularjsxeorView Answer on Stackoverflow
Solution 4 - AngularjsDaniel EganView Answer on Stackoverflow
Solution 5 - AngularjsSai DubbakaView Answer on Stackoverflow
Solution 6 - AngularjsedrianView Answer on Stackoverflow
Solution 7 - AngularjsMasadowView Answer on Stackoverflow