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>
Related
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>
is it possible with this example from Ben Foster to get all nodes closed by default (at the loading of the page) ? and to open each node with ng-click ?
http://jsfiddle.net/benfosterdev/NP7P5/
I have found a way to get selected node but I don't know how to combine it with ng-click and eventually ng-show or ng-hide:
ng-click='nodeSelected($event, category)'
and in controller
$scope.nodeSelected = function($event, category){
$event.stopPropagation();
console.log('This node is selected' + category);
}
Just found a similar example :
var gyroEditor = angular.module('gyroEditor', []);
gyroEditor.controller('Ctrl', function($scope) {
$scope.nodes = [
{
title: 'Computers',
categories: [
{
title: 'Laptops',
categories: [
{
title: 'Ultrabooks'
},
{
title: 'Macbooks'
}
]
},
{
title: 'Desktops'
},
{
title: 'Tablets',
categories: [
{
title: 'Apple'
},
{
title: 'Android'
}
]
}
]
},
{
title: 'Printers'
}
];
});
gyroEditor.directive('tree', function() {
return {
restrict: 'E',
replace: true,
scope: {nodes: '=nodes'},
templateUrl: 'tree.html',
controller: function($scope) {
console.log('tree ctrl');
}
};
});
gyroEditor.directive('treenode', function() {
return {
restrict: 'E',
replace: true,
scope: {node:'=node'},
templateUrl: 'treenode.html',
controller: function($scope) {
console.log('node ctrl');
}
};
});
gyroEditor.directive("recursive", function($compile) {
return {
restrict: "EACM",
priority: 100000,
compile: function(tElement, tAttr) {
var contents = tElement.contents().remove();
var compiledContents;
return function(scope, iElement, iAttr) {
if(!compiledContents) {
compiledContents = $compile(contents);
}
iElement.append(
compiledContents(scope,
function(clone) {
return clone; }));
};
}
};
});
.panel-left {
float: left;
width: 200px;
margin: 0 20px 20px 0;
}
.panel-editors {
float: right;
height: 100%;
width: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<div ng-app=gyroEditor ng-controller=Ctrl>
<script type="text/ng-template" id="treenode.html">
<li ng-init="collapsed=true">
<a ng-click="collapsed=!collapsed"><i class="fa fa-{{((collapsed || !node.categories) ? '' : '-open')}}"></i> {{node.title}}</a>
<ol ng-if="!collapsed && node.categories && node.categories.length">
<recursive>
<treenode ng-repeat="c in node.categories" node=c>
</treenode>
</recursive>
</ol>
</li>
</script>
<script type="text/ng-template" id="tree.html">
<ol>
<treenode ng-repeat="n in nodes" node=n></treenode>
</ol>
</script>
<div class=panel-left>
<tree nodes=nodes></tree>
</div>
</div>
{{node.title}}
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: '='
},
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
I have a view and a controller for it:
<app-head options="options"></app-head>
app.controller('homeCtrl',function($scope){
$scope.options = {
first: {
description: "A link",
link: "../"
},
second:{
description: "Another link",
link: "../../"
}
};
});
I want to use the object options within my directive:
app.directive('appHead',function(){
return{
restrict: 'E',
templateUrl: 'partials/modules/appHead.html',
replace: true,
controller: function($scope,$element,$attrs){
$scope.options = $attrs.options;
}
}
});
When i call console.log($scope.options) in my directive's controller however, it will return options
No Properties
Why is that and how do i accomplish this?
Try this
app.directive('appHead',function(){
return{
scope: {
options: '=',
},
restrict: 'E',
templateUrl: 'partials/modules/appHead.html',
replace: true,
link: function($scope,$element,$attrs){
// $scope.options
}
}
});
Working Demo