How to get access for function in controller from directive template - angularjs

Example:
HTML:
<div ng-repeat="notification in vm.notifications" class="notifications">
<notification notification="notification"></notification>
</div>
Directive notification is replaced other directives:
app.directive('notification', function($compile){
return {
restrict: 'E',
transclude: true,
scope: {
notification: "=notification"
},
link: function(scope, element) {
var temp = "<notifications" + scope.notification.type.normalCase() + ">";
element.replaceWith($compile(temp)(scope));
}
}
});
For example a few directives:
app.directive('notificationsStudentRequest', function(){
return {
template: '<div class="alert alert-info alert-dismissible"><button type="button" class="close" ng-click="vm.deleteNotification(notification)"><span aria-hidden="true">×</span></button>Запрос на участие в курсе: "{{notification.payload.course.name}}"</div>',
restrict: 'E',
replace: true,
transclude: true
}
});
app.directive('notificationsStudentRequestAccepted', function(){
return {
template: '<div class="alert alert-success alert-dismissible"><button type="button" class="close" ng-click="vm.deleteNotification(notification)"><span aria-hidden="true">×</span></button>[{{ notification.createdAt | date:"medium"}}] Запрос на участие в курсе: "{{notification.payload.course.name}}" был принят</div>',
restrict: 'E',
replace: true,
transclude: true
}
});
And in controller I have a fuction vm.deleteNotification(value)
app.controller('NotificationsController', function(){
var vm = this;
vm.notifications = {
//some object with notifications
}
vm.deleteNotification = function(notification){
vm.notifications.splice(vm.notifications.indexOf(notification), 1);
}
return vm;
});
ng-click in template don't see this fuction. This problem can fix $parent.vm.del......., but I need other solutions.

Move your notification list and management functions into a service. Inject that service into your directives and controller.
app.service('NotificationManager', function(){
var self = this;
angular.extend(self, {
notifications: {},
deleteNotification: function(notification){
self.notifications.splice(self.notifications.indexOf(notification), 1);
};
});
});
app.directive('notificationsStudentRequest', function(){
return {
template: '<div class="alert alert-info alert-dismissible"><button type="button" class="close" ng-click="dvm.deleteNotification(notification)"><span aria-hidden="true">×</span></button>Запрос на участие в курсе: "{{notification.payload.course.name}}"</div>',
restrict: 'E',
replace: true,
transclude: true,
bindToController: true,
controllerAs: 'dvm'
controller: function(NotificationManager){
this.deleteNotification = function(n){ NotificationManager.deleteNotification(n); }
}
}
});
app.directive('notificationsStudentRequestAccepted', function(NotificationManager){
return {
template: '<div class="alert alert-success alert-dismissible"><button type="button" class="close" ng-click="dvm.deleteNotification(notification)"><span aria-hidden="true">×</span></button>[{{ notification.createdAt | date:"medium"}}] Запрос на участие в курсе: "{{notification.payload.course.name}}" был принят</div>',
restrict: 'E',
replace: true,
transclude: true,
bindToController: true,
controllerAs: 'dvm'
controller: function(NotificationManager){
this.deleteNotification = function(n){ NotificationManager.deleteNotification(n); }
}
}
});
app.controller('NotificationsController', function(NotificationManager){
var vm = this;
vm.notifications = NotificationManager.notifications;
return vm;
});

Try nesting the deleteNotification function within an object -- I've explained a bit more here.
Perhaps something like this, should give you access to that function.
app.controller('NotificationsController', function(){
var vm = this;
vm.someObject = {};
//some code
vm.someObject.deleteNotification = function(notification){
vm.notifications.splice(vm.notifications.indexOf(notification), 1);
}
return vm;
});

Related

Angular js : How i can change the color of accordion active on click

How can I change the color of an active accordion when I click on the glyphicon using ng-if. I have this code: I can't succeeded to do that
have you an idea please. Thanks an advance.
style.css:
selectionAccordion {
&:hover, &:focus {
color: White;
background-color:Blue;
}
}
.directive('accordion', function () {
return {
restrict: 'EA',
replace: true,
transclude: true,
template: '<div data-ng-transclude=""></div>',
controller: function () {
var Spots = [];
this.Open = function (selected_Spot) {
angular.forEach(Spots, function (Spot) {
if (selected_Spot != Spot)
Spot.showMe = false;
});
};
this.addSpot = function (Spot) {
Spots.push(Spot);
};
}
};
})
.directive('spotcam', function () {
return {
restrict: 'EA',
replace: true,
transclude: true,
require: '^accordion',
scope: { title: '#' },
template: '<div>' +
'<div class="title selectedAccordionStyle ng-if="true"><a class=" more-less glyphicon glyphicon-plus" ng-class="{true: \'glyphicon glyphicon-minus\', false:\'glyphicon glyphicon-plus\'}[showMe]" data-ng-click="toggle()"></a>{{title}}</div>' +
'<div class="body" data-ng-show="showMe" data-ng-transclude=""></div>' +
'</div>',
link: function (scope, element, attrs, accordionController)
scope.showMe = false;
accordionController.addSpot(scope);
scope.toggle = function () {
scope.showMe = !scope.showMe;
accordionController.Open(scope);
} } }
});
thanks an advance
You could use the same condition as on the nested a-tag which checks if the accordion is selected by the value showMe.
template: '<div>' +
'<div class="title" ng-class="{true: \'selectedAccordionStyle\', false:\'\'}[showMe]"><a class=" more-less glyphicon glyphicon-plus" ng-class="{true: \'glyphicon glyphicon-minus\', false:\'glyphicon glyphicon-plus\'}[showMe]" data-ng-click="toggle()"></a>{{title}}</div>' +
'<div class="body" data-ng-show="showMe" data-ng-transclude=""></div>' +
'</div>',

AngularJS $emit.$on query

Plunker is here:-
http://plnkr.co/edit/JaEi7ftnokYhdvBn4fRh?p=preview
I have emitted data and its available in $on() under data.name.
However, I am not able to display $scope.dumbevent1_name in UI using {{ dumbevent1_name }}
What am I doing wrong ?
Code:-
// Code goes here
angular.module('myapp', [])
.controller('mycontroller', mycontroller)
.component('semantic3', { // semantic3
restrict: 'E',
transclude: true,
template: `<h6> imsemantic3</h6> <div ng-transclude></div>
`,
controller: semantic3Controller,
})
.component('semantic2', { // semantic2
scope: {},
restrict: 'E',
transclude: true,
template: `<h6> imsemantic2</h6> <div ng-transclude></div>
`,
})
.component('semantic1', { // semantic1
scope: {},
restrict: 'E',
transclude: true,
template: `<h6> imsemantic1</h6> <div ng-transclude></div>
`,
})
.component('dumbCompDisplayNames', {
scope:true,
restrict: 'E',
template: `<h6>h6dumb1</h6> {{$ctrl.names}}`,
bindings:{
names : '<' //one-way data binding
},
controller: dumbController,
});
function semantic3Controller($scope, $element, $attrs){
var self = $scope;
$scope.$on('dumbevent1', function(event, data){
console.log(data.name); //works. received.
self.dname = data.name;
$scope.dumbevent1_name = data.name; //works
// console.log($scope.dumbevent1_name); //works
// capturename( data); //works
});
// console.log($scope.dumbevent1_name); //doesnt work.
// function capturename(x){
// $scope.dumbevent1_name = x.name;
// console.log($scope.dumbevent1_name); //works
// }
}
function dumbController($scope, $element, $attrs){
// console.log($scope); //works. these r NOT positional injection
// console.log($element);
// console.log($attrs);
$scope.$emit('dumbevent1', {name: 'namedumb1'});
}
// main controller
function mycontroller(){
console.log('i am mycontroller');
}
The time wasted on commenting repeatedly is worthwhile somehow?? If don't have time to look at the plunker don't waste time commenting. Like I said I fixed the issue.

Two way binding with controller in directive with isolated scope

Im developing a directive, where when the user will click UPDATE, a field for EmployeeId will show up, and that employeeId field on the directive should be bound directly to the controller when changes. If i isolate the scope it doesn't change the controller employeeId, if i dont, than all directives gets toggled.
Plunker: https://plnkr.co/edit/aq06WnfniN51iR7ZtFLc?p=preview
code:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.employeeId = 'Josh'
$scope.employeePin = '1234'
$scope.updateFields = function() {
alert(1);
}
});
app.directive('crudEmp', function($parse) {
var html = '<div class="form-inline">\
<input type="text" class="form-control"\
placeholder="Employe ID"\
ng-model="employeeId"\
required\
ng-show="isEditing"/>\
<input type="password" class="form-control"\
placeholder="Employe PIN"\
ng-model="employeePin"\
ng-show="user.AdminSecurity && isEditing"\
ng-required="user.AdminSecurity"\
disabled/>\
<div class="col-md-12 text-right noPadding pt10 operationButtons" id="opBtn">\
<div class="col-md-12 noPadding" ng-hide="isEditing">\
<button class="btn btn-info" ng-click="toggleEditing()">Edit</button>\
</div>\
<div class="col-md-12 text-right noPadding" ng-hide="!isEditing">\
<button class="btn btn-success mr80 updateBtn" ng-click="ctrl.onClick(); toggleEditing()" ng-disabled="ctrl.valid">Update</button>\
<button class="btn btn-warning" ng-click="toggleEditing()">Cancel</button>\
</div>\
</div>';
return {
restrict: 'E',
template: html,
replace: true,
bindToController: {
employeeId: '=',
employeePin: '=',
valid: '=',
onClick: '&',
},
// scope: true, // When i make the scope true, it doesnt change the EmployeeId defined in the controller
link: function(scope, element, attr){
scope.onClick = attr.onClick;
scope.toggleEditing = function() {
scope.isEditing = !scope.isEditing;
if(scope.isEditing){
element.closest('form').find(':input:not(.operationButtons button)').attr('disabled', false);
var getAllInputs = element.closest('form').find(':input').filter(':visible:enabled');
if(getAllInputs[0]){
getAllInputs[0].focus();
}
}else{
element.closest('form').find(':input:not(.operationButtons button)').attr('disabled', true);
}
};
},
controllerAs: 'ctrl',
controller: function($scope) {
var ctrl = this;
ctrl.onClick = function() {
$scope.$eval($scope.onClick);
};
},
};
});
If I understand you currently it would be solved by changing:
...
restrict: 'E',
template: html,
replace: true,
bindToController: {
employeeId: '=',
employeePin: '=',
valid: '=',
onClick: '&',
},
// scope: true, // When i make the scope true, it doesnt change
To:
...
restrict: 'E',
template: html,
replace: true,
scope: {
employeeId: '=',
employeePin: '=',
valid: '=',
onClick: '&',
},
...
You can verify the behaviour in my plunker: https://plnkr.co/edit/Xia8BhlfFJ6mqgLjaoOp?p=preview

Communicate between custom directives in angularJS

I'm relatively new to AngularJS and working on creating tabs in a page. Till now I have resolved my problems with angularjs by searching a lot on internet but I can't resolve this. Hope anyone can help me with ideas and better knowledge of angularjs.
I have two custom directives tabset and tab. 'Tabset' is the directive to maintain the tabs and 'tab' is for a single tab.
app.directive('tabset', function() {
return {
restrict: 'E',
transclude: true,
templateUrl: 'tabset.html',
bindToController: true,
scope: {},
controller: function($scope){
$scope.tabs = [];
this.addTab = function(tab) {
$scope.tabs.push(tab);
}
console.log("In tabset controller");
},
link : function(scope){
console.log("In the tabset link");
}
}
});
//Custom Directive for the tab controls
app.directive('tab', function() {
return {
restrict: 'E',
transclude: true,
template: '<h2>Welcome to Stackoverflow</h2> <div role="tabpanel" ng-transclude></div>',
require : '^tabset',
scope: {},
link : function(scope, elem, attr, tabsetCntrl) {
tabsetCntrl.addTab(scope);
console.log("In the tab link");
}
}
});
I call these directives in my HTML page as shown below:
<tabset>
<tab>
This is one tab
</tab>
<tab>
This is another tab
</tab>
</tabset>
But, when I run the code, the link function of the tab directive is not running. The 'require : ^tabset' option gets the controller from the tabset, but the link function of the tab directive is not working.
Try adding controllerAs: '$ctrl' to your tabset directive.
Like:
angular.module('app').directive('tabset', function() {
return {
restrict: 'E',
transclude: true,
templateUrl: 'tabset.html',
bindToController: true,
controllerAs: '$ctrl', // <---- HERE
scope: {},
controller: function($scope){
$scope.tabs = [];
this.addTab = function(tab) {
$scope.tabs.push(tab);
}
console.log("In tabset controller");
},
link : function(scope){
console.log("In the tabset link");
}
}
});
Tested
Further info found by checking the error seen in console here
Prudhvee, take a look at this demo i did to understand the making of angular tabs using nested directives.
app.directive('tabset', function() {
return {
restrict: 'E',
transclude: true,
scope: {},
controller: [ "$scope", function($scope) {
var panes = $scope.panes = [];
$scope.select = function(pane) {
angular.forEach(panes, function(pane) {
pane.selected = false;
});
pane.selected = true;
}
this.addPane = function(pane) {
if (panes.length == 0) $scope.select(pane);
panes.push(pane);
}
}],
template:
'<div class="tabbable">' +
'<ul class="nav nav-tabs">' +
'<li ng-repeat="pane in panes" ng-class="{active:pane.selected}">'+
'{{pane.title}}' +
'</li>' +
'</ul>' +
'<div class="tab-content" ng-transclude></div>' +
'</div>',
replace: true
};
});
app.directive('tab', function() {
return {
require: '^tabset',
restrict: 'E',
transclude: true,
scope: { title: '#' },
link: function(scope, element, attrs, tabsCtrl) {
tabsCtrl.addPane(scope);
},
template:
'<div class="tab-pane" ng-class="{active: selected}" ng-transclude>' +
'</div>',
replace: true
};
})
http://plnkr.co/edit/BJWWw2?p=preview

Dynamic template in AngularJS Directive (Not templatUrl)

I have seen the ability to have a dynamic templateUrl within a Directive in Angular but while researching I have yet to see a dynamic template.
.directive('col', function () {
var template = '<div class="one" ng-transclude></div>';
return {
restrict: 'E',
scope: {},
transclude: true,
replace: true,
template: template,
link: function (scope, ele, attrs) {
if (attrs.two !== undefined) {
template = '<div class="two" ng-transclude></div>';
} else if (attrs.three !== undefined) {
template = '<div class="three" ng-transclude></div>';
} else {
template = '<div class="one" ng-transclude></div>';
}
console.log(template);
}
};
})
HTML:
<col three>Three</col>
<col two>Two</col>
<col>Nothing</col>
The console shows appropriately:
<div class="three" ng-transclude></div>
<div class="two" ng-transclude></div>
<div class="one" ng-transclude></div>
However the output shows the default / initial <div class="one" ng-transclude></div>
This is because the template is collected before the link function is fired, if your only trying to change the classes then just do something like this.
.directive('col', function () {
var template = '<div class="{{class}}" ng-transclude></div>';
return {
restrict: 'E',
scope: {},
transclude: true,
replace: true,
template: template,
link: function (scope, ele, attrs) {
$scope.class = attrs.three ? 'three' : attrs.two ?'two' : attrs.one ? 'one' : '';
}
};
});
Other than that I don't know how else you would accomplish it.
When you say 'template = 'xxx' in your link function it is referencing your template variable, not the property on the returned object. Try this:
.directive('col', function () {
var result = {
restrict: 'E',
scope: {},
transclude: true,
replace: true,
template: template,
link: function (scope, ele, attrs) {
if (attrs.two !== undefined) {
result.template = '<div class="two" ng-transclude></div>';
} else if (attrs.three !== undefined) {
result.template = '<div class="three" ng-transclude></div>';
} else {
result.template = '<div class="one" ng-transclude></div>';
}
console.log(result.template);
}
};
return result;
})
If the template is dynamically constructed based on the attributes then you can just supply a template function passing in the attributes
.directive('col', function () {
return {
restrict: 'E',
scope: {},
transclude: true,
replace: true,
template: function(element, attrs) {
var template = null;
if (attrs.two !== undefined) {
template = '<div class="two" ng-transclude></div>';
} else if (attrs.three !== undefined) {
template = '<div class="three" ng-transclude></div>';
} else {
template = '<div class="one" ng-transclude></div>';
}
return template;
},
link: function (scope, element, attrs) {
}
};
})
If the template is dynamically constructed based on the model then it's bit more involved. Note that the transclusion is done explicitly rather than using the ng-transclude directive.
.directive('col', function () {
return {
restrict: 'E',
scope: {
myVariable: '='
},
transclude: true,
replace: true,
link: function (scope, element, attrs, nullController, transclude) {
var template = null;
if (scope.myVariable == 'two') {
template = '<div class="two"></div>';
} else if (scope.myVariable == 'three') {
template = '<div class="three"></div>';
} else {
template = '<div class="one"></div>';
}
element.html(template);
$compile(element.contents())(scope);
transclude(scope.$parent, function(clone, scope) {
element.children().append(clone);
});
}
};
})

Resources