I am trying to watch changes on an json array defined in an angularj service, but when the change occures, the $watch function is not firing. My controller and service code goes as follows (plunker demo):
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope,cityService) {
//$scope.cities = [];
$scope.service = cityService;
cityService.initCities();
$scope.$watch('service.getCity()', function(newVal) {
$scope.cities = newVal;
console.log(newVal)
});
});
app.service('cityService', function($http) {
this.cities = [];
this.initCities = function() {
$http.get('data.js').success(function(data) {
this.cities = data;
});
};
this.getCity = function() {
return this.cities;
};
});
This is because the callback from get set this to window object. Keep the reference of the service in self.
See this plunker
http://plnkr.co/edit/CrgTWRBsg5wi7WOSZiRS?p=preview
I changed several things to make it work:
http://plnkr.co/edit/PDMaEvmx7hG1fKvAmR7R?p=preview
Function watch instead of variable
In the service, removed the keyword this because this has not the same context inside functions.
Return functions in service
Seems ok
Related
I have my notification listener in the run function. When a notification is received I need to update a object present in $scope with a parameter present in notification object.
angular.module('app', ['ionic', 'chatsCtrl'])
.run(function($state, $ionicPlatform) {
window.FirebasePlugin.onNotificationOpen(function(notification) {
// Need to append this notification.parameter to a scope variable present in a controller
}
}
.controller('chatsCtrl', function($scope) {
// $scope.chats
});
How can I go about doing this? I don't want to use $rootScope object as $scope.chat object will get very heavy.
Thanks
you can't call scope variables/functions inside run block. since you don't want to use rootscope my suggestion is to create a service and assign values to a particular method in that service from the run block. Then get that value from the controller using the same service.
angular.module('app', ['ionic', 'chatsCtrl'])
.run(function($state, $ionicPlatform) {
window.FirebasePlugin.onNotificationOpen(function(notification) {
sampleService.setData(notification)
}
}
.controller('chatsCtrl', function($scope,sampleService) {
$scope.chats = sampleService.getData()
});
.factory('sampleService', function() {
var data;
return {
getData : function(){ return data},
setData: function(param){ data = param},
}
});
I have two controllers. The view is tied to the firstCtrl. I'm trying to run the function in the second one so the view is updated. Function runs but view not updated. Any ideas?
<div>{{people.length}}</div>
angular.module('myApp').controller('firstCtrl', function($scope, $http) {
var self = this;
$scope.people = [];
function getData() {
$http.get('/people').then(function(res) {
$scope.people = res.data;
});
}
getData();
$scope.getData = getData;
self.getData = function(){
$scope.getData();
};
return self;
});
angular.module('myApp').controller('secondCtrl', function( $controller, $scope, $http) {
var firstCtrl= $controller('firstCtrl', { $scope: $scope.$new() });
firstCtrl.getData(); //This runs but view is not updated above.
});
I think your code has some problem with the $scope. So instead of pass data directly in the firstCtrl. I pass a callback to getData function and assign data to $scope.people in the callback.
Here is the working Plunker:
> http://plnkr.co/edit/QznxFL
Short question is: do we need to use var self = this; in a AngularJS Factory and Service?
I have seen AngularJS code for factory or service that uses a var self = this; to remember itself (the service object), and then inside of the functions of the service object, use self to refer to itself. (kind of like how we are afraid we will lose what this is, so we set self = this to use it later, by the principle of closure.
However, I have found that we can safely use this. Why? Because we get back a singleton service object, and when we invoke myService.getNumber(), the this is bound to the service object myService, so it does work. Example:
If it is a service:
https://jsfiddle.net/yx2s3e72/
angular.module("myApp", [])
.service("myService", function() {
console.log("Entering factory");
this.s = "hello";
this.getLength = function() {
return this.s.length;
};
})
.controller("myController", function(myService) {
console.log("Entering controller");
var vm = this;
vm.s = myService.s;
vm.length = myService.getLength();
});
or if it is a factory:
https://jsfiddle.net/935qmy44/
angular.module("myApp", [])
.factory("myService", function() {
console.log("Entering factory");
return {
s: "hello",
getLength: function() {
return this.s.length;
}
};
})
// controller code not repeated here...
It works too. So is it true that we really don't need to set a var self = this; in a custom AngularJS factory or service?
You may lose the reference to this, therefore the reference is saved in the self var.
angular.module('myApp', [])
.run(function (MyService) {
MyService.method();
})
.service('MyService', function ($timeout) {
this.key = 'value';
this.method = function () {
console.log(this) // service
$timeout(function () {
console.log(this); // window because callback is called from the window
}, 0);
}
});
angular.bootstrap(document.querySelector('#app'), ['myApp']);
See JSFiddle
I started using angularjs and the first examples i saw were ones that use this instead of $scope.
for example
app.controller('TabController',function(){
this.tab = 1;
});
I am trying to pass data between controllers. every example i see uses a service that is using $rootScope and then broadcast an event. the controller then uses the $scope.$on to listen to that event.
for example
app.service('mySharedService', function($rootScope) {
var sharedService = {};
sharedService.tab = 1;
sharedService.setTab= function(tab) {
this.tab = tab;
$rootScope.$broadcast('handleBroadcast');
};
return sharedService;
});
app.controller('TabController',['$scope','mySharedService',function($scope,mySharedService){
$scope.tab = 1;
$scope.$on('handleBroadcast', function() {
$scope.tab = sharedService.tab;
});
}]);
my question is how do i do this without using $scope but instead using this.
I took #Dieter Goetelen answer and changed it a bit
the result:
app = angular.module('app', []);
app.service('SharedService',function(){
this.counter = {value:0};
this.add = function (amount){
this.counter.value++;}
});
app.controller('TabController',['SharedService',function(SharedService){
this.sharedService = SharedService;
this.counter = SharedService.counter;
this.add = function(amount){
this.sharedService.add(amount);
}
}]);
here is the complete plunk
I'm using a service to share data between controllers. If a value on the service changes, I want to update some data binding on my controllers. To do this, I'm using $scope.$watchCollection (because the value I'm watching is a simple array). I'm having trouble trying to figure out how to test this in Jasmine + Karma.
Here is a simple Controller + Service setup similar to what I'm doing in my app (but very simplified):
var app = angular.module('myApp', []);
// A Controller that depends on 'someService'
app.controller('MainCtrl', function($scope, someService) {
$scope.hasStuff = false;
// Watch someService.someValues for changes and do stuff.
$scope.$watchCollection(function(){
return someService.someValues;
}, function (){
if(someService.someValues.length > 0){
$scope.hasStuff = false;
} else {
$scope.hasStuff = true;
}
});
});
// A simple service potentially used in many controllers
app.factory('someService', function ($timeout, $q){
return {
someValues: []
};
});
And here is a test case that I've attempted (but does not work):
describe('Testing a controller and service', function() {
var $scope, ctrl;
var mockSomeService = {
someValues : []
};
beforeEach(function (){
module('myApp');
inject(function($rootScope, $controller) {
$scope = $rootScope.$new();
ctrl = $controller('MainCtrl', {
$scope: $scope,
someService: mockSomeService
});
});
});
it('should update hasStuff when someService.someValues is changed', function (){
expect($scope.hasStuff).toEqual(false);
// Add an item to someService.someValues
someService.someValues.push(1);
//$apply the change to trigger the $watch.
$scope.$apply();
//assert
expect($scope.hasStuff).toEqual(true);
});
});
I guess my question is twofold:
How do I properly mock the service that is used in the controller?
How do I then test that the $watchCollection function is working properly?
Here is a plunkr for the above code. http://plnkr.co/edit/C1O2iO
Your test (or your code ) is not correct .
http://plnkr.co/edit/uhSdk6hvcHI2cWKBgj1y?p=preview
mockSomeService.someValues.push(1); // instead of someService.someValues.push(1);
and
if(someService.someValues.length > 0){
$scope.hasStuff = true;
} else {
$scope.hasStuff = false;
}
or your expectation makes no sense
I strongly encourage you to lint your javascript (jslint/eslint/jshint) to spot stupid errors like the first one.Or you'll have a painfull experience in writing javascript. jslint would have detected that the variable you were using didnt exist in the scope.