Replace ng-include node with template? - angularjs

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>

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.

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>

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.

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>

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

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

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.

Related

Ng-Transclude not working for ng-include

I have created a ng-transclude sample which is not working when I use ng-include inside directive template property.Below are the code I have tried
angular.module("app",[]);
angular.module("app").controller('appController', function ($scope) {
$scope.message="Message from controller";
});
angular.module("app").directive('myFrame', function () {
return {
restrict: 'E',
template : '<div ng-include=\"getTemplateUrl()\"></div>',
controller:function($scope){
$scope.hidden=false;
$scope.close=function(){
$scope.hidden=true;
}
$scope.getTemplateUrl=function(){
//dynamic url
return "frame.html";
}
$scope.message="message from directive";
},
transclude:true,
}
});
angular.module("app").directive('mychildFrame', function () {
return {
restrict: 'E',
templateUrl:"childframe.html",
controller:function($scope){
},
}
});
childFrame.html
<h2>I am from Child</h2>
<div></div>
frame.html
<div class="well" style="width:350px;" ng-hide="hidden">
<div style="float:right;margin-top:-15px">
<i class="glyphicon glyphicon-remove" ng-click="close()" style="cursor:pointer"></i>
</div>
<div ng-transclude>
/*frame content goes here*/
</div>
</div>
index.html
<my-frame>
<mychild-frame></mychild-frame>
</my-frame>
https://plnkr.co/edit/O58voI?p=preview
When I change to the property template to templateUrl="frame.html" it's working fine. But the problem is, this HTML page inside the template is dynamic. So I end up with ng-include.
Could you please provide any possible workaround?
When using the templateUrl property you can also pass it a function to dynamically load a template.
There's no need to put the function in a controller, especially since it doesn't really belong there anyway: the controller is supposed to contain view logic, not a function to load the view itself.
I added the function in the templateUrl instead in your plunkr:
templateUrl : function() {
return 'frame.html';
}
https://plnkr.co/edit/HQHI9hkTojkZFK2Gjxfw?p=preview
As you can see this gives you the desired behavior

create dynamic template in angularjs

Hi I want to create angular template dynamically like below:
Index.html:
<div ui-view></div>
<ul class="nav-links">
<li ng-repeat="item in items">
<a ui-sref="{{item.id}}"">{{item.name}}</a>
</li>
</ul>
<script ng-repeat="item in items" type="text/ng-template" id="{{item.id}}.html">
<h1>{{item.name}}</h1>
</script>
How could I do that? I searched on web and found that we can include template dynamically, but I did not find any place where it is specified that how to create template itself dynamically.
Thanks,
Sombir
The best way you can do that is Custom Directives! it is what makes angularjs powerfull.
.directive('myCustomer', function() {
return {
restrict: 'E',
template: 'myCustomer.html'
};
});
and in your index or whatever you can call it:
<my-customer></my-customer>
EDIT:
To generate dynamicaly your Custom directive Use the property Controller or Link.
.directive('myCustomer', function() {
return {
restrict: 'E',
template: 'myCustomer.html',
controller: function ($scope,$http) {
// here you can use $scope, $http etcs... but
}, // to make it clean and standard use Link
link: function (scope, element, attrs) {
// do all stuff like API call JQuery stuff and all here
}
};
});
with that you can generate dynamicaly you directive :) from an API or whatever you want to. :)

Concat Template With Custom Directive Template

I'm not sure if its possible to achieve what I want. I'll try to explain it with an example:
a custom directive:
appDirectives.directive("myTestDirective",
function() {
return {
restrict: "E",
templateUrl: "<div> Some template here... {{ testObject }} <div>",
scope: {
'testObject' = '#testObject'
}
};
}]);
Use directive in a tempalte:
<my-test-directive testObject="And some more here...">
<div>
I also want to be in the template!
</di>
</my-test-directive>
And I want to achieve this template:
<div> Some template here... And some more here... <div>
<div>
I also want to be in the template!
</di>
You can do this with transclusion. Just add a param to the directive, and use ng-transclude on the element you want to have the contents be inserted.
You might have to remove some the original since the transclusion needs an element to operate on, but this is the basic idea.
angular.module('test', [])
.directive('myTestDirective', function() {
return {
restrict: "E",
template: "<div> Some template here... {{testObject}} <div><div ng-transclude></div>",
scope: {
testObject: '#'
},
transclude: true
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test">
<my-test-directive test-object="And some more here...">
<div>
I also want to be in the template!
</div>
</my-test-directive>
</div>

How to write an angularjs directive that makes use of both scope and attributes and refer it thru compiled partial?

I want to write a directive which takes advantage of custom attributes, as follows:
<plant-stages
title="Exploration<br/>du cycle de<br/>développement<br/>de la plante"
></plant-stages>
The controller is currently as follows:
app.directive('plantStages', function () {
return {
restrict: 'AE',
templateUrl: 'corn.figure.plant.stages.html',
link: function (scope, element, attrs) {
scope.title = attrs.title;
}
};
});
The partial is as follows:
<figure class="cornStages">
<div>
<p>{{title}}</p>
</div>
<div ng-repeat="stage in stages">
<div class="stage{{stage.stage}}"></div>
<div>
BBCH : {{stage.bbch}}<br/>
{{stage.displayName}}
</div>
</div>
</figure>
The partial makes use of some scope model variables.
And {{title}} should support plain HTML injection out of the view which embeds it, hence should be compiled. I tried to support this but without success.
What modification should I make to have the HTML compiled?
A bonus question: when I pass the attribute in, I create a dummy title variable in the scope that persists where it should only be local. How would one make changes to handle this?
If you want to wrap HTML in your custom directive take a look at the transclude option (see docs):
module.directive('myDirective', function() {
return {
restrict: 'E',
transclude: true,
template: '<div ng-transclude></div>'
};
});
This enables you to place HTML within the directive tag which can be used in the template:
<div ng-controller="Controller">
<my-directive>
<h1>Test</h1>
</my-directive>
</div>
In case you really want to pass HTML via an attribute use ng-bind-html. This requires the ngSanitize module:
module.directive('myDirective', function () {
return {
restrict: 'E',
template: '<div ng-bind-html="title"></div>',
scope: {
title:'#'
}
};
});
I added this to your fiddle.

Using ng-switch in directive with transclude

I am trying to create a template that shows some transcluded content. When I use ng-show everything works fine, but using ng-if or ng-switch gives me problems. I get this error message: Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found
I understand that ng-switch creates a new scope. But the transclude should still go up to the parent chain. Is this a defect in angularjs? See http://jsfiddle.net/HgvP7/
Here is my html, modified from the documentation example:
<div ng-app="docsTransclusionExample">
<div ng-controller="Ctrl">
<my-dialog>Check out the contents, {{name}}!</my-dialog>
</div>
<!-- my-dialog.html -->
<script type="text/ng-template" id="my-dialog.html">
<div ng-switch="1+1">
<div ng-switch-when="2">
<div ng-transclude></div>
</div>
</div>
</script>
</div>
And the code:
angular.module('docsTransclusionExample', [])
.controller('Ctrl', function($scope) {
$scope.name = 'Tobias';
})
.directive('myDialog', function() {
return {
restrict: 'E',
transclude: true,
scope: {},
templateUrl: 'my-dialog.html',
link: function (scope, element) {
scope.name = 'Jeff';
}
};
});

Resources