How to bind html code with ng-... attributes - javascript

I want to create <select ng-model='selected' ng-options='stage as stage.name for stage in stages'></select> element in variable as a string. As you see here, there are ng-... attributes inside the select element. If I use select element without ng-... attribute it is displaying without problem. If I use ng-... inside it, it's not showing anything. So, how do I make it display with ng-...? Please help.
My html code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="Scripts/angular.min.js"></script>
<script src="Scripts/angular-sanitize.js"></script>
<script src="Scripts/myScripts/index.js"></script>
</head>
<body>
<div ng-app="myapp" ng-controller="myCtrl">
<div ng-bind-html="htmlCode">
</div>
</div>
</body>
</html>
My js code:
var app = angular.module('myapp', ['ngSanitize']);
app.controller('myCtrl', ["$scope", function ($scope) {
$scope.stages =
[{ name: "Identification of Discontinuing Factors", value: 1 },
{ name: "Project Level Assessment", value: 2 },
{ name: "Organizational Readiness Assessment", value: 3 }];
$scope.htmlCode = "<select ng-model='selectedStage' ng-options='stage as stage.name for stage in stages'></select>";
}]);

you need to recompile the dom in order to work the ng- attributes. use this directive as an attribute to the ng-bind-html element.
.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);
});
}
};
});
Demo
var app = angular.module('myapp', []);
app.controller('myCtrl', ["$scope","$sce", function ($scope,$sce) {
$scope.stages =
[{ name: "Identification of Discontinuing Factors", value: 1 },
{ name: "Project Level Assessment", value: 2 },
{ name: "Organizational Readiness Assessment", value: 3 }];
$scope.htmlCode = "<select ng-model='selectedStage' ng-options='stage as stage.name for stage in stages'></select>";
$scope.trust = function(html){
return $sce.trustAsHtml(html)
}
}]);
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);
});
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myapp" ng-controller="myCtrl">
<div ng-bind-html="trust(htmlCode)" dynamic>
</div>
{{selectedStage}}
</div>

Related

Iframe content is not refreshing correctly in AngularJS directive

I'm using a directive to set html, css and javascript code inside of and iframe. These iframes are inside a ng-repeat, so there is an iframe per result.
Then these results are filtered by title and sortby title or date.
So, I have two problems:
Firefox is not showing the iframe html code but the rest of the browsers are.
After selecting a sort option, the title and date are refresing correctly, but the iframe content is not.
Here is a link to Plunker: http://plnkr.co/edit/Kvsbir?p=info
And here is my code:
<body ng-controller="HomeController">
<div class="panel-heading text-center">
<h3>Projects</h3>
<form action="" class="form-inline">
<div class="form-group">
<input class="form-control" type="text" ng-model="query.name" placeholder="Search...">
</div>
<div class="form-group dropdown">
<select class="btn btn-default dropdown-toggle" type="text" ng-model="sortOrder" ng-options="item.value as item.text for item in sortOptions"></select>
</div>
</form>
</div>
<div class="panel-body">
<div ng-model="result" ng-repeat="project in projects | orderBy:sortOrder | filter:query.name" class="col-md-4 col-sm-6">
<div class="single-pen">
<div class="meta">
<h3>{{project.title}}</h3>
<h6> {{project.created_at | date}} </h6>
</div>
<div class="iframe-wrap">
<div theframe="project"></div>
</div>
</div>
</div>
</div>
</body>
The controller
var app = angular.module('plunker', []);
app.controller('HomeController', function ($rootScope,$filter, $scope) {
'use strict';
var vm = this;
// Filter & sort
$scope.sortOptions = [
{ value: "-created_at", text: "Ordenar by Date"},
{ value: "title", text: "Ordenar by Title"}
];
$scope.sortOrder = $scope.sortOptions[0].value;
$scope.result = [
{
title: 'Project 1',
code: [{
css: "body{background-color: pink;}",
html: "<h1>Hello</h1>",
js: ""
}],
created_at: "2016-02-06T22:52:24.900Z"
},
{
title: 'Project 2',
code: [{
css: "body{background-color: blue;}",
html: "<h1>Hello</h1>",
js: ""
}],
created_at: "2016-02-10T11:52:33.055Z"
},
{
title: 'Project 3',
code: [{
css: "body{background-color: yellow;}",
html: "<h1>Hello</h1>",
js: ""
}],
created_at: "2016-02-06T22:52:24.900Z"
},
{
title: 'Project 4',
code: [{
css: "body{background-color: orange;}",
html: "<h1>Hello</h1>",
js: ""
}],
created_at: "2016-02-08T11:52:33.055Z"
},
{
title: 'Project 5',
code: [{
css: "body{background-color: red;}",
html: "<h1>Hello</h1>",
js: ""
}],
created_at: "2016-02-01T13:46:33.917Z"
},
];
$scope.projects = $scope.result;
})
.directive('theframe', function($compile){
return {
restrict: 'A',
scope: {
theframe: '='
},
template: '<iframe id="frame" scrolling="no"></iframe>',
link: function($scope, element, attrs) {
var $head = $(element).find('#frame').contents().find('head');
var $body = $(element).find('#frame').contents().find('body');
$scope.$watch('projects', function(n,o){
$head[0].innerHTML = '<style>'+$scope.theframe.code[0].css+'</style>';
$body[0].innerHTML = $scope.theframe.code[0].html + '<script> (function(){ \n' + $scope.theframe.code[0].js + '\n})();</script>';
}, true);
}
}
})
Any hints of why is not working correctly?
EDIT
SOLVED Firefox issue!
Thanks for the $timeout idea.
This is what i've changed: http://plnkr.co/edit/Kvsbir?p=preview
.directive('theframe', function($compile, $timeout){
return {
restrict: 'A',
scope: {
theframe: '='
},
template: '<iframe id="frame" scrolling="no"></iframe>',
link: function($scope, element, attrs) {
var $head = $(element).find('#frame').contents().find('head');
var $body = $(element).find('#frame').contents().find('body');
var timeout = setInterval(function(){
$head[0].innerHTML = '<style>'+$scope.theframe.code[0].css+'</style>';
$body[0].innerHTML = $scope.theframe.code[0].html + '<script> (function(){ \n' + $scope.theframe.code[0].js + '\n})();</script>';
$scope.$apply();
}, 500);
}
}
})

Angular directive scope sharing between two directive

I am working on a project where I have one controller and two directives and I need to share the scope between them all, I have created plnkr here.
The code structure is as following:
Main Controller
--Drawable Directive
----Draw-rectangle Directive
In the Main ctrl there is one object rois on scope which I am to Drawable and Draw-rectangle directive. and on click of drawable it updates to scope of Main controller but when I click on the draw-rectangle directive it's not updating the scope.
I want to all (3) scopes to be synced using two way data binding.
It seems conceptually correct but why its not updating the scope from Draw-rectangle Directive?
Thanks in advance!
When you click "draw-rectangle" your are also clicking "drawable" because "draw-rectangle" is inside "drawable". You must stop propagation from "draw-rectangle" to "drawable" using event.preventDefault(); as folow:
var app = angular.module('myApp', []);
app.controller('MainCtrl', function($scope) {
$scope.rois = [{
name: 'Jack',
city: 'pune'
}, {
name: 'Tony',
city: 'Mumbai'
}];
$scope.test = "Test";
});
app.directive('drawable', [
function() {
return {
restrict: "EA",
link: function(scope, element, attrs) {
element.on('click', function(event) {
event.preventDefault();
scope.rois = [{
name: 'Stark',
city: 'pune'
}, {
name: 'Inc',
city: 'Mumbai'
}];
scope.$apply();
console.log(scope.rois);
});
}
};
}
]);
app.directive('drawRectangle', [
function() {
return {
restrict: "EA",
link: function(scope, element, attrs) {
element.on('click', function(event) {
event.stopPropagation(); // STOP PROPAGATION
event.preventDefault();
scope.rois = [{
name: 'Meuk',
city: 'pune'
}, {
name: 'Tony',
city: 'Mumbai'
}];
scope.$apply();
console.log(scope.rois);
});
}
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller='MainCtrl' style='width: 400px;height: 400px;border: 1px solid red;'>
<div drawable rois="rois" style='width: 300px;height: 300px;border: 1px solid red;'>
<div draw-rectangle rois="rois" style='width: 200px;height: 200px;border: 1px solid red;'>
<button type="button" style='margin: 20px; border: 1px solid red;'>Click me!</button>
</div>
</div>
<br>
<br>{{rois | json}}
</div>
u need to stop bubble up the event, because when rect directive clicked, drawable also trigger click! use event.stopPropagation()
var app = angular.module('myApp');
app.directive('drawable', ['$document',
function($document) {
return {
restrict: "EA",
scope: {
rois: '='
},
link: function(scope, element, attrs) {
console.log(scope.rois);
element.on('click', function(event) {
event.stopPropagation();
scope.rois = [{
name: 'Stark',
city: 'pune'
}, {
name: 'Inc',
city: 'Mumbai'
}];
scope.$apply();
console.log(scope.rois);
});
}
};
}
]);
app.directive('drawRectangle', ['$document',
function($document) {
return {
restrict: "EA",
scope: {
rois: '='
},
link: function(scope, element, attrs) {
element.on('click', function(event) {
event.stopPropagation();
scope.rois = [{
name: 'Meuk',
city: 'pune'
}, {
name: 'Tony',
city: 'Mumbai'
}];
scope.$apply();
console.log(scope.rois);
});
}
};
}
]);
You are using isolated scope for two directives. Isolated scope will create a child scope. So, you cannot access "rois" out of the directive's link function.
Try after you remove the isolated scope,
scope: {
rois: '='
},

Angular js dynamic grid headers after importing JSON data

From the following code, i will get the the grid having columns Name, Gender and Company.
Now how will i dynamically change the Grid column names : Like I want FirstName instead of Name
<!doctype html>
<html ng-app="app">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular-touch.js"> </script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular-animate.js"> </script>
<script src="http://ui-grid.info/docs/grunt-scripts/csv.js"></script>
<script src="http://ui-grid.info/docs/grunt-scripts/pdfmake.js"></script>
<script src="http://ui-grid.info/docs/grunt-scripts/vfs_fonts.js"> </script>
<script src="/release/ui-grid-unstable.js"></script>
<script src="/release/ui-grid-unstable.css"></script>
<script>
var app = angular.module('app', ['ngAnimate', 'ngTouch', 'ui.grid', 'ui.grid.importer']);
app.controller('MainCtrl', ['$scope', '$http', '$interval', function ($scope, $http, $interval) {
$scope.data = [];
$scope.gridOptions = {
enableGridMenu: true,
data: 'data',
importerDataAddCallback: function ( grid, newObjects ) {
$scope.data = $scope.data.concat( newObjects );
},
onRegisterApi: function(gridApi){
$scope.gridApi = gridApi;
}
};
}]);
</script>
</head>
<body>
<div ng-controller="MainCtrl">
<div ui-grid="gridOptions" ui-grid-importer class="grid"></div>
</div>
</body>
</html>
Suppoese this is the json file
[{
"Name":"John Smith",
"Gender":"male",
"Company":"TestIcon"
},
{
"Name":"Jane Doe",
"Gender":"female",
"Company":"FastTruck"
}]
From the following code, i will get the the grid having columns Name, Gender and Company.
Now how will i dynamically change the Grid column names : Like I want FirstName instead of Name
Guys Please help me in sorting this?
I guess you want:
columnDefs: [
{ field: 'Name', displayName: 'FirstName' },
{ field: 'Gender' },
{ field: 'Company' }
]

Directive within a directive within a template not working

I'm an Angular noob. I'm structuring my panes as sets of tables and using directives to keep the template HTML abstract from the table implementation. The panes are getting transcluded correctly, but interior only contains the text not the table structure. Here's my code:
<!DOCTYPE html>
<html>
<head>
<script src= "angular.js"></script>
<script src= "angular-sanitize.js"></script>
</head>
<body ng-app="pp">
<div ng-controller="ppMain">
<ul class="menu">
<li ng-repeat="pageID in homePageList">{{pages[pageID].str}}</li>
</ul>
<div>
<div ng-repeat="pageID in pageList" ng-bind-html="pages[pageID].template">
</div>
</div>
</div>
<script>
var pp = {
ctl: {}
};
pp.mod = angular.module('pp', ['ngSanitize']);
pp.mod.directive({
'ppHeader': function () {
return ({
});
},
'ppGroup': function () {
return ({
template: '<table ng-transclude></table>',
transclude: true,
restrict: 'EA'
});
},
'ppRow': function () {
return ({
template: '<tr ng-transclude></tr>',
transclude: true,
restrict: 'EA'
});
},
'ppLabel': function () {
return ({
template: '<td ng-transclude></td>',
transclude: true,
restrict: 'EA'
});
},
'ppValue': function () {
return ({
template: '<td ng-transclude></td>',
transclude: true,
restrict: 'EA'
});
},
});
pp.ctl.main = pp.mod.controller('ppMain', ['$scope', function ($scope) {
$scope.curPage = 'page1';
$scope.pages = {
"page1": {
str: "page 1",
template:'\
<pp-group>\
<pp-row>\
<pp-label>Page:</pp-label><pp-value>1</pp-value>\
</pp-row>\
</pp-group>\
'},
"page2": {
str: "page 2",
template:'\
<pp-group>\
<pp-row>\
<pp-label>Page:</pp-label><pp-value>2</pp-value>\
</pp-row>\
</pp-group>\
'},
"page3": {
str: "page 3",
template:'\
<pp-group>\
<pp-row>\
<pp-label>Page:</pp-label><pp-value>3</pp-value>\
</pp-row>\
</pp-group>\
'}
};
$scope.pageList = ["page1","page2","page3"];
$scope.homePageList = ["page2", "page3"];
}]);
</script>
</body>
</html>
Looking at the generated DOM with the debugger, I find no tables, only text.
That is not possible with ng-bind-html because ng-sanitize does not compile the html. It can only be used for static content. You would need to create a directive yourself for this.
Something like this:
.directive('compileHtml', function($compile) {
return {
restrict: 'A',
link: function(scope, elm, attrs) {
var template = scope.$eval(attrs.compileHtml); //get the template by evaluating
elm.html(template); //set the html
$compile(elm.contents())(scope); //compile the contents
}
}
});
Also remember that you have child directives with no replace, which means you will be creating invalid html, table created by ppGroup will have ppRow as child which results in invalid html and it will push the directive content out of the table and compilation will not happen properly. So you would instead need to use the option replace:true on these directives.
However it will be really unsafe to compile the html dynamically though unless you know it is from a trusted source which is what $sce service used by ng-bind-html does. It sanitizes the boundhtml.
Demo
var pp = {
ctl: {}
};
pp.mod = angular.module('pp', ['ngSanitize']);
pp.mod.directive({
'ppHeader': function() {
return ({});
},
'ppGroup': function() {
return ({
template: '<table ng-transclude></table>',
transclude: true,
restrict: 'EA'
});
},
'ppRow': function() {
return ({
replace: true,
template: '<tr ng-transclude></tr>',
transclude: true,
restrict: 'EA'
});
},
'ppLabel': function() {
return ({
replace: true,
template: '<td ng-transclude></td>',
transclude: true,
restrict: 'EA'
});
},
'ppValue': function() {
return ({
replace: true,
template: '<td ng-transclude></td>',
transclude: true,
restrict: 'EA'
});
},
}).directive('compileHtml', function($compile) {
return {
restrict: 'A',
link: function(scope, elm, attrs) {
var template = scope.$eval(attrs.compileHtml);
elm.html(template);
$compile(elm.contents())(scope);
}
}
});
pp.ctl.main = pp.mod.controller('ppMain', ['$scope',
function($scope) {
$scope.curPage = 'page1';
$scope.pages = {
"page1": {
str: "page 1",
template: '\
<pp-group>\
<pp-row>\
<pp-label>Page:</pp-label><pp-value>1</pp-value>\
</pp-row>\
</pp-group>\
'
},
"page2": {
str: "page 2",
template: '\
<pp-group>\
<pp-row>\
<pp-label>Page:</pp-label><pp-value>2</pp-value>\
</pp-row>\
</pp-group>\
'
},
"page3": {
str: "page 3",
template: '\
<pp-group>\
<pp-row>\
<pp-label>Page:</pp-label><pp-value>3</pp-value>\
</pp-row>\
</pp-group>\
'
}
};
$scope.pageList = ["page1", "page2", "page3"];
$scope.homePageList = ["page2", "page3"];
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.3/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.3/angular-sanitize.min.js"></script>
<div ng-app="pp">
<div ng-controller="ppMain">
<ul class="menu">
<li ng-repeat="pageID in homePageList">{{pages[pageID].str}}</li>
</ul>
<div>
<div ng-repeat="pageID in pageList" compile-html="pages[pageID].template">
</div>
</div>
</div>
</div>

angular js directive attribute and controller scope

I would like to have a directive which creates a div as title and a ul list under it.
I want the title to be set from attribute and a the list by a controller.
Here is a fiddle of my code
HTML:
<div ng-app="myModule">
<my-list caption="My List" ng-controller="ListController"></my-list>
</div>
JavaScript:
angular.module('myModule', []).
controller('ListController', ['$scope', function ($scope) {
$scope.items = [{
caption: 'Item 1'
}, {
caption: 'Item 2'
}, {
caption: 'Item 3'
}];
}]).directive('myList', [function () {
return {
restrict: 'E',
template: '<div>' +
'<div style="font-weight:bold;">{{caption}}</div>' +
'<ul>' +
'<li ng-repeat="item in items">{{item.caption}}</li>' +
'</ul>' +
'</div>',
scope: {
caption: '#caption'
},
link: function (scope, element) {
element.find('li').on('click', function (evt) {
alert($(this).html());
});
}
}
}])
How can I solve this issue?
fixed few things in your controller.
moved controller to div
<div ng-app="myModule" ng-controller="ListController">
<my-list caption="My List" list="items"></my-list>
</div>
fixed directive to receive list as a parameter
directive('myList', [function () {
return {
restrict: 'E',
template: '<div>' +
'<div style="font-weight:bold;">{{caption}}</div>' +
'<ul>' +
'<li ng-repeat="item in items" ng-click="onClick(item)">{{item.caption}}</li>' +
'</ul>' +
'</div>',
scope: {
caption: '#caption', items: '=list'
},
link: function (scope, element) {
scope.onClick= function(item){console.log(item);}
}
}
}])
there is one doubt though that i have.
controller myList to is tried to view or to directive?? in case it is tied to directive then
angular.module('myModule', []).
controller('ListController', ['$scope', function ($scope) {
$scope.items = [{
caption: 'Item 1'
}, {
caption: 'Item 2'
}, {
caption: 'Item 3'
}];
}]).directive('myList', [function () {
return {
restrict: 'E',
template: '<div>' +
'<div style="font-weight:bold;">{{caption}}</div>' +
'<ul>' +
'<li ng-repeat="item in items" ng-click="onClick(item)">{{item.caption}}</li>' +
'</ul>' +
'</div>',
scope: {
caption: '#caption'
},
link: function (scope, element) {
scope.onClick= function(item){console.log(item);}
},
controller: 'ListController'
}
}])
The problem (as the error message indicates) is that you specify multiple directives which request for an isolated scope.
Actually, if you want to specify a controller for your directive, you use the controller property of the Directive Definition Object:
<my-list caption="My List"></my-list>
.directive('myList', [function () {
return {
...
controller: 'ListController',
...
Updated fiddle

Categories

Resources