I'm having some troubles with services in angular.
Basically I have a service that I use as a constant class for user specific parameters, I store these in variables that read the values from a cookie.
This is my service:
shoutApp.service('userConfig', function (userService, config) {
this.USER_ID = $.cookie('shoutUserObj')._id;
this.USER_USERNAME = $.cookie('shoutUserObj').username;
this.USER_PASSWORD = $.cookie('shoutUserObj').password;
this.USER_JOINDATE = $.cookie('shoutUserObj').joinDate;
this.USER_TWITTER = $.cookie('shoutUserObj').twitter;
this.USER_FACEBOOK = $.cookie('shoutUserObj').facebook;
this.USER_GOOGLE = $.cookie('shoutUserObj').google;
this.refreshUserObj = function (callback) {
console.log("Starting refresh");
//This function retrieves new user data
userService.requestUserObj($.cookie('shoutUserObj')._id, function (code, userObj) {
if (code === config.CODE_AJAX_SUCCESS) {
//Here I delete the old cookie and set a new one with the same name
$.removeCookie('shoutUserObj');
$.cookie.json = true;
$.cookie('shoutUserObj', userObj, { path: '/', expires: 7 });
//When I log the new values of the cookie at this point they changed
}
});
}
});
I also tried storing these in an object, but every time I change the paramters they only change inside the class ( Like when i log out the new variables in my refresh function, they changed, but when I try to access them from a controller through the return values, they are not changed).
Example Controller:
shoutApp.controller('profileController', function ($scope, config, userConfig) {
console.log("Username: " + userConfig.USER_USERNAME);
//This value allways stays the same, even when I changed the cookie
});
My goal is to get the changed paramteres in all my Controllers that use the user service, how can I achieve this?
Would be really thankful for any help!
Services are singletons, therefore the values of your userConfig properties will always have the values they had when the service was initialized.
In the service, use a function if you want to retrieve a new value each time
this.getUSER_USERNAME = function() {
return $.cookie('shoutUserObj').username;
}
instead of a property
this.USER_USERNAME = $.cookie('shoutUserObj').username;
then the example controller would be:
shoutApp.controller('profileController', function ($scope, config, userConfig) {
console.log("Username: " + userConfig.getUSER_USERNAME());
//This will return the current value of the cookie
});
Related
The angularjs guide contains an example detailing an async call in a service.
The following code is given
angular.module('finance3', [])
.factory('currencyConverter', ['$http', function($http) {
var currencies = ['USD', 'EUR', 'CNY'];
var usdToForeignRates = {};
var convert = function(amount, inCurr, outCurr) {
return amount * usdToForeignRates[outCurr] / usdToForeignRates[inCurr];
};
var refresh = function() {
var url = 'https://api.exchangeratesapi.io/latest?base=USD&symbols=' + currencies.join(",");
return $http.get(url).then(function(response) {
usdToForeignRates = response.data.rates;
usdToForeignRates['USD'] = 1;
});
};
refresh();
return {
currencies: currencies,
convert: convert
};
}]);
Since the refresh function is async, how does angularjs ensure that the data is loaded before loading the controller which accesses the data returned by the refresh function, ie the data contained in the usdToForeignRates object.
I assume there is some blocking going on somewhere, otherwise when the controller accesses the object returned, it will get undefined values.
I want to understand the flow, does angular internally ensure that service loads before injecting it into the controller?
I am currently develloping a little app with angularJS;
the users have to go through a login form and then they can click on a few links who display data fetched from a server.
The login form only takes the user's input and it is then "stored" into a factory
app.factory('IdFormHolder', function () {
var that = this;
that.setId = function (data) {
that = data;
};
that.getId = function () {
return that;
};
return that;
});
Which works fine I need to keep it stored because the server requires the login form to be sent each time I do a $http.get as a header.
Each controller takes the login form from the factory and uses it to get the data from the server.
This works fine until someone decides to refresh the page, at which point it seems the factory is "emptied" from its login form, and the web-app then fails to show anything.
Is there a way to store this login info so that it doesn't get erased so easily ?
You can use this code after youve installed sessionStorage:
app.factory('IdFormHolder', ['$sessionStorage', function ($sessionStorage) {
var that = this;
that.setId = function (data) {
$sessionStorage.id = data;
that = data;
};
that.getId = function () {
return $sessionStorage.id;
};
return that;
}]);
Download Link: https://github.com/gsklee/ngStorage
In order to persist data you'd have to use some kind of local DB || LocalStorage || SessionStorage at least. When initializing the Factory you could check and attempt to retrieve from DB/LS that data and hold it as a variable if it does exist.
Something like
app.factory('IdFormHolder', function () {
this.heldId = attemptToGetSavedDataFromSomewhere(); // would be null otherwise
this.setId = (id) => {
this.heldId = id;
};
this.getId = () => this.heldId;
return this;
});
Using angular-local-storage you can access to the browsers local storage:
app.factory('IdFormHolder', function(localStorageService) {
return {
setId: function(data) {
return localStorageService.set('loggedUserData', data);
},
getId: function() {
return localStorageService.get('loggedUserData');
}
};
});
I don't get what I'm doing wrong, I am trying to globally store and pass data from one controller to another via a service. I stored the data in one controller and confirmed that it was stored at the beginning of my buildReportController. Then, when I click a button on my UI, it opens reportResultsController. However, issue is, I can store the data correctly in buildReportController via locationHistoryService.store() but when I go to reportResultsController and calllocationHistoryService.get(), thepreviousLocationvariable inlocationHistoryService` is empty as if the data was never set. Any ideas on how why or how I can "globally" store data and pass it between controllers? Below is my attempt. Thanks!
In reportView.js
angular.module('reportView', [])
.service('locationHistoryService', function(){
var previousLocation = "";
return {
store: function(location){
previousLocation = location;
},
get: function(){
return previousLocation;
}
};
});
In buildReport.js
angular.module('buildReport', ['reportView'])
.controller('buildReportController', ['$rootScope', 'locationHistoryService'], function($rootScope, locationHistoryService){
$rootScope.$on('$locationChangeSuccess', function(e, newLocation, oldLocation){
locationHistoryService.store(oldLocation);
console.log("Old location: ", oldLocation);
});
}
In reportResults.js
angular.module('reportResults', ['reportView'])
.controller('reportResultsController', ['$rootScope', 'locationHistoryService'], function($rootScope, locationHistoryService){
console.log("location : ", locationHistoryService.get());
}
The locationHistoryService.get() method in reportResults.js is called before it is set in buildReport.js.
It would be better if you announce when the previousLocation variable has been set.
In reportView.js
angular.module('reportView', [])
.service('locationHistoryService',['$rootScope'] ,function($rootScope){
var previousLocation = "";
return {
store: function(location){
previousLocation = location;
$rootScope.$broadcast('previous-location-set');
},
get: function(){
return previousLocation;
}
};
});
In reportResults.js
angular.module('reportResults', ['reportView'])
.controller('reportResultsController', ['$rootScope', 'locationHistoryService'], function($rootScope, locationHistoryService){
$rootScope.$on('previous-location-set', function(){
console.log("location : ", locationHistoryService.get());
});
}
Your webapp should have only one module which is automatically bootstrapped by angular, and other modules as dependencies. The syntax of writing service is incorrect. You wrote .service but returning object which .factory should return. Here is working example of your code http://codepen.io/Chyngyz/pen/NxbdpW?editors=101
Also you wrote the safe for minification syntax of controllers incorrect, the function block of controller should be the last item in the array.
I am using amplify.js with Knockout.js and I want to store data locally. I tried using this code: amplify guide
but it isn't working for me.
My view model
define(['services/datacontext'], function (dataContext) {
var store = amplify.store("offlineData"); // Why is agency undefined after retrieving from the store?!?!?!
var agency = ko.observableArray([]);
var initialized = false;
var save = function (agency) {
return dataContext.saveChanges(agency);
};
var vm = { // This is my view model, my functions are bound to it.
//These are wired up to my agency view
activate: activate,
agency: agency,
title: 'agency',
refresh: refresh, // call refresh function which calls get Agencies
save: save
};
return vm;
function activate() {
if (initialized) {
return;
}
initialized = true;
if (initialized == true) {
amplify.store("offlineData", vm.agency);
}
return refresh();
}
function refresh() {
return dataContext.getAgency(agency);
}
});
After refresh retrieves the data, I save this data to the local store. So when I make another request for this page. I would expect var store to contain this data but it is undefined.
Does anyone know how to use amplify?
amplify.store("offlineData", vm.agency);
vm.agency is a function, therefore you need to invoke it to get its value
amplify.store("offlineData", vm.agency());
How do I save URL parameters state throughout lifecycle of application using pushState?
Page load.
Go to "/search" via href
submitSearch() through filter fields where $location.search(fields)
Go to "/anotherPage" via href
Go back to "/search" via href
Search paramters are set back to what they last were.
Is this a built in feature somewhere?
If not what's the best way to go about this?
If you're planning on a mostly single page website through pushState, you might want to get an intimate understanding of $routeProvider (http://docs.angularjs.org/api/ngRoute.%24routeProvider).
To go further down the rabbit hole, I would recommend looking at the ui-router module: (https://github.com/angular-ui/ui-router). $stateProvider (from ui-router) and $routeProvider work very similar, so sometimes the ui-router docs can give insights that you can't find in the poor documentation of the $routeProvider.
I reccomend going through the five page ui-router documentation (https://github.com/angular-ui/ui-router/wiki) page by page.
After all that preamble, here's the practical: you would set up a factory that holds history data and use the controller defined in your $routeProvider/$stateProvider to access and manipulate that data.
Note: the factory is a service. A service is not always a factory. The namespace goes:
angular.module.<servicetype[factory|provider|service]>.
This post explains the service types: https://stackoverflow.com/a/15666049/2297328. It's important to remember that they're all singletons.
Ex:
var myApp = angular.module("myApp",[]);
myApp.factory("Name", function(){
return factoryObject
});
The code would look something like:
// Warning: pseudo-code
// Defining states
$stateProvider
.state("root", {
url: "/",
// Any service can be injected into this controller.
// You can also define the controller separately and use
// "controller: "<NameOfController>" to reference it.
controller: function(History){
// History.header factory
History.pages.push(History.currentPage);
History.currentPage = "/";
}
})
.state("search", {
url: "/search",
controller: function(History, $routeParams) {
History.lastSearch = $routeParams
}
});
app.factory('<FactoryName>',function(){
var serviceObjectSingleton = {
pages: []
currentPage: ""
lastSearch: {}
}
return serviceObjectSingleton
})
If you're wondering what the difference between $routeProvider and $stateProvider is, it's just that $stateProvider has more features, mainly nested states and views... I think.
The easiest way is using cookies, angularjs provides a wrapping service for that.
Simply when you go to "/search" save your current URL parameters with "$cookieStore.put()" and once you've back you've got what you need with "$cookieStore.get()".
See the documentation at angularjs cookie store
I made a locationState service, you simply give it the values you want to persist and it stores them in the URL. So you can store all the state you want across all routes in your app.
Use it like this:
angular.module('yourapp')
.controller('YourCtrl', function ($scope, locationState) {
var size = locationState.get('size');
;
// ... init your scope here
if (size) {
$scope.size = size;
}
// ...and watch for changes
$scope.$watch('size', locationState.setter('size'));
}
Here's the code:
// Store state in the url search string, JSON encoded per var
// This usurps the search string so don't use it for anything else
// Simple get()/set() semantics
// Also provides a setter that you can feed to $watch
angular.module('yourapp')
.service('locationState', function ($location, $rootScope) {
var searchVars = $location.search()
, state = {}
, key
, value
, dateVal
;
// Parse search string
for (var k in searchVars) {
key = decodeURIComponent(k);
try {
value = JSON.parse(decodeURIComponent(searchVars[k]));
} catch (e) {
// ignore this key+value
continue;
}
// If it smells like a date, parse it
if (/[0-9T:.-]{23}Z/.test(value)) {
dateVal = new Date(value);
// Annoying way to test for valid date
if (!isNaN(dateVal.getTime())) {
value = dateVal;
}
}
state[key] = value;
}
$rootScope.$on('$routeChangeSuccess', function() {
$location.search(searchVars);
});
this.get = function (key) {
return state[key];
};
this.set = function (key, value) {
state[key] = value;
searchVars[encodeURIComponent(key)] = JSON.stringify(value);
// TODO verify that all the URI encoding etc works. Is there a mock $location?
$location.search(searchVars);
};
this.setter = function (key) {
var _this = this;
return function (value) {
_this.set(key, value);
};
};
});