angular js directive attribute and controller scope - javascript

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

Related

Year Only in ExtJs, overriding css of extjs

I need a date picker which let me choose 'year' only in extjs.I already have multiple datepicker in my application, but for this i need to choose year only. Is there any way i can override css of datepicker(Extjs have component which shows month and year but not year only). I don't want to define custom component, i want to use datepicker by overriding it's css. I found one custom css example over stackoverflow.Please check it out here.
Ext.onReady(function() {
Ext.define('Ext.ux.OnlyYearPicker', {
xtype: 'onlyyearpicker',
extend: 'Ext.picker.Month',
afterRender: function(){
this.callParent();
this.el.setStyle({width: '106px',})
},
renderTpl: [
'<div id="{id}-bodyEl" data-ref="bodyEl" class="{baseCls}-body">',
'<div style="display: none; width:0px;" id="{id}-monthEl" data-ref="monthEl" class="{baseCls}-months">',
'<tpl for="months">',
'<div class="{parent.baseCls}-item {parent.baseCls}-month">',
'<a style="{parent.monthStyle}" role="button" hidefocus="on" class="{parent.baseCls}-item-inner">{.}</a>',
'</div>',
'</tpl>',
'</div>',
'<div id="{id}-yearEl" data-ref="yearEl" class="{baseCls}-years">',
'<div class="{baseCls}-yearnav">',
'<div class="{baseCls}-yearnav-button-ct">',
'<a id="{id}-prevEl" data-ref="prevEl" class="{baseCls}-yearnav-button {baseCls}-yearnav-prev" hidefocus="on" role="button"></a>',
'</div>',
'<div class="{baseCls}-yearnav-button-ct">',
'<a id="{id}-nextEl" data-ref="nextEl" class="{baseCls}-yearnav-button {baseCls}-yearnav-next" hidefocus="on" role="button"></a>',
'</div>',
'</div>',
'<tpl for="years">',
'<div class="{parent.baseCls}-item {parent.baseCls}-year">',
'<a hidefocus="on" class="{parent.baseCls}-item-inner" role="button">{.}</a>',
'</div>',
'</tpl>',
'</div>',
'<div class="' + Ext.baseCSSPrefix + 'clear"></div>',
'<tpl if="showButtons">',
'<div class="{baseCls}-buttons">{%',
'var me=values.$comp, okBtn=me.okBtn, cancelBtn=me.cancelBtn;',
'okBtn.ownerLayout = cancelBtn.ownerLayout = me.componentLayout;',
'okBtn.ownerCt = cancelBtn.ownerCt = me;',
'Ext.DomHelper.generateMarkup(okBtn.getRenderTree(), out);',
'Ext.DomHelper.generateMarkup(cancelBtn.getRenderTree(), out);',
'%}</div>',
'</tpl>',
'</div>'
]
});
Ext.define('Ext.form.field.Month', {
extend: 'Ext.form.field.Date',
alias: 'widget.monthfield',
requires: ['Ext.picker.Month', 'Ext.ux.OnlyYearPicker'],
alternateClassName: ['Ext.form.MonthField', 'Ext.form.Month'],
selectMonth: null,
createPicker: function() {
var me = this,
format = Ext.String.format;
return Ext.create('Ext.ux.OnlyYearPicker', {
pickerField: me,
ownerCt: me.ownerCt,
renderTo: document.body,
floating: true,
hidden: true,
focusOnShow: true,
minDate: me.minValue,
maxDate: me.maxValue,
disabledDatesRE: me.disabledDatesRE,
disabledDatesText: me.disabledDatesText,
disabledDays: me.disabledDays,
disabledDaysText: me.disabledDaysText,
format: me.format,
showToday: me.showToday,
startDay: me.startDay,
minText: format(me.minText, me.formatDate(me.minValue)),
maxText: format(me.maxText, me.formatDate(me.maxValue)),
listeners: {
select: {
scope: me,
fn: me.onSelect
},
monthdblclick: {
scope: me,
fn: me.onOKClick
},
yeardblclick: {
scope: me,
fn: me.onOKClick
},
OkClick: {
scope: me,
fn: me.onOKClick
},
CancelClick: {
scope: me,
fn: me.onCancelClick
}
},
keyNavConfig: {
esc: function() {
me.collapse();
}
}
});
},
onCancelClick: function() {
var me = this;
me.selectMonth = null;
me.collapse();
},
onOKClick: function() {
var me = this;
if (me.selectMonth) {
me.setValue(me.selectMonth);
me.fireEvent('select', me, me.selectMonth);
}
me.collapse();
},
onSelect: function(m, d) {
var me = this;
me.selectMonth = new Date((d[0] + 1) + '/1/' + d[1]);
}
});
Ext.create('Ext.form.field.Month', {
format: 'Y',
fieldLabel: 'Date',
renderTo: Ext.getBody()
});
});

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: '='
},

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>

AngularJS : ng-repeat not executing inside scoped directive

Run into an issue where ng-repeat doesn't seem to be executing inside a custom directive. I think the problem is something to do with the transclusion/scope but I'm not really sure.
directive:
.directive('node', ['$compile', function($compile) {
return {
restrict: 'E',
scope: {
obj: '=obj',
},
transclude: true,
link: function(scope, element, attrs, ctrl, transclude) {
transclude(scope, function(clone, scope) {
console.log(clone);
var tmp = angular.element('<div></div>'), template;
tmp.append(clone);
if(scope.obj.hasSub) {
template =
'<div>' +
'<span>{{obj.name}}</span>' +
'<ul>' +
'<li ng-repeat="child in obj.children">' +
'<node data-obj="child" >' + tmp.html() + '</browser-node>' +
'</li>' +
'</ul>' +
'</div>';
} else {
template =
'<div>' +
tmp.html() +
'</div>'
}
element.html('').append($compile(template)(scope));
});
}
};
}]);
controller:
.controller('Main', ['$scope', function($scope) {
$scope.items = [
{'name': 'one', hasSub: true, children: [
{ 'name': 'one-one', items: { foo: 3, bar: 2 } }
] },
{'name': 'two', hasSub: true, children: [
{ 'name': 'two-one', items: { foo: 6, bar: 5 } }
] }
];
}]);
html:
<ul>
<li ng-repeat="item in items">
<node obj="item">
{{obj.name}}
<ul>
<li ng-repeat="(k,v) in obj.items">{{k}}: {{v}}</li>
</ul>
</node>
</ul>
The name of the item node displays fine, and {{obj.items}} will output the correct contents, but the ng-repeat seems to do nothing.
Plunker
There is no property items in the first level of your data. If you nest the repeater for items in your template and change to child.items it works fine
template =
'<div>' +
'<span>{{obj.name}}</span>' +
'<ul>' +
'<li ng-repeat="child in obj.children">' +
'<node data-obj="child" >' + tmp.html() + '</node>' +
'<ul>'+
'<li ng-repeat="(k,v) in child.items">{{k}}: {{v}}</li>'+
'</ul>'+
'</li>' +
'</ul>' +
'</div>';
DEMO

Passing an object to a directive as an argument

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

Categories

Resources