How to bind a directive to a controller via a service update ?
I want to create the possibility to update a cart(the service) via a directive(add to cart button) and then the controller (that display the cart) will update its view.
Despite the fact that I added a watch on the service itself my controller is not been updated.
Of course it will be good if the controller and the directive doesn't share the same scope (transclude: true in the directive)
The service:
angular.module('stamModule', [])
.factory('valueService', function () {
var factory = {
data: {value: 1000},
inc: inc,
getData: getData
};
function inc() {
this.data.value++;
}
function getData() {
return this.data;
}
return factory;
})
the directive:
.directive('buttonDirective', function (valueService) {
var directive = {
restrict: 'E',
template: '<button>Inc</button>',
link: linkFnc
};
function linkFnc(scope, el) {
el.on('click', function () {
valueService.inc();
});
}
return directive;
})
The controller:
.controller('FirstController', function ($scope, valueService) {
var vm = this;
vm.serv = valueService;
$scope.$watch('vm.serv.getData()', function (newValue) {
console.log("watch");
console.log(newValue);
});
})
The html:
<body ng-app="stamModule">
<hr>
<div ng-controller="FirstController as vm">
<p>{{vm.serv.data}}</p>
<button-directive ></button-directive>
</div>
here's a demo:
https://jsfiddle.net/07tp4d03/1/
Thanks
All your code needed was a little push. No need for event broadcasting or anything like that.
The problem was, that the click event listener was working outside Angular's digest loop, and thus Angular watch wasn't working for you.
If you change your directive's code to the following, it will work.
.directive('buttonDirective', function (valueService) {
var directive = {
restrict: 'E',
template: '<button ng-click="inc()">Inc</button>',
link: linkFnc
};
function linkFnc(scope) {
scope.inc = function() {
valueService.inc();
};
}
return directive;
})
Here is a fork of your fiddle that works
Related
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>
I have several hierarchical directives and in one, I need to have some functions in its controller, so that the child elements can interact with it. But this one directive also needs to reference its parent directive's controller, but I don't know how to do that in controller (I know how in the "link()" but this time I need controller for the child interaction). It should be possible to do it with scope:
controller: function($scope){},
link: function (scope, ..., parentCtrl){
scope.parentCtrl = parentCtrl;
}
but it seems weird, because the link function is executed after the controller is, or it it OK? I'm confused and I think it might be a bad design?
diagram:
ParentParentDirective
controller: function(service){
this.service = service;
}
ParentDirective
controller: function(){
this.callOnService = function(id){
???ParentParentDirective???.service.callSth(id);
}
}
ChildDirective
link(scope, ...,ParentDirectiveCtrl){
scope.makeChanges = ParentDirectiveCtrl.callOnService;
}
You can use $element.controller method for that, as in example below.
angular.module('app', []);
angular.module('app').directive('grandparent', function () {
return {
restrict: 'E',
controller: function () {
this.go = function () {
console.log('Grandparent directive');
};
}
};
});
angular.module('app').directive('parent', function () {
return {
restrict: 'E',
controller: function () {
this.go = function () {
console.log('Parent directive');
};
}
};
});
angular.module('app').directive('child', function () {
return {
restrict: 'E',
require: ['^parent', '^grandparent'],
controller: function ($element) {
var parentCtrl = $element.controller('parent');
var grandparentCtrl = $element.controller('grandparent');
parentCtrl.go();
grandparentCtrl.go();
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js"></script>
<div ng-app="app">
<grandparent>
<parent>
<child></child>
</parent>
</grandparent>
</div>
When looking for information regarding Angular directives and passing behavior to directives, I get ended up being pointed in the direction of method binding on an isolate scope, i.e.
scope: {
something: '&'
}
The documentation for this functionality is a bit confusing, and I don't think it'll end up doing what I want.
I ended up coming up with this snippet (simplified for brevity), that works by passing a scope function in HomeCtrl, and the directive does it's work and calls the function. (Just incase it matters, the real code passes back a promise from the directive).
angular.module('app', []);
angular.module('app')
.directive('passingFunction',
function() {
var changeFn,
bump = function() {
console.log('bump() called');
internalValue++;
(changeFn || Function.prototype)(internalValue);
},
internalValue = 42;
return {
template: '<button ng-click="bump()">Click me!</button>',
scope: {
onChange: '<'
},
link: function(scope, element, attrs) {
if (angular.isFunction(scope.onChange)) {
changeFn = scope.onChange;
}
scope.bump = bump;
}
};
})
.controller('HomeCtrl',
function($scope) {
$scope.receive = function(value) {
console.log('receive() called');
$scope.receivedData = value;
};
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.4/angular.min.js"></script>
<div ng-app="app" ng-controller="HomeCtrl">
<passing-function on-change="receive"></passing-function>
<p>Data from directive: {{receivedData}}</p>
</div>
Is this a proper "Angular" way of achieving this? This seems to work.
What you need is to pass the function to the directive. I'll make a very small example.
On controller:
$scope.thisFn = thisFn(data) { console.log(data); };
In html:
<my-directive passed-fn="thisFn()"></my-directive>
On directive:
.directive('myDirective', [
() => {
return {
restrict: 'E',
scope: {
passFn: '&'
},
template: '<div id="myDiv" ng-click="passFn(data)"></div>',
link: (scope) => {
scope.data = "test";
}
}
}]);
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/
How to get some data from controller and use it inside directive thats not a problem.
But I stack with such situation when I need get data from directive and use it in my controller.
For exmpl:
My controller:
function MyController($scope, $location, myDirective) {
"use strict";
// here i need use scope.importantValue and create() method from directive
}
My directive:
.directive("myDirective", function() {
"use strict";
return {
restrict: 'A',
template: '<div></div>',
replace: true,
scope: {
data: '=',
},
link: function(scope, elm) {
scope.importantValue = "value";
function create() {
console.log("Directive works...");
}
};
})
How I can use variables or/and methods from directive inside my controller?
The simplest way to accomplish this is to make both your controller and directive get importantValue and create() from a service.
angular.module(/* Your module */).service('sharedData', function () {
return {
importantValue: "value",
create: function () {
console.log("Directive works...");
}
};
});
Now you can inject sharedData into your directive and controller and access importantValue and create() from either place.