Updating model programmatically in angularjs - angularjs

I am trying to do the following:
angular.module("controllers", [])
.controller("FooController", function($scope) {
$scope.foo = {};
$scope.foo['bar'] = 0;
setInterval(function(){
$scope.foo['bar']++;
}, 100);
} );
And then, I display the value of foo.bar in my view using
<div> {{ foo.bar }} </div>
The initial value is displayed correctly, but it is never updated. The callback within setInterval is called correctly and the value of bar is updated in javascript.
How can I programmatically "push" my data into the model? (in my real app I'll be pushing data from the server via websockets / atmosphere)

If you use the angular $interval service instead of setInterval. Then you will not need to call $scope.$apply.
angular.module("controllers", [])
.controller("FooController", function($scope, $interval) {
$scope.foo = {};
$scope.foo['bar'] = 0;
$interval(function(){
$scope.foo['bar']++;
}, 100);
} );

You have to trigger a new digest cycle, in which Angular will check all the registered watches and update the view for objects that changed value. You can do it by calling $scope.$apply, which will trigger the digest cycle for you.
setInterval(function(){
$scope.$apply(function() {
$scope.foo.bar++;
})
}, 100);

Ok, I've found the answer: wrap $scope.foo['bar']++ into
$scope.$apply(function({
$scope.foo['bar']++
}))

Related

AngularJS - Watch service changes not updating view

Im working on angularjs 1.4. Im trying to have some frontend-cache collection that updates the view when new data is inserted. I have checked other answers from here Angularjs watch service object but I believe Im not overwriting the array, meaning that the reference is the same.
The code is quite simple:
(function(){
var appCtrl = function($scope, $timeout, SessionSvc){
$scope.sessions = {};
$scope.sessions.list = SessionSvc._cache;
// Simulate putting data asynchronously
setTimeout(function(){
console.log('something more triggered');
SessionSvc._cache.push({domain: "something more"});
}, 2000);
// Watch when service has been updated
$scope.$watch(function(){
console.log('Watching...');
return SessionSvc._cache;
}, function(){
console.log('Modified');
}, true);
};
var SessionSvc = function(){
this._cache = [{domain: 'something'}];
};
angular.module('AppModule', [])
.service('SessionSvc', SessionSvc)
.controller('appCtrl', appCtrl);
})();
I thought that the dirty checking would have to catch the changes without using any watcher. Still I put the watcher to check if anything gets executed once the setTimeout function is triggered. I just dont see that the change is detected.
Here is the jsbin. Im really not understanding sth or doing a really rockie mistake.
You need to put $scope.$apply(); at the bottom of your timeout to trigger an update. Alternatively you can use the injectable $timeout service instead of setTimeout and $apply will automatically get called.
jsbin

How to update view angularjs from data server (api/nodejs/mongodb) by click

I would like to know how can I update a view from data server after an update with ng-click.
I use Daftmonk Mean stack generator.
I opened an other topic with code but no answer, maybe I'm on the wrong way update/refresh angularjs ng-repeat with data from server
check out $watch and $apply
$watch will watch for a change and call $apply with a button
function MyController($scope) {
$scope.myVar = 1;
$scope.$watch('myVar', function() {
alert('hey, myVar has changed!');
});
$scope.changeValueButton = function() {
$scope.myVar = 2; // This will trigger $watch expression to kick in
};
$scope.updateButton = function() {
$scope.apply();
};
}

Angular JS: attribute not influenced by $scope...?

Angular newbie here.
I have the following div:
<div id="mainfr" data-curpos="dy[ {{curPosObj.dy}} ]" ...>blah blah </div>
And in my controller I have:
var nxtest = angular.module('nxtest', []);
var appController = nxtest.controller('AppCtrl', ['$scope', function ($scope) {
$scope.curPosObj = { dir: "down", dy:5 };
$scope.clr = window.setTimeout(function(){ $scope.curPosObj.dy = 777;
window.clearTimeout($scope.clr); }, 5000); //see if the attr responds to a random change
}])
In firebug, inspecting the scope object shows that it is indeed modified. I want to understand why the bound attribute {{curPosObj.dy}} is not 'bound' and the view does not respond to the changing values? Thanks very much in advance.
Update: added link to plunker as suggested - the red text never changes:
http://plnkr.co/edit/HJxEpgR8VepxuT47zJDJ?p=preview
Update 2: OK so there may be a separate issue here - the red text is in a pseudo element whose contrent attribute depends on the main divs attribute... and I'm not calling setAttribute anywhere... but regardless: in firebug, the 'data-curpos' attribute itself is NOT updating, never mind the pseudo elem that depends on it...
That's because angular doesn't tracking scope changes out of the dygest cycle and window.setTimeout that case. You should use the $timeout service instead of window.setTimeout or put code which chenge scope into $scope.$apply call
angularjs API reference - $timeout service
angularjs API reference - scope guide
try this:
var nxtest = angular.module('nxtest', []);
var appController = nxtest.controller('AppCtrl', ['$scope', '$timeout',
function($scope, $timeout) {
$scope.curPosObj = {
dir: "down",
dy: 5
};
$scope.clrPromise = $timeout(function() {
$scope.curPosObj.dy = 777;
}, 5000); //see if the attr responds to a random change
}
])

AngularJs variable not updating

Not able to figure out what the bug in this code is.I've tried to only post the relevant parts of the code here.
Controller
myApp.controller('MessageCtrl', function ($scope, notificationService, $rootScope) {
$scope.notificationService = notificationService;
$scope.msgCount = 0;
$scope.notificationService.subscribe({channel : 'my_channel'});
$rootScope.$on('pubnub:msg',function(event,message){
$scope.msgCount = $scope.msgCount + 1;
//$scope.$digest();
});
});
My Notification Angular Service
myApp.factory('notificationService',['$rootScope', function($rootScope) {
var pubnub = PUBNUB.init({
publish_key : '..',
subscribe_key : '..'
});
var notificationService = {
subscribe : function(subscription) {
pubnub.subscribe({
channel : subscription.channel,
message : function(m){
$rootScope.$broadcast('pubnub:msg', m);
}
});
}
};
return notificationService;
}]);
And the template :
<div>
Count = {{msgCount}}
</div>
The problem :
Using console logs & using karma tests I have confirmed that the $rootScope.$on method in MessageCtrl is getting called when I do a $broadcast from Notification Service. And that the msgCount variable is getting incremented. However, I don't see the updated value being reflected in the template without running a $scope.$digest() . I am pretty sure I shouldn't be needing to have to call $scope.$digest , ie Angular should be providing me this binding.
Interestingly, when I tried a $rootScope.$broadcast from another controller, the msgCount in the template got incremented without having to call $scope.$digest().
Can anyone kindly help me here. Thank you.
Update
Thanks to Peter and looking at the google group discussion, wrapping the $broadcast in an $apply did the trick.
$rootScope.$apply(function(){
$rootScope.$broadcast('pubnub:question', m);
});
It seems that your $broadcast happens outside AngularJS and you need to notify your app about it with calling $apply(), but better do it in the notificationService.
As for $broadcast and $on trigger a apply/digest you can read in this post. Brief overview of AngularJs source files make me sure that $broadcast does not auto-apply changes (look here ). $broadcast just calling listeners and nothing else.
Please, take a look at this simple example on jsFiddle .
The template
<div ng-controller="myCtrl">
<p>Count: {{ count }}</p>
<button ng-click="fireEvent()">Fire Event</button>
</div>
The controller
angular.module("app", [])
.controller('myCtrl', function($scope, $rootScope, notificationService) {
$scope.count = 0;
notificationService.subscribe();
$rootScope.$on('event', function() {
console.log("event listener");
$scope.count++;
});
$scope.fireEvent = function() {
// it is ok here due to ngClick directve
$rootScope.$broadcast('event', true);
};
})
And factory
.factory('notificationService',['$rootScope', function($rootScope) {
var notificationService = {
subscribe : function() {
setInterval(function(){
console.log("some event happend and broadcasted");
$rootScope.$broadcast('event', true);
// angular does not know about this
//$rootScope.$apply();
}, 5000);
}
};
return notificationService;
}]);
Of course in both cases you will see that event listener fires, but ngClick fires $digest and your notificationService does not.
Also you can get some info about sources that will start the digest cicle in this nice answer https://stackoverflow.com/a/12491335/1274503

AngularJS - How to watch model that is used in two controllers without using $rootScope

I have two different views that has volume sliders.
I have $watch for each volume slider model and when it is changed I call the setVolume method.
$scope.$watch("speakerVolume", function () {
voipService.SpeakerVolumeChange($scope.speakerVolume * 655);
});
I want to watch the changed volume in a way that whenever I change the volume in one slider it will be changed also in the second one.
I have heard that i can put the model in $rootScope and then watch on it.
Is there another solution for that?
Thanks
You can set the first parameter watchExpression with a function like :
app.controller('MainCtrl', function($scope, voipService) {
$scope.speakerVolume = voipService.speakerVolume;
$scope.$watch("speakerVolume", function (newValue) {
voipService.speakerVolume = newValue;
});
});
app.controller('SecondCtrl', function($scope, voipService) {
$scope.$watch(function () { return voipService.speakerVolume }, function (newValue) {
$scope.speakerVolume = newValue;
});
});
app.service('voipService', function() {
this.speakerVolume = 2;
});
With two data-binding :
<p ng-controller="MainCtrl">
<input ng-model="speakerVolume" />
{{speakerVolume}}
</p>
<p ng-controller="SecondCtrl">{{speakerVolume}}</p>
Plunker
Using watchers is always kind of ugly.
Maybe this https://egghead.io/lessons/angularjs-sharing-data-between-controllers will help you.
Services are singletons and there is only 1 instance in angular.
If you set attributes on the service, pass the service to controllers and bind this attribute to the scope, it wont loose the 2-way binding.
I'd recommend you to use the watcher in the service and have 2 variables: 1 the watcher is watching and 1 that actually carries the value.

Resources