Reputation: 619
I have a factory that I am passing into a directive. There is some data within the factory that gets updated via a function:
app.factory('CallsDataService', function() {
var factory = {};
//Contacts Handled data
var data = [
{
"id": "0",
"handle": "A",
},
{
"id": "1",
"handle": "B",
}
];
factory.getData = function(){
return data;
};
factory.updateData = function(value){
data[0].handle= value);
};
..............
In the directive it gets passed in as a dependancy:
app.directive('pieChart', ['CallsDataService', function(CallsDataService) {
return {
// use a new isolated scope
restrict: 'AE',
replace: 'true',
scope:{
templateUrl: "@"
},
link: function(scope,element,attrs){
//Draw initial chart with default data
drawChart();
function drawChart() {
}
....
My question is: if the data in the factory gets updated in factory via a call to the factory.updateData function in some other part of the code how can I detect this in the directive? I assume there has to be some use of $broadcast or $watch but I have had no success with this so far (needless to say I am new to Angular)
Any pointers would be appreciated.
Many thanks.
Upvotes: 0
Views: 53
Reputation: 619
Thanks guys for your help. It turns out that I needed to add a timeout delay when sending the broadcast in order for the $watch function to pick up the change. This was why my previous efforts were failing. I am using cometd to retrieve data and I guess this might be causing the need to add the delay.
In the factory:
factory.updateData = function(value){
data[0].teamFormations = Math.abs(value.rows[0].fields[0].v);
$timeout(function() {
$rootScope.$broadcast('updatedData1', data);
console.log('update with timeout fired')
}, 1000);
};
Many thanks,
Brian.
Upvotes: 0
Reputation: 4505
You can still use scope.$watch in your directive however you just need to return your factory value...
scope.$watch(function(){
return MyFactory.getData()[0].handle;
}, function(newVal, oldVal){
console.log(newVal, oldVal);
});
When a digest cycle starts angular will evaluate your scope.$$watchers which will return your factory data, it will then compare to the previous cached value and trigger your callback if it has changed.
Upvotes: 1
Reputation: 17354
The most broad way to do this is to use a $broadcast although this may not be the most efficient way, it usually gets the job done.
Note, $broadcast works by sending the data to child nodes and $emit works by sending the data to parent nodes. The node in reference here is usually the element the directive is attached to (or where the controller is). Since factories/services are not attached to any scope/element, we need to inject an element. The easiest way to achieve this is to inject the $rootScope service and $broadcast from there.
Step 1. Add $rootScope
app.factory('CallsDataService', function($rootScope) {
...
});
Step 2. Add $broadcast to method
factory.updateData = function(value){
data[0].handle = value;
$rootScope.$broadcast('updatedData', data); //Note data here should be an object you want to pass to the listeners.
};
Step 3. Listen for event in directive
link: function(scope,element,attrs){
scope.$on('updateData', function(data){
//Do whatever you like here
})
}
Keep in mind that there are better ways to do this through a better design process and if you're designing an performant app then you would try to avoid $broadcasting or $watching. However, if you're starting out and performance of the app is not exactly key (meaning that your app won't have a million things on it) then it should be perfectly fine.
Upvotes: 1