everyone!
I'm creating app with authorization.
To store user data during session I use such service:
app.service('Session', function ($rootScope, $cookies) {
var Session = $rootScope.$new();
Session.createSession = function (accessTo, level, user_data) {
this.level = level;
this.accessTo = accessTo;
if(user_data) {
$cookies.isAuth = true;
} else {
$cookies.isAuth = false;
}
this.user = {
email : user_data.email,
fullname : user_data.fullname,
id : user_data.id,
level : user_data.level
}
if(accessTo && accessTo.length == 1 && !$cookies.account) {
this.account = accessTo[0].id;
$cookies.account = accessTo[0].id;
} else {
this.account = $cookies.account;
}
};
Session.destroy = function () {
this.level = null;
this.account = null;
this.user = null;
this.isAuth = false;
$cookies.account = null;
};
return Session;
});
In controller use:
Session.createSession(some, params, here);
Afteer that it put some data to rootscope, and i can show it in console, but when i try to see for example Session.user/.level etc. it doesn't work. What is wrong?
You should be doing this:
app.service('Session', function() {
// A service is a singleton, as in it a persistent
// object you can inject in controllers
this.foo = 'bar'; // anything on "this" is publicly available
this.createSession = function() { ... };
// you can also expose functions that you can call from the controllers
});
app.controller('Ctrl1', function($scope, Session) {
// You can inject "Session" here and use stuff you
// added on "this" inside of it
$scope.foo = Session.foo;
// you can put stuff on the controller's $scope
// and you can bind to it in your HTML by using <span>{{foo}}</span>
Session.foo = 'baz';
// you can change stuff in the service and it will be persisted
});
So now if you navigate to a certain "Ctrl2" that injects "Session" too, Session.foo will be "baz", not the initial "bar" value.
Related
I am trying to unit test two functions codes and keep getting error of undefined object.
my controller
vm = this;
//always fire first in the app
vm.getCompany = function() {
api.getCompany(function(res){
//do stuff
})
}
//always fire second in the app
vm.getEmployee = function() {
api.getEmployee(function(res){
//do stuff
})
}
api service
var company;
function getCompany() {
var company;
var q = $q.defer();
var url = ‘something.com’;
anotherApi.getCompany(url).then(function(comp){
company = comp;
q.resolve(company)
})
}
function getEmployee = function() {
var name = company.name
var url = ‘something.com/’ + name;
var q = $q.defer();
anotherApi.getEmployee(url).then(function(employee){
q.resolve(employee)
})
}
unit test.
beforeEach(function(){
module(‘myApp);
inject(function ($injector) {
$controller = $injector.get('$controller');
$rootScope = $injector.get('$rootScope');
$scope = $rootScope.$new();
$httpBackend = $injector.get('$httpBackend');
api = $injector.get('api');
});
vm = $controller'myCtrl', {
$scope : $scope
});
})
describe (‘test’, function(){
it(‘should get company’, function(){
vm.getCompany();
$httpBackend.flush();
// stuff and works
})
it(‘should get Employee’, function(){
vm.getEmployee()
$httpBackend.flush();
//getting error says
//'undefined' is not an object (evaluating 'company.name’)
})
})
I am getting 'undefined' is not an object (evaluating 'company.name’)
under getEmployee function in service.
I have tried many different ways but still not sure how to fix it, can someone help me about it? Thanks!
What is the expected behavior of the service if getEmployee is called before getCompany is called? You should at least check for company being null before attempting to use it. Also, you may want to consider storing the company in a property that you can access in your service. NOTE: I'm prefixing the property name with an underscore just to make a distinction between the public api and this pseudo-private property:
{
_company: null,
getCompany: function() {
var self = this;
var url = '...';
return $http.get(url).then(function(comp){
self._company = comp;
return self._company;
});
},
getEmployee: function() {
var self = this;
if (!self._company) {
return null; //or throw error or return rejected promise: $q.reject('company is null')
} else {
var url = '...';
var name = self._company.name;
return http.get(url);
}
}
}
Lastly, you can (and should) test your service separately from your controller now. In your controller test, you can just spyOn your service methods without it calling through to the server. And when you test your service, you can just set the service._company to a mock value when testing the getEmployee method.
Issue is in your Service. "company" should be the object literal since you access .name over it else it will through an error which you have specified.
Try below code:
Service
var company = {};
function getCompany() {
$http.get(url).then(function(comp){
company = comp;
return company;
})
}
function getEmployee = function() {
var name = company.name
$http.get(url).then(function(employee){
// do stuff
}
}
It should work.
I am very new to this Angular component communication. I am using Angular 1.5.X version and I am using factory to share data between components. I am facing one issue where Async value of Service Variable refreshes after certain time.
I understand one solution is to set watcher on non scope variable but I think I am missing something important here. Can you guys please share views?
This is Service.js code
Service.$inject = ['$http','$q'];
function Service($http,$q) {
this.$http = $http;
this.$q = $q;
};
Service.prototype.getTileCount = 0;
Service.prototype.getTileData = function(Url){
var deferred = this.$q.defer();
this.$http.get(Url)
.success(function(response){
Service.prototype.getTileCount = response.data.length;
console.log('data length :', Service.prototype.getTileCount);
deferred.resolve(response);
});
return deferred.promise;
};
This is component 1 controller code
function Component1Controller(Service) {
this.tileData ={};
var self = this;
var promise = Service.getTileData(this.sourceUrl);
promise.then(function(data) {
self.tileData = data;
Service.getTileCount = data.length;
console.log('This is tileData : '+ Service.getTileCount);
});
};
This is component 2 controller code
function Component2Controller(Service) {
var self = this;
console.log(Service.getTileCount);
// getting getTileCount = 0; After setting timeout function of 5 second I am able to get getTileCount value
};
The thing is that Service.getTileCount is updated asynchronously, that's why it's 0 at first and then at some point it changes. I would recommend you to simplify your service and always work with getTileData method, which would be a single source of data. The implementation would also become simpler:
function Service($http, $q) {
this._tileData = null;
this.$http = $http;
this.$q = $q;
}
Service.prototype.getTileData = function(Url) {
if (!this._tileData) {
this._tileData = this.$http.get(Url).then(function(response) {
return response.data;
}.bind(this));
}
return this._tileData;
};
Note, how it caches tiles response in "private" _tileData property. Now you can always rely on getTileData method which will return data no matter when you call it:
function Component1Controller(Service) {
this.tileData = {};
var self = this;
Service.getTileData(this.sourceUrl).then(function(data) {
self.tileData = data;
console.log('This is tileData:', self.tileData.length);
});
};
function Component2Controller(Service) {
var self = this;
Service.getTileData(this.sourceUrl).then(function(data) {
console.log('tile count', data.length);
});
};
In this case Service.getTileCount is not needed anymore.
Demo: http://plnkr.co/edit/3zE6VL4emXaLRx2nCRih?p=info
I'm pretty much new in angular js. What I am trying to do is pass an integer argument to http get request in my controller. This is how my sample code looks like.
(function() {
angular
.module('myApp.directory', [])
.factory('NewsService', function($http)
{
return {
getallnews: function() {
return $http.get('get_all_news_feed.php?page='+pageNumber);
}
};
})
.factory('NewsFeed', function(directoryService) {
var NewsFeed = function() {
this.items = [];
this.busy = false;
this.pageNumber = 1;
};
NewsFeed.prototype.nextPage = function() {
if (this.busy) return;
this.busy = true;
NewsService.getallnews().success(function(data) {
var itemData = data;
for (var i = 0; i < itemData.length; i++) {
this.items.push(itemData[i]);
}
this.pageNumber++;
this.busy = false;
}.bind(this));
};
return NewsFeed;
})
.controller('MyController', function(NewsFeed, NewsService) {
var inst = this;
inst.news = new NewsFeed();
});
})();
I am building a news feed app. News is fetched from get_all_news_feed.php page and I want to pass a parameter pageNumber to it. This is while implementing infinte scrolling in angular.
I am getting undefined error. Any ideas?
Modify the factory method to accept pageNumber as parameter
getallnews: function(pageNumber) {
return $http.get('get_all_news_feed.php?page='+pageNumber);
}
Pass it when calling the method
NewsService.getallnews(this.pageNumber)
I'm trying to access the properties of my service, I think theres something wrong with the way I'm injecting the service into my class. I get the following error message when running my app
angular.js:13424 ReferenceError: CouponsService is not defined
at CouponsComponent.$onInit (coupons.controller.js:13)
...
Service
angular.module('couponGeneratorApp')
.service('CouponsService', function () {
// AngularJS will instantiate a singleton by calling "new" on this function
this.amount_type = null;
this.amount = null;
this.has_min = null;
this.min_order = null;
});
Controller
(function() {
class CouponsComponent {
constructor($state, CouponsService) {
this.test = 'hello';
this.state = $state;
this.couponParams = {};
}
$onInit() {
console.log(CouponsService);
}
processParams() {
if (!this.couponParams.amount || this.couponParams.amount <= 0) {
alert('Enter a valid amount');
} else if (this.couponParams.has_min && (!this.couponParams.min_order || this.couponParams.min_order < 0)) {
alert('Enter a valid min order');
} else {
CouponsService.amount_type = this.couponParams.amount_type;
CouponsService.amount = this.couponParams.amount;
CouponsService.has_min = this.couponParams.has_min;
if (CouponsService.has_min) CouponsService.min_order = this.couponParams.min_order;
this.state.go('coupons.login');
}
}
}
angular.module('couponGeneratorApp')
.component('couponsForm', {
templateUrl: 'app/coupons/form.html',
controller: CouponsComponent
});
angular.module('couponGeneratorApp')
.component('couponsLogin', {
templateUrl: 'app/coupons/login.html',
controller: CouponsComponent
});
})();
The issue is with the scope of the variable. When you inject it into an ES6 class, the constructor method doesn't make the variable available to all other methods. So just like you set $state to this.$state, you need to do the same for any injected services that other methods will use.
class CouponsComponent {
constructor($state, CouponsService) {
this.test = 'hello';
this.state = $state;
this.CouponsService = CouponsService;
this.couponParams = {};
}
$onInit() {
console.log(this.CouponsService);
}
// Rest of class
}
Also recommend to use ngAnnotate so your build tool can help make the injection work better.
You have to decorate your class with the injectable service.
Add:
CouponComponent.$inject = ['$state', 'CouponService'];
I'm attempting to call a service from within another service, then use the returned object to perform some operations. I keep running into a TypeError: getDefinitions is not a function error, however.
Below is my service is called, the service doing the calling, and my relevant controller code:
definitions.service.js:
'use strict';
angular.module('gameApp')
.factory('definitionsService', ['$resource',
function($resource) {
var base = '/api/definitions';
return $resource(base, {}, {
get: {method: 'GET', url: base}
});
}]);
utilities.service.js:
'use strict';
angular.module('gameApp')
.factory('utilitiesService', ['definitionsService', function(definitionsService) {
return {
description: description,
detail: detail,
severity: severity,
};
function description(account) {
var key = angular.isDefined(getDefinitions().ABC[account.code]) ? account.code : '-';
return getDefinitions().IDV[key].description;
}
function detail(account) {
var key = angular.isDefined(getDefinitions().ABC[account.code]) ? account.code : '-';
return getDefinitions().IDV[key].detail;
}
function severity(account) {
var key = angular.isDefined(getDefinitions().ABC[account.code]) ? account.code : '-';
return getDefinitions().IDV[key].severity;
}
var getDefinitions = function() {
definitionsService.get().$promise.then(function(data) {
return data;
});
};
}]);
controller.js:
'use strict';
angular.module('gameApp')
.controller('AccountsController', AccountsController);
AccountsController.$inject = ['$routeParams', 'customersService', 'utilitiesService'];
function AccountsController($routeParams, playersService, utilitiesService) {
var vm = this;
var playerId = $routeParams.playerId;
var getAccounts = function() {
playersService.getAccounts({
playerId: playerId
}).$promise.then(function(accounts) {
for (var i = 0; i < accounts.length; i++) {
if (angular.isDefined(accounts[i].secCode)) {
accounts[i].code = accounts[i].secCode;
accounts[i].severity = utilitiesService.severity(accounts[i]);
accounts[i].detail = utilitiesService.detail(accounts[i]);
accounts[i].description = utilitiesService.description(accounts[i]);
}
}
vm.accounts = accounts;
});
};
var init = function() {
getAccounts();
};
init();
}
Currently your service returns before your variable gets defined. That means the definition is never reached. So it is declared, as the function executes, but is undefined. Just move your variable definition to the top.
This will only prevent the definition error. Another problem is that your getDefinitions function doesn't return anything but you're calling a property on it. One solution I can think of is using a callback, that gets executed when your data is loaded:
angular.module('gameApp')
.factory('utilitiesService', ['definitionsService', function(definitionsService) {
var data;
reload();
var utils = {
description: description,
detail: detail,
severity: severity,
reload: reload,
loaded: null
};
return utils;
function reload() {
definitionsService.get().$promise.then(function(data) {
data = data;
if (utils.loaded && typeof utils.loaded === "function") {
utils.loaded();
}
});
}
function description(account) {
var key = angular.isDefined(data.ABC[account.code]) ? account.code : '-';
return data.IDV[key].description;
}
}]);
Then in your controller you could use the service like this:
utilitiesService.loaded(function(){
accounts[i].description = utilitiesService.description(accounts[i]);
})
old question but still relevant. To expand on Florian Gl's answer above if you have a service with multiple functions and one or more of those functions requires a "pre-service" function to be called for example to load some resource data in like configuration information move that service call to the top, outside of the nested function (in this case below I am dealing with the promise scenario in JavaScript):
angular.module('gameApp')
.factory('utilitiesService', ['definitionsService', function(definitionsService) {
var myFirstConfigValue = '';
// call any all services here, set the variables first
configurationService.GetConfigValue('FirstConfg')
.then(function (response) {
// set the local scope variable here
myFirstConfigValue = response;
},
function() { });
function myTestFunction() {
// make an ajax call or something
// use the locally set variable here
ajaxService.functionOneTwo(myFirstConfigValue)
.then(response) {
// handle the response
},
function(err) {
// do something with the error
});
}
}]);
Key point to note here is that if you need to load in some data you do that first outside of any other functions inside your service (e.g. you want to load some JSON data).