Angular.js directive dynamic templateURL - angularjs

I have a custom tag in a routeProvider template that that calls for a directive template. The version attribute will be populated by the scope which then calls for the right template.
<hymn ver="before-{{ week }}-{{ day }}"></hymn>
There are multiple versions of the hymn based on what week and day it is. I was anticipating to use the directive to populate the correct .html portion. The variable is not being read by the templateUrl.
emanuel.directive('hymn', function() {
var contentUrl;
return {
restrict: 'E',
link: function(scope, element, attrs) {
// concatenating the directory to the ver attr to select the correct excerpt for the day
contentUrl = 'content/excerpts/hymn-' + attrs.ver + '.html';
},
// passing in contentUrl variable
templateUrl: contentUrl
}
});
There are multiple files in excerpts directory that are labeled before-1-monday.html, before-2-tuesday.html, …

emanuel.directive('hymn', function() {
return {
restrict: 'E',
link: function(scope, element, attrs) {
// some ode
},
templateUrl: function(elem,attrs) {
return attrs.templateUrl || 'some/path/default.html'
}
}
});
So you can provide templateUrl via markup
<hymn template-url="contentUrl"><hymn>
Now you just take a care that property contentUrl populates with dynamically generated path.

You can use ng-include directive.
Try something like this:
emanuel.directive('hymn', function() {
return {
restrict: 'E',
link: function(scope, element, attrs) {
scope.getContentUrl = function() {
return 'content/excerpts/hymn-' + attrs.ver + '.html';
}
},
template: '<div ng-include="getContentUrl()"></div>'
}
});
UPD. for watching ver attribute
emanuel.directive('hymn', function() {
return {
restrict: 'E',
link: function(scope, element, attrs) {
scope.contentUrl = 'content/excerpts/hymn-' + attrs.ver + '.html';
attrs.$observe("ver",function(v){
scope.contentUrl = 'content/excerpts/hymn-' + v + '.html';
});
},
template: '<div ng-include="contentUrl"></div>'
}
});

Thanks to #pgregory, I could resolve my problem using this directive for inline editing
.directive("superEdit", function($compile){
return{
link: function(scope, element, attrs){
var colName = attrs["superEdit"];
alert(colName);
scope.getContentUrl = function() {
if (colName == 'Something') {
return 'app/correction/templates/lov-edit.html';
}else {
return 'app/correction/templates/simple-edit.html';
}
}
var template = '<div ng-include="getContentUrl()"></div>';
var linkFn = $compile(template);
var content = linkFn(scope);
element.append(content);
}
}
})

You don't need custom directive here. Just use ng-include src attribute. It's compiled so you can put code inside. See plunker with solution for your issue.
<div ng-repeat="week in [1,2]">
<div ng-repeat="day in ['monday', 'tuesday']">
<ng-include src="'content/before-'+ week + '-' + day + '.html'"></ng-include>
</div>
</div>

I had the same problem and I solved in a slightly different way from the others.
I am using angular 1.4.4.
In my case, I have a shell template that creates a CSS Bootstrap panel:
<div class="class-container panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">{{title}} </h3>
</div>
<div class="panel-body">
<sp-panel-body panelbodytpl="{{panelbodytpl}}"></sp-panel-body>
</div>
</div>
I want to include panel body templates depending on the route.
angular.module('MyApp')
.directive('spPanelBody', ['$compile', function($compile){
return {
restrict : 'E',
scope : true,
link: function (scope, element, attrs) {
scope.data = angular.fromJson(scope.data);
element.append($compile('<ng-include src="\'' + scope.panelbodytpl + '\'"></ng-include>')(scope));
}
}
}]);
I then have the following template included when the route is #/students:
<div class="students-wrapper">
<div ng-controller="StudentsIndexController as studentCtrl" class="row">
<div ng-repeat="student in studentCtrl.students" class="col-sm-6 col-md-4 col-lg-3">
<sp-panel
title="{{student.firstName}} {{student.middleName}} {{student.lastName}}"
panelbodytpl="{{'/student/panel-body.html'}}"
data="{{student}}"
></sp-panel>
</div>
</div>
</div>
The panel-body.html template as follows:
Date of Birth: {{data.dob * 1000 | date : 'dd MMM yyyy'}}
Sample data in the case someone wants to have a go:
var student = {
'id' : 1,
'firstName' : 'John',
'middleName' : '',
'lastName' : 'Smith',
'dob' : 1130799600,
'current-class' : 5
}

I have an example about this.
<!DOCTYPE html>
<html ng-app="app">
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
<div class="container-fluid body-content" ng-controller="formView">
<div class="row">
<div class="col-md-12">
<h4>Register Form</h4>
<form class="form-horizontal" ng-submit="" name="f" novalidate>
<div ng-repeat="item in elements" class="form-group">
<label>{{item.Label}}</label>
<element type="{{item.Type}}" model="item"></element>
</div>
<input ng-show="f.$valid" type="submit" id="submit" value="Submit" class="" />
</form>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-1.10.2.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.min.js"></script>
<script src="app.js"></script>
</body>
</html>
angular.module('app', [])
.controller('formView', function ($scope) {
$scope.elements = [{
"Id":1,
"Type":"textbox",
"FormId":24,
"Label":"Name",
"PlaceHolder":"Place Holder Text",
"Max":20,
"Required":false,
"Options":null,
"SelectedOption":null
},
{
"Id":2,
"Type":"textarea",
"FormId":24,
"Label":"AD2",
"PlaceHolder":"Place Holder Text",
"Max":20,
"Required":true,
"Options":null,
"SelectedOption":null
}];
})
.directive('element', function () {
return {
restrict: 'E',
link: function (scope, element, attrs) {
scope.contentUrl = attrs.type + '.html';
attrs.$observe("ver", function (v) {
scope.contentUrl = v + '.html';
});
},
template: '<div ng-include="contentUrl"></div>'
}
})

Related

angular bind html tags from controller to html view

I need to render the $scope.htmlView tags in to html view.
I already tried using ng-bind-html. It renders the html tags but scope variable values will not appear.
How can I render both html tags and and scope variable values?
This is the controller:
$scope.newObj = {
billStatus : true;
eventTime : "2015-01-10"
};
$scope.htmlView = '<p>{{newObj.eventTime}}</p> <div style="margin-top: -15px;"><md-checkbox ng-checked="{{newObj.billStatus}}" style="margin-left: 0px;" aria-label="Bilable"><span style="margin-left:0px;">Bilable</span> </md-checkbox></div>'
Expected result is:
<p> 2015-01-10</p>
<div style="margin-top: -15px;">
<md-checkbox ng-checked="true" style="margin-left: 0px;" aria- label="Bilable">
<span style="margin-left:0px;">Bilable</span>
</md-checkbox>
</div>
I search over the internet over days and still could't find out a way to figure out this. please help me. thank you.
You have to do 2 things.
Use data-ng-bind-html=""
Use $sce.trustAsHtml(string)
UPDATED:
If you wont to use angular expressions, you have to compile them using
$compile.
You can read more via this $SCE
I will tell you a long way but it will help you.Make a custom directive like this.
app.directive('dynamic', function ($compile) {
return {
restrict: 'A',
replace: true,
link: function (scope, ele, attrs) {
scope.$watch(attrs.dynamic, function(html) {
ele.html(html);
$compile(ele.contents())(scope);
});
}
};
});
Use as
<span dynamic="{{htmlView}}" >
Hi please check this fiddle
https://plnkr.co/edit/iqNltdDYv2n9Agke0C2C?p=preview
HTML
<div ng-controller="ExampleController">
<p >{{newObj.eventTime}}</p>
<p dynamic="htmlView"></p>
</div
and JS
(function(angular) {
'use strict';
angular.module('bindHtmlExample', ['ngSanitize'])
.controller('ExampleController', ['$scope', function($scope) {
$scope.newObj = {
billStatus : true,
eventTime : "2015-01-10"
}
$scope.htmlView = '<p> {{newObj.eventTime}}</p> <div style="margin-top: -15px;">Hello <md-checkbox ng-checked="{{newObj.billStatus}}" style="margin-left: 0px;" aria-label="Bilable"><span style="margin-left:0px;">Bilable</span> </md-checkbox></div>'
}])
.directive('dynamic', function($compile) {
return {
restrict: 'A',
replace: true,
link: function (scope, element, attrs) {
scope.$watch(attrs.dynamic, function(html) {
element[0].innerHTML = html;
$compile(element.contents())(scope);
});
}
};
});
})(window.angular);

Variable value not passing in a controller using directive with ng-class

I am referencing the value of the variable in a controller in an ng-class template but its not working.
here is the html directive template URl :
<div class="tooltip-anchor">
<div class=" tooltip-content ehub-body" ng-class="{ 'tooltip__content--disabled': tooltipContentValue}" ng-transclude>Tooltip content</div>
</div>
Here is where i am using the directive in the index page
<div style="text-align:center;">
<ehub-tooltip>Hello i am here, and i am her to stay</ehub-tooltip>over here
<ehub-tooltip>Be nice to people on your way up and they will be nice to you on your way down</ehub-tooltip>click me
</div>
And here is the directive:
in this directive i am creating a variable and setting it to false and also trying to use it in an ng-class attribute
(function (window) {
'use strict';
angular
.module('ehub.component.tooltip', [])
.controller('ehubTooltipCtrl', ['$scope', function ($scope) {
$scope.tooltipContentValue = false;
}])
.directive('ehubTooltip', ehubTooltip);
function ehubTooltip() {
var directive = {
controller: "ehubTooltipCtrl",
link: link,
transclude: true,
templateUrl: 'ehub-tooltip.html',
restrict: 'E'
};
return directive;
function link(scope, element, attrs) {
scope.keyupevt = function () {
if (event.keyCode === 27) {
$scope.tooltipContentValue = true;
}
}
}
}
})();
Try this working jsfiddle.
angular.module('ExampleApp', ['ngMessages'])
.controller('ExampleController', function($scope) {
})
.directive('ehubTooltip', function() {
var directive = {
link: link,
transclude: true,
template: '<div class="tooltip-anchor"><div class=" tooltip-content ehub-body" ng-class="{ \'tooltip__content--disabled\': tooltipContentValue}" ng-transclude>Tooltip content</div></div>',
restrict: 'E'
};
function link(scope, element, attrs) {
scope.tooltipContentValue = false;
scope.keyupevt = function() {
if (event.keyCode === 27) {
scope.tooltipContentValue = true;
}
}
}
return directive;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ExampleApp">
<div ng-controller="ExampleController">
<div style="text-align:center;">
<a href="" ng-keyup="keyupevt()">
<ehub-tooltip>Hello i am here, and i am her to stay</ehub-tooltip>over here</a>
<a href="" ng-keyup="keyupevt()">
<ehub-tooltip>Be nice to people on your way up and they will be nice to you on your way down</ehub-tooltip>click me</a>
</div>
</div>
</div>

AngularJS custom directive - map isolate scope to new child scope

I created a custom directive for bootstrap alerts. My alerts display fine (hard code and data bind). Based on the alert type, I want display a unique header into my alert message based on the returned scope values (success, info, warning, danger). Currently I'm passing the type into <h1> but I don't want those values, they need to be custom.
<!-- data binding example -->
<trux-alert ng-repeat="alert in alerts" type="{{alert.type}}" close="closeAlert($index)">{{alert.msg}}</trux-alert>
<!-- hard coded example -->
<trux-alert close="close" type="warning">This is an important warning message!</trux-alert>
Inside my directive, the scope is isolated using scope: '#' (one-way)
.directive('truxAlert', function() {
return {
scope: {
type: '#',
close: '&'
},
replace: true,
transclude: true,
template:
'<div class="alert" '+
'ng-class="[\'alert-\' + (type || \'warning\'), closeable ? \'alert-dismissible\' : null]" '+
'role="alert">'+
'<button ng-show="closeable" '+
'type="button" class="close" '+
'ng-click="close({$event: $event})" '+
'data-dismiss="alert" aria-label="Close">'+
'<span aria-hidden="true">×</span>'+
'<span class="sr-only">Close</span>'+
'</button>'+
'<h1>{{type}}</h1>'+
'<div ng-transclude></div>'+
'</div>',
link: function (scope, element, attrs) {}
}
});
This would be easier if all my values were pulled through data binding, but I need to allow for manual hard coded option. I know with one-way isolated scopes '#' I can't change these values though DOM manipulation. I can't use '=' or '&' for two-way because the values are strings.
How do I solve for this problem?
My recommendation is have one attribute for the control the open/closed state of the directive's alert and another attribute for the dismiss handler function.
angular.module("myApp").directive('truxAlert', function() {
return {
scope: {
type: '#',
dismissHandler: '&',
title: '#',
open: '='
},
replace: true,
transclude: true,
template:
'<div class="alert" ng-show="open" '+
'ng-class="[\'alert-\' + type]" '+
'role="type">'+
'<button type="button" class="close" '+
'ng-click="truxClose($event)" '+
'data-dismiss="alert" aria-label="Close">'+
'<span aria-hidden="true">×</span>'+
'<span class="sr-only">Close</span>'+
'</button>'+
'<h1>{{title+" "+type}}</h1>'+
'<div ng-transclude></div>'+
'</div>',
link: function (scope, element, attrs) {
console.log("truxAlert linking");
if (!scope.type) { scope.type="warning" }
scope.truxClose = function(event) {
console.log("truxClose "+event);
if (attrs.dismissHandler) {
scope.dismissHandler({$event: event});
return;
}
scope.open = false;
};
}
};
});
The linking function of the directive determines if the dismiss-handler attribute exists and either invokes the dismiss handler or directly closes the alert.
The DEMO PLNKR show the directive being used both with an ng-repeat directive and in a standalone manner.
Maybe, i don't understand you question.
You want to do so jsfiddle?
<form name="ExampleForm" id="ExampleForm">
<span simple="{{val}}">{{val}} - value from data-binding </span>
<br>
<span simple="valueTwo">valueTwo - hard code value</span>
</form>
And js controller
.controller('ExampleController', function($scope, $rootScope, $alert) {
$scope.val = "valueOne";})
And js directive
.directive('simple', function() {
return {
restrinct: 'A',
scope: {
simple: "#"
},
link: function(scope) {
console.log(scope.simple, typeof(scope.simple));
}
}})
UPDATED
angular.module('ExampleApp', ['use', 'ngMessages'])
.controller('ExampleOneController', function($scope) {
$scope.val = "valueOne";
$scope.$on('pass.from.directive', function(event, value) {
$scope.valFromDirective = value;
});
})
.controller('ExampleTwoController', function($scope) {
$scope.val = "valueTwo";
$scope.$on('pass.from.directive', function(event, value) {
$scope.valFromDirective = value;
});
})
.controller('ExampleThreeController', function($scope) {
$scope.val = "valueThree";
$scope.$on('pass.from.directive', function(event, value) {
$scope.valFromDirective = value;
});
})
.directive('simple', function($interval) {
return {
restrinct: 'A',
scope: {
simple: "#"
},
link: function(scope) {
var i = 0;
$interval(function() {
i++;
scope.$emit('pass.from.directive', scope.simple + i);
}, 1000);
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular-messages.min.js"></script>
<script src="https://cdn.rawgit.com/Stepan-Kasyanenko/use-form-error/master/src/use-form-error.js"></script>
<div ng-app="ExampleApp">
<div ng-controller="ExampleOneController">
<h3>
ExampleOneController
</h3>
<form name="ExampleForm" id="ExampleForm">
<div simple="{{val}}">{{val}} - value from scope </div>
<div>{{valFromDirective}} - value from directive </div>
</form>
</div>
<div ng-controller="ExampleTwoController">
<h3>
ExampleTwoController
</h3>
<form name="ExampleForm" id="ExampleForm">
<div simple="{{val}}">{{val}} - value from scope </div>
<div>{{valFromDirective}} - value from directive </div>
</form>
</div>
<div ng-controller="ExampleThreeController">
<h3>
ExampleThreeController
</h3>
<form name="ExampleForm" id="ExampleForm">
<div simple="{{val}}">{{val}} - value from scope </div>
<div>{{valFromDirective}} - value from directive </div>
</form>
</div>
</div>

Angular directive scope - template include vs inline transclude

I have an angular directive for displaying a modal window. It can accept the contents either inline between the HTML tags, or be pointed to a template. When using this directive I seem to have normal access to the $scope when I am using the transcluded inline version of this directive, but when I use a template I do not.
What am I missing here? I've made a smaller sample directive that has the same behavior.
Demo: http://fiddle.jshell.net/ahezfaxj/2
Inline Content Usage
<ang-test show="showBoolean">
<p>Content here!</p>
</ang-test>
Template Usage
<ang-test show="showBoolean" template="'myTemplate.html'"></ang-test>
Directive
app.directive("angTest", function () {
return {
template: function () {
return "<div class='test-container'>" +
" <div ng-if='show && template' ng-include='template'></div>" +
" <div ng-if='show && !template' ng-transclude></div>" +
"</div>";
},
restrict: "E",
replace: true,
transclude: true,
scope: {
template: "#",
show: "="
},
link: function ($scope, $element, attrs) {
if(value){
$element[0].style.display="block";
}else{
$element[0].style.display="none";
}
}
};
});
Please see demo below. You created isolated scope in your directive thus your directive scope is not this same as controller $scope. But you can add as well thing to your directive scope like in example below.
I hope that will help.
var app = angular.module("app", []);
app.controller("BaseCtrl", function ($scope) {
$scope.thing = "Hello!";
$scope.showOne=false;
$scope.showTwo=false;
});
app.directive("angTest", function () {
return {
template: function () {
return "<div class='test-container'>" +
" <div ng-if='show && template' ng-include='template'></div>" +
" <div ng-if='show && !template' ng-transclude></div>" +
"</div>";
},
restrict: "E",
replace: true,
transclude: true,
scope: {
template: "#",
show: "=",
thing:'#'
},
link: function ($scope, $element, attrs) {
//Show/hide when `show` changes
$scope.$watch("show", function (value) {
if(value){
$element[0].style.display="block";
}else{
$element[0].style.display="none";
}
});
}
};
});
.test-container{
padding:5px;
background: #EEE;
}
.transcluded {
color:red
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="BaseCtrl">
Outside Directive: <strong>{{thing}}</strong>
<hr />
<button type="button" ng-click="showOne=!showOne">Toggle One</button>
<ang-test show="showOne">
<p class="transcluded">Inside Included Directive: <strong>--> thing transcluded-->{{thing}}</strong></p>
</ang-test>
<hr />
<script type="text/ng-template" id="myTemplate">
<p>Inside Template Directive: <strong>thing from directive scope -->{{thing}}</strong></p>
</script>
<button type="button" ng-click="showTwo=!showTwo" >Toggle Two</button>
<ang-test show="showTwo" template="myTemplate" thing="{{thing}}"></ang-test>
</div>
</div>

Render a directive inside another directive (within repeater template)

I am trying to render a directive inside another directive (not sure if the repeater inside the template is working this), and it seems to just output as text rather than compiling the directive (plunker code here: http://plnkr.co/edit/IRsNK9)
Any ideas on how I can actually get it to render properly my-dir-one, my-dir-two, my-dir-three directives inside the repeater?
index.html
<!doctype html>
<html ng-app="plunker" >
<head>
<meta charset="utf-8">
<title>AngularJS Plunker</title>
<link rel="stylesheet" href="style.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.js"></script>
<script src="app.js"></script>
<script id="partials/addressform.html" type="text/ng-template">
partial of type {{type}}<br>
</script>
</head>
<body>
<div container></div>
<br /><br /><br />
<b>Below is just to test the directives are actually usable outside the repeater</b>
<div my-dir-one></div>
<div my-dir-two></div>
<div my-dir-three></div>
</body>
</html>
app.js
var app = angular.module('plunker', []);
app.directive('container', function () {
return {
restrict: 'A',
scope: {},
replace: true,
template: '<div class="views">' +
' <div class="view" ng-repeat="view in views">' +
' <div {{view.dir}}>{{view.dir}}</div>' +
' </div>' +
'</div>',
link: function (scope, elm) {
scope.views = [
{ dir: 'my-dir-one' },
{ dir: 'my-dir-two' },
{ dir: 'my-dir-three' }
];
}
}
});
app.directive('myDirOne', function () {
return {
restrict: 'A',
scope: {},
replace: true,
template: '<div>This is directive one.</div>'
}
});
app.directive('myDirTwo', function () {
return {
restrict: 'A',
scope: {},
replace: true,
template: '<div>This is directive two.</div>'
}
});
app.directive('myDirThree', function () {
return {
restrict: 'A',
scope: {},
replace: true,
template: '<div>This is directive three.</div>'
}
});
I managed to work around this issue by re-writing the code:
First I updated the template code as follows:
template: '<div class="views">' +
' <div class="view-wrapper" ng-repeat="view in views">' +
' <div view="{{view.dir}}"></div>' +
' </div>' +
'</div>',
Note that I created a new 'view' directive. Next the view directive definition as follows:
app.directive('view', ['$compile', function (compile) {
return {
restrict: 'A',
scope: {
view: '#'
},
replace: true,
template: '<div class="view"></div>',
controller: ['$scope', function (scope) {
scope.$watch('view', function (value) {
scope.buildView(value);
});
}],
link: function (scope, elm, attrs) {
scope.buildView = function (viewName) {
var view = compile('<div ' + viewName + '></div>')(scope);
elm.append(view);
}
}
}
}]);
So essentially, the view.dir variable is passed as an attribute to the 'view' directive, which then watches it's value and compiles a template with the directive in it.
This is in part a timing problem...I think that by the time it's resolving the {{}} expressions, it's already parsed out and rendered directives. It's not the nesting or the repeater that are the problem, per se.
What you're after here, though, is 'decide which directive to render based on the value of a variable'. There are a couple ways to do that.
Here's one that should work, though it might not scale as nicely as you'd like:
<div class='views' ng-repeat='view in views'>
<div ng-switch='view.dir'>
<div ng-when='my-dir-one' my-dir-one />
<div ng-when='my-dir-two' my-dir-two />
<div ng-when='my-dire-three' my-dir-three />
</div>
</div>
Other options for similar tricks: it looks like you could use ngBindTemplate to take a string from your data and use it as the template for an element. This would probably permit some tricky (and illegible) behavior.
You can specify a directive for an element as a class, but I don't know whether using ngClass to do this would allow you to dynamically select the directive, or whether that would come too late in the pipeline.

Resources