AngularJS Share Data Between Controllers Using Service And Watch For Updates - angularjs

Simple task here, but not sure about the mistake.
My service:
app.factory('Progress', function () {
var data = {
progressPercentageLoaded: 0
};
return {
getProgress: function () {
return data.progressPercentageLoaded;
},
setProgress: function (progress) {
data.progressPercentageLoaded = progress;
}
};
});
I have one controller that is uploading a file, this one sets the progress value.
//in my controller
$scope.progressPercentageLoaded = {progress:0};
//a few lines down
function (evt) {
console.log(evt);
$scope.progressPercentageLoaded.progress = evt.loaded;
Progress.setProgress($scope.progressPercentageLoaded.progress);
My second controller should simply watch the service for changes and update the view, but it stays stuck at zero even though I confirm the upload is happening and that evt.loaded is changing.
$scope.progress = 0;
$scope.$watch(function () { return Progress.getProgress(); }, function (newValue, oldValue) {
if (newValue !== oldValue) {
$scope.progress = Math.min(100, parseInt(100 * newValue / $scope.size));
if($scope.progress == 100)
{
$scope.progressModalInstance.close();
window.location.reload();
}
}
});
That is, $scope.progress in second controller should update with the value of evt.loaded in the first controller, but it doesn't.
Thanks in advance for any guidance.
EDIT: I even added the third watch parameter as true but that didn't help either.
EDIT 2 : The code above actually works to the best of my knowledge, I believe something else was causing a problem as when I reverted the code to the above after editing it due to the answers, it suddenly worked like it should. Sorry about this.

using $rootScope.$broadcast will work better for this
app.factory('Progress', function ($rootScope) {
var data = {
progressPercentageLoaded: 0
};
return {
getProgress: function () {
$rootScope.$broadcast('Event');
return data.progressPercentageLoaded;
},
setProgress: function (progress) {
data.progressPercentageLoaded = progress;
}
};
});
In your second controller instead of using watch
something like
$rootScope.$on('Event', function(){
//your logic here
})

You are losing the reference because you are watching the Int value that you are updating every time, hence you are changing the reference. You have to watch the whole object progressPercentageLoaded.
You have to pass true as the last parameter of the $watch function so that the equality check is angular.equals. Otherwise, only reference equality is checked.

I'm not 100% sure, but Angular may not be aware of the file upload event. Try calling $apply:
$scope.$apply(function() {
Progress.setProgress($scope.progressPercentageLoaded.progress);
});

Related

Auto trigger function throuigh service in angularjs

hi all i am using angulrajs passing one value from one controller to another controller using service it's work fine but my need is when service value change in controller 2 i get the service value in one scope when scope value change i need trigger the function it's called refresh function when service value change and that i need to call the refresh function here my fiddle
https://jsfiddle.net/ctawL4t3/10/
You can just $watch your value.storeObject. Though it's not best of the practices, but it suits this kind of feature.
$scope.$watch('value.storedObject', function(newVal) {
if(newVal !== '') {
refresh()
}
})
working fiddle (open console to see refresh function logging)
You can try to use angular default $emit, $broadcast, or try to do 2 simple functions in own service
angular.module('app').factory('StoreService', function() {
var listeners = {};
var emit = function(name, val) {
if(listeners[name]) {
listeners[name](val)
}
}
var on = function(name, callback) {
listeners[name] = callback;
}
return {
emit: emit,
on: on,
storedObject: ''
};
});
JSFiddle example
JSFiddle example $watch
JSFiddle example ng-change is better because, you can use easily debounce
you can use broadcast function for that
Please check this SO link to find the related answer
How to call a function from another controller in angularjs?
app.controller('One', ['$scope', '$rootScope'
function($scope) {
$rootScope.$on("CallParentMethod", function(){
$scope.parentmethod();
});
$scope.parentmethod = function() {
// task
}
}
]);
app.controller('two', ['$scope', '$rootScope'
function($scope) {
$scope.childmethod = function() {
$rootScope.$emit("CallParentMethod", {});
}
}
]);

AngularJS - share variable between two controllers

I have two controllers that have to communicate each other.
The first reference to a video player and the second one to a timeline.
From the first one, I get the currentTime of the video playback and I want to pass it to the second one that should move the time-bar as the video is playing.
I tried using the factory to share a variable called time between controllers but this doesn't change during the time.
First Controller:
angular.module('videoCtrl', ['vjs.video'])
.controller('videoController', ['$scope', 'Timeline', function (scope, Timeline) {
scope.mediaToggle = {
sources: [
{
src: 'http://static.videogular.com/assets/videos/videogular.mp4',
type: 'video/mp4'
}
],
};
//listen for when the vjs-media object changes
scope.$on('vjsVideoReady', function (e, videoData) {
videoData.player.on('timeupdate', function () {
var time = this.currentTime();
Timeline.setTime(time); // setting the time on factory
})
});
}]);
Second Controller:
angular.module('timelineCtrl', ['mt.media-timeline'])
.controller('timelineController', function ($scope, Timeline) {
$scope.time = Timeline.getTime(); // here I'm trying to get the time
});
Factory:
.factory('Timeline', function(){
var timelines = [];
var time = null;
return {
getTime: function() {
return time;
},
setTime: function(_time) {
time = _time;
}
}
});
time appears to be a primitive, which means it is returned byVal rather than byRef. In other words, each call to getTime will return the value that time is currently set to, and calls to setTime will change the value for future calls, but not for anything that already called it. This is a classic case of the angular rule, Always use a dot.
Try changing time to an object instead:
.factory('Timeline', function() {
var timelines = [];
var time = {
value: null
};
return {
getTime: function() {
return time;
},
setTime: function(_time) {
time.value = _time;
}
}
});
In your HTML, use {{time.value}}.
Saving in $rootScope instead of $scope would give you the ability to access a variable across all your app and your controllers. But have in mind that creating a large number of $rootScope could affect your app's performance.
Do not forget to inject $rootScope into the controller (like you did with $scope), so you can access it.
Well as far as I can tell what're doing in the second controller is that you retrieve the value of time on instantiation of the controller. Of course further changes of the value in the service can't be picked up this way. To do that can use $scope.$watch in the second controller:
angular.module('timelineCtrl', ['mt.media-timeline'])
.controller('timelineController', function ($scope, Timeline) {
$scope.time = Timeline.getTime(); //Set the time once so it's not undefined
$scope.$watch(
function() {return Timeline.getTime();},
function(newVal) {$scope.time = newVal;}
);
});
Angular will call the first function in every $digest cycle(That's about at least every 10ms if I recall correctly) and will call the second function when a change has been detected. Detailed documentation for $watch can be found here
This is one way to do it. You could also add a function to your $scope(e.g. getTime()), which should return the current time, and then call this function in the HTML template: {{getTime()}}. Both ways pretty much work the same way, except that the second one leaves the 'dirty' work to angular(creating watchers and updating values)

Watching service object for changes is not working when using the controller as syntax

I tried to follow along with this example but my code never enters the callback with the newValue when the input changes for some reason. The only difference in my example and the example bin below is that I'm using a service to hold the value instead of a controller variable.
I've been trying to make it work but I can't wrap my head around it. What am I missing?
http://jsbin.com/yinadoce/1/edit?html,js,output
Note that I've excluded the input and the steps prior to the value being updated in the service as this works perfect. The issue is only that my watch doesn't understand when the value has changed in the service.
EDIT: Forgot to inject $scope in the controller when I pasted over the code, now it's complete.
Controller:
coForms.controller('CoFormsCtrl', ['$scope', 'coFormsInfo', function($scope, coFormsInfo) {
$scope.$watch(angular.bind(coFormsInfo.getInfo(), function() {
return coFormsInfo.getInfo();
}), function(newVal) {
console.log(newVal);
});
}]);
Service:
coForms.service('coFormsInfo', [function() {
var info = {
filteredList: []
}
this.setFilteredList = function(list) {
info.filteredList = list;
};
this.getInfo = function() {
return info;
};
}]);
The watcher is there to detect any changes in the variable you're watching. How can he watch something that is not... Strictly present like a return value?
I'm not sure about what I'm saying because I'm new to angular, but the logic seems false there. You need to watch something declared to detect some changes.
You should call your service to get your infos when you need them and watch for an info variable.
EDIT
My bad there is something like that but you should declare it in a function maybe like the example on the documentation
var food;
scope.foodCounter = 0;
expect(scope.foodCounter).toEqual(0);
scope.$watch(
// This function returns the value being watched. It is called for each turn of the $digest loop
function() { return food; },
// This is the change listener, called when the value returned from the above function changes
function(newValue, oldValue) {
if ( newValue !== oldValue ) {
// Only increment the counter if the value changed
scope.foodCounter = scope.foodCounter + 1;
}
}
);

problems with $watch function

I'm not really sure about this issue but it seems that sometimes when I activate $watch for a function then it doesn't work.
for example I have this simple service
angular.module('sp-app').factory('mediaSources', function() {
var storages = [];
return {
addStorage: function(storage) {
storages.push(storage);
},
getStorages: function() {
return storages;
}
}
});
and when I watch getStorage method in order to update my view it doesn't call change callback or calls only at initialization stage
$scope.$watch(function($scope) {
return mediaSources.getStorages();
}, function() {
console.log('call')
});
and I can only track changes by watching length property of returned array
return mediaSources.getStorages().length;
and I wonder because I have written similar think somewhere else within my application and it works fine.
If i interpret what you are trying to do, you should not need to set a watch on something like this, you can just use a factory like so :
angular.module('app').factory('mediaSources', function(){
var storages = {};
storages.list = [];
storages.add = function(message){
storages.list.push(message);
};
return storages;
});
then in the controller you want to receive/update the data to for instance, you would do
$scope.myControllerVar = mediaSources.list;
No need to watch over it, it should update for you.
You will have to set up watcher with equality flag as the third argument:
$scope.$watch(function($scope) {
return mediaSources.getStorages();
}, function() {
console.log('call');
}, true);

Angularjs adding $scope.$watch after $routeProvider's resolve

I'm having an issue where I am trying to watch a certain element from my scope after the resolve is done. For some reason, it gets run when i run the second line from this snippet and I dont seem to be able to add this $watch during the "resolve".
I understand that promises are asynch, but how can I know when my resolve is done to then add the watch?
The variableToWatch can be changed either by code or in the UI (otherwise i would've just used ng-change="doWork()")
$scope.variableToWatch = $route.current.locals.data.initialValue;
$scope.listOfDependantData = $route.current.locals.data.items;
$scope.$watch('variableToWatch', function (newValue) {
myService.getNewDependantData(newValue).$promise.then(
function (items) {
$scope.listOfDependantData = items;
}
);
};
Update:
If you want to run your code only when it changes after the initial setting of value, you can use the second form of the watchFunc passed as the second argument to $scope.$watch:
$scope.$watch('variableToWatch', function (newValue, oldValue) {
if (typeof newValue !== 'undefined' && newValue !== oldValue) {
// Use the new value here ...
}
});
I don't fully follow the question, but I suspect that you want to watch the value $route.current.locals.data.initialValue instead of $scope.varibleToWatch?
In that case, you can use the alternate form of $scope.$watch with a function as the first argument:
$scope.$watch(function () { return $route.current.local.data.intialValue; }, function (newValue) {
// ...
};
Or, if you want to watch the variable on your $scope which is referenced to by the sting contained in $route.current.local.data.initialValue, then you can use this:
$scope.$watch(function () { return $scope[$route.current.local.data.intialValue]; }, function (newValue) {
// ...
};
Does this solve your problem?

Resources