angular directive to call scope method on click event - javascript

I have a grid with button that has k-grid-cancel-changes class. I would like to create a directive that will attach a click event to that button and call method on the page scope
.directive('kGridCancelChanges', function () {
return {
restrict: 'C',
scope: {
onCancelChanges: "&"
},
controller: function ($scope, $element, $attrs, $location) {
$element.click(function () {
$scope.onCancelChanges();
});
}
}
});
When I press button I can see $scope.onCancelChanges() fired from my directive but it never reaches function on the page scope.
$scope.onCancelChanges = function () {
alert('test');
}
I would appreciate any suggestions

If you want to call a function in the scope it has to be provided like this:
<button class="k-grid-cancel-changes" on-cancel-changes="onCancelChanges()">test</button>
Demo: http://plnkr.co/edit/8vQ1wmdriGrDFGZwhqW2?p=preview
If for some reason you can't modify HTML code (say, it's rendered dynamically by Kendo) and can't add attribute, then you can only access the function to call via $parent scope reference:
$scope.$parent.onCancelChanges();
Demo: http://plnkr.co/edit/tpEEZs9VQunKXABud9yN?p=preview
And finally, if it's not principal to have isolated scope for your directive then you can simply call the function as it's the same scope:
.directive('kGridCancelChanges', function() {
return {
restrict: 'C',
controller: function($scope, $element, $attrs, $location) {
$element.click(function() {
$scope.onCancelChanges();
});
}
}
});
Demo: http://plnkr.co/edit/0OmlCJ6SgYU2GQRyBgYj?p=preview

You can create you directive like this:
app.directive('myDir', function () {
return {
restrict: 'C',
scope: {
foo: '&'
},
link: function(scope,elem){
elem.on('click',function(){
scope.foo();
});
}};
});
or use controller function instead of link if you need:
controller: function($scope,$element){
$element.on('click',function(){
$scope.foo();
});
}};
Note that angular's jqLite has no element.click function.
Here is fiddle:
http://jsfiddle.net/cxo77xb4/2/

Related

How to communicate events between sibling component directives

angular JS $watch and communication between two directives
I Have a code in AngularJs where I'd like to call function in one directive when state of variable changes in other directive. I have a controller:
app.controller('TaskerCtrl', ['$scope', function($scope) {
$scope.tasksReload = false;
}
]);
Here as we can see is variable tasksReload. I'd like to call function in one of my directive when state of that variable changes on true in other directive.
Below I show code of my directives:
app.directive('newTaskWidget', function (TaskerForm, Consultants) {
return {
restrict: 'E',
scope: {
sortReverse: '=sortReverse',
tasksReload: '=tasksReload'
},
scope.test = function(){
scope.tasksReload = true;
}
app.directive('taskListWidget', function ($filter, $uibModal, Notification, TaskerForm, Consultants) {
return {
restrict: 'E',
scope: {
sortReverse: '=sortReverse',
departments: '=departmtens',
myDepartment: '=myDepartment',
tasksReload: '=tasksReload'
},
link: function (scope) {
scope.$watch('tasksReload', function (data) {
console.log("Musze przeladowac taski");
});
Below I show HTML code with my directives:
<new-task-widget sort-reverse="false" tasks-reload = 'tasksReload'>
</new-task-widget>
<task_list_widget sort-reverse="false" departmtens = "departments"
my-department="session.user.department" tasks-reload = 'tasksReload'>
</task_list_widget>
As we can see in newTaskWidget there is a function test. I'd like to call $watch action in taskListWidget when value scope.tasksReload = true; is been changed but it dosen't work correctly. I call that function with ng-click directive on button:
<button class="btn btn-primary validateButton" ng-click="test()">
</button>
There is no reaction. How could I do that properly? I would be grateful for help. Best regards ;)
One approach to communicating click events between sibling components is to use scope.$root.$broadcast:
scope.$root.$broadcast("test-event",args);
And in the sibling component, use scope.$on:
scope.$on("test-event", function(event,args) {
//Handle event here
});
The DEMO
angular.module("app",[])
.directive('newTaskWidget', function () {
return {
restrict: 'E',
scope: { },
template: `
<fieldset>
<button ng-click="test()">Click me</button>
</fieldset>
`,
link: function(scope,elem,attrs) {
scope.test = function(){
scope.$root.$broadcast("test-event");
};
}
};
})
.directive('taskListWidget', function () {
return {
restrict: 'E',
scope: {},
template: `<fieldset>clicks={{count}}</fieldset>`,
link: function (scope) {
scope.count=0;
scope.$on('test-event', function (event) {
scope.count++;
console.log("Musze przeladowac taski");
});
}
};
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app">
<new-task-widget>
</new-task-widget>
<task_list_widget>
</task_list_widget>
</body>

Query selector in nested AngularJS Directives

I'm using two directives in this HTML code:
<div animations>
<h2>Title</h2>
<span class="animateBtn">Animate!</span>
<info-section></info-section>
</div>
The first, is an Attribute Directive:
angular.module('app').directive('animations', ['$window', ($window: any) => {
return {
restrict: "A",
link: function ($scope: any, element: any, attrs: any) {
angular.element(document).ready(() => {
let animateBtns = angular.element(element[0].querySelector('.animateBtn'));
if (animateBtns && animateBtns.length) {
for (let i = 0, animateBtnsLength = animateBtns.length; i < animateBtnsLength; i++) {
let currentBtn = animateBtns[i];
currentBtn.addEventListener("click", function () {
.... other code....
});
}
}
..... other code .....
});
}
};
}])
So, it simply does a querySelector to select all buttons that, at the click, have to start a certain function.
And it works. The problem is that the second directive also contains an "animateBtn":
.directive('infoSection', function() {
return {
replace: true,
restrict: 'E',
template: '<div><span class="animateBtn">Animate with this too</span></div>'
}
});
The problem is that in the first directive (even if I user (document).ready()), the selector returns just one element (the span under the title), and it doesn't include the "animateBtn" of the second directive.
Here you can find the full code: PLNKR
With AngularJS one uses directives to attach code to elements instead of query selectors:
app.directive("animateBtn", function($window) {
return {
restrict: 'C',
link: postLink
};
function postLink (scope, elem, attrs) {
elem.on('click', function() {
.... other code....
});
.... other code....
}
})
The above directive will attach the click handler and associated code to each element with the class animateBtn when the element is added to the DOM by the AngularJS framework.
if a write a function inside "animations" directive, how can I trigger it inside "animatBtn" directive? I mean, in your code, inside the first line of "..... other code...." how can I call a function written in the "animations" directive?
Use the require property of the DDO to access the controller of the animations directive:
app.directive("animateBtn", function($window) {
return {
restrict: 'C',
link: postLink,
require: '^animations'
};
function postLink (scope, elem, attrs, animations) {
elem.on('click', function() {
.... other code....
});
//call animations API
animations.someMethod(args);
}
})
In the animations directive:
app.directive("animations", function() {
return {
controller: ctrl
}
function ctrl($element, $scope) {
this.someMethod = function(args) {
//code here
};
}
})
For more information, see AngularJS Comprehensive Directive API Reference - require

Using service inside directive?

I am learning how to create custom directives.
My service looks like that:
myApp.service('myService',function(){
this.myFunction=function(myParam){
// do something
}
});
Here is my directive:
myApp.directive('myDirective',function(myService){
return {
restrict: 'E',
scope: {
param: '=myParam',
},
template: '<button ng-click="myService.myFunction(param)">Do action</button>',
}
});
In HTML, when I use <my-directive my-param="something"></my-directive> it properly renders as a button. However when I click it, myService.myFunction, doesn't get executed.
I suppose I am doing something wrong. Can someone give me a direction?
I guess this has something to do with the directive's scope.
The service wont be available directly inside the template. You'll have to use a function attached to the directive's scope and call the service function from within this function.
myApp.directive('myDirective',function(myService){
return {
restrict: 'E',
scope: {
param: '=myParam',
},
template: '<button ng-click="callService(param)">Do action</button>',
link: function(scope, element, attrs) {
scope.callService = function() {
myService.myFunction();
}
}
}
});
It doesn't work because in your example a directive doesn't actually know what is myService. You have to explicitly inject it e.g.:
myApp.directive('myDirective', ['myService', function(myService){ ... }]);
See also this question or this question.
You should use a controller to do all DOM-modifications.
See this plunkr: https://plnkr.co/edit/HbfD1EzS0av5BG6NgtIv?p=preview
.directive('myFirstDirective', [function() {
return {
'restrict': 'E',
'controller': 'MyFirstController',
'controllerAs': 'myFirstCtrl',
'template': '<h1>First directive</h1><input type="text" ng-model="myFirstCtrl.value">'
};
}
You can inject the service in the controller and then call that function inside your template:
Inject myService into controller:
myApp.controller("ctrl", function($scope, myService) {
$scope.doService = function(myParam) {
return myService.myFunction(myParam);
};
});
Call doService method of the controller inside your template:
myApp.directive('myDirective',function(){
return {
restrict: 'E',
scope: {
param: '=myParam',
},
template: '<button ng-click="doService(param)">Do action</button>',
}
});

ng-click in angular directive - pass function from root scope

Fixed the issue, here is the final fiddle that shows it working:
http://jsfiddle.net/mbaranski/tfLeexdc/
I have a directive:
var StepFormDirective = function ($timeout, $sce, dataFactory, $rootScope) {
return {
replace: false,
restrict: 'AE',
scope: {
context: "=",
title: "="
},
template: '<h3>{{title}}</h3><form id="actionForm" class="step-form"></form><button ng-click="alert()" type="button">Save</button>',
link: function (scope, elem, attrs) {
}
}
}
How do I make the alert() do something from the controller?
Here is a fiddle:
http://jsfiddle.net/mbaranski/tfLeexdc/
Angular can be twitchy, so I've built a whole new fiddle to demonstrate all of the "glue-up" pieces you need to make this work.
First, you weren't passing the properties through to the directive, so I've made that adjustment:
// You have to pass the function in as an attribute
<hello-directive list="osList" func="myFunc()"></hello-directive>
Second, you were using onclick instead of ng-click in your template, which was part of the problem, so I made that switch:
// You need to use "ng-click" instead of "onclick"
template: '<h3>{{list}}</h3><button ng-click="func()" type="button">Button</button>',
And lastly, you need to bind the function in the scope of the directive, and then call it by the bound name:
scope: {
list: "=",
// Bind the function as a function to the attribute from the directive
func: "&"
},
Here's a Working Fiddle
All of this glued up together looks like this:
HTML
<div ng-controller="MyCtrl">
Hello, {{name}}!
<hello-directive list="osList" func="myFunc()"></hello-directive>
</div>
Javascript
var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.name = 'Angular Directive';
$scope.osList = "Original value";
$scope.stuffFromController = {};
$scope.myFunc = function(){ alert("Function in controller");};
};
var HelloDirective = function() {
return {
scope: {
list: "=",
func: "&"
}, // use a new isolated scope
restrict: 'AE',
replace: false,
template: '<h3>{{list}}</h3><button ng-click="func()" type="button">Button</button>',
link: function(scope, elem, attrs) {
}
};
};
myApp.directive("helloDirective", HelloDirective);
If you'd like to execute a function defined somewhere else, make sure you pass it in by the scope directive attribute.
Here you can do:
scope: {
context: '=',
title: '=',
alert='&' // '&' is for functions
}
In the place where you using the directive, you'll pass the "expression" of the function (meaning not just the function, but the actual invocation of the function you want to happen when the click occurs.
<step-form-directive alert="alert()" title=".." context=".."></step-form-directive>

AngularJS: Longpress directive not working with function in controller's view on nested directives

The onLongPress directive was working before I had to make an adjustment to my directives structures.
Now it's not working and I can't find why, perhaps someone can help me find my error.
The onLongPress should call a method from the controller assigned to the view, it doesn't, but if I add a $scope.someFn = function(); inside the directive, it works, but I need to call a method from the view's controller.
How can I solve this?
The example is the next:
<my-tabs>
<tab-one></tab-one>
<tab-two></tab-two>
...
</my-tabs>
Inside each of this tabs I have lists.
Example inside template:
<ul>
<li ng-click="doSomething()" on-long-press="doOtherthing()">
Some nice item.
</li>
</ul>
Directives are as follow:
tabOne
app.directive('tabOne', function() {
return {
restrict: 'E',
scope: false,
transclude: false,
templateUrl: 'templates/tab-one.html'
};
});
myTabs
app.directive('myTabs', function() {
return {
restrict: 'E',
templateUrl: 'templates/my-tabs.html',
scope: {
myMenues: '=',
selectedTab: '=',
general: '=',
userPoints: '=',
changetab: '&ngClick'
},
controller: function($scope) {
// Some code to add functionality. Nothing fancy.
}
};
});
onLongPress
app.directive('onLongPress', function($timeout) {
return {
restrict: 'A',
link: function($scope, $elm, $attrs) {
$elm.bind('touchstart', function(evt) {
// Locally scoped variable that will keep track of the long press
$scope.longPress = true;
// We'll set a timeout for 600 ms for a long press
$timeout(function() {
if ($scope.longPress) {
// If the touchend event hasn't fired,
// apply the function given in on the element's on-long-press attribute
$scope.$apply(function() {
$scope.$eval($attrs.onLongPress)
});
}
}, 600);
});
$elm.bind('touchend', function(evt) {
// Prevent the onLongPress event from firing
$scope.longPress = false;
// If there is an on-touch-end function attached to this element, apply it
if ($attrs.onTouchEnd) {
$scope.$apply(function() {
$scope.$eval($attrs.onTouchEnd)
});
}
});
}
};
});
UPDATE:
If I add $scope.doOtherthing = function() {}; into myTabs directive it works, but as I said, I need to get that function from the view's controller :/
Thanks.

Categories