view not updating in angularjs after a promise is resolved - angularjs

I have a factory which fetches data from server and passes to controllers , many controllers are calling get methods from the factory , but only the scope variable of controller that calls the factory first is changed in view not all... here is my factory code
(function () {
angular.module('myapp').factory('agendaFactory', ['$http', '$q', function ($http, $q) {
var user = "";
var defer = $q.defer();
var agenda = {
getAll: function (tempData, addType) {
$http.post('/api/' + addType + '/getAll', tempData).success(function (data, status) {
console.log(addType, data);
//console.log(status);
defer.resolve(data);
}).error(function (err, status) {
console.log(err, status);
defer.reject(status);
});
return defer.promise;
}
};
return agenda;
}]);
}());
here are the two controllers calling them..
changeData = function (data) {
console.log("called function in liffe");
$scope.lifetimeData = data;
}
agendaFactory.getAll(tempData, addType).then(function (data) {
console.log('lifeData', data);
changeData(data);
return;
}, function (err) {
console.log('today err', err);
})
and second controller
changeData = function (data) {
console.log("called function in year");
$scope.yearData = data;
}
agendaFactory.getAll(tempData, addType).then(function (data) {
console.log('yeardata', data);
changeData(data);
return;
}, function (err) {
console.log('today err', err);
});
but data from only first controller is being updated in the view

Remove the promise wrapper since $http already returns a promise. Also get rid of the success/error handlers since they're deprecated.
(function () {
angular.module('myapp').factory('agendaFactory', ['$http', '$q', function ($http, $q) {
var user = "";
var agenda = {
getAll: function (tempData, addType) {
return $http.post('/api/' + addType + '/getAll', tempData);
}
};
return agenda;
}]);
}());
You can then handle the success/error conditions in your controller

Related

AngularJS get value from service function

I have a problem with getting value from service in my controller.
I get the value from API using service:
angular.module('app').factory('service',
['$q', '$rootScope', '$timeout', '$http',
function ($q, $rootScope, $timeout, $http) {
// create user variable
var user = null;
// return available functions for use in the controllers
return ({
isLoggedIn: isLoggedIn,
getUserStatus: getUserStatus,
login: login,
getEmail: getEmail
});
function isLoggedIn() {
if(user) {
return true;
} else {
return false;
}
}
function getUserStatus() {
return $http.get('/user/status')
// handle success
.success(function (data) {
if(data.status){
user = true;
} else {
user = false;
}
})
// handle error
.error(function (data) {
user = false;
});
}
function login(username, password) {
// create a new instance of deferred
var deferred = $q.defer();
// send a post request to the server
$http.post('/user/login',
{username: username, password: password})
// handle success
.success(function (data, status) {
if(status === 200 && data.status){
user = true;
deferred.resolve();
} else {
user = false;
deferred.reject();
}
})
// handle error
.error(function (data) {
user = false;
deferred.reject();
});
// return promise object
return deferred.promise;
}
function getEmail() {
// create a new instance of deferred
var deferred = $q.defer();
$http.get('/email/'+$rootScope.username)
.success(function (data) {
console.log(data);
deferred.resolve();
})
.error(function (data) {
deferred.reject();
})
return deferred.promise;
}
}]);
and I'm trying to get and use value in the controller:
angular.module('app')
.controller('myController', ['$rootScope', '$scope', '$state', '$http', '$q', 'service', function ($rootScope, $scope, $state, $http, $q, service) {
$scope.email;
$scope.getEmail = function() {
// call getEmailFromDB from service
service.getEmail()
// handle success
.then(function (data) {
$scope.email = data; //here I got undefined
console.log($scope.email);
})
// handle error
.catch(function () {
$scope.error = true;
$scope.errorMessage = "wrong#mail.com";
});
};
$scope.getEmail();
}]);
but in the controller there is undefined value.
In my console:
Regarding the documentation of $q, I use then() function in service and controller.
Does anybody know where is the problem?
you forgot to send your result in your service like this
deferred.resolve(data);
In your code you are not returning anything with the promise, to do that you must call deferred.resolve(data) or deferred.reject(data).
Instead of creating a deferred object, you can simply return the $http request.
Example:
function getEmail() {
return $http.get('/email/'+$rootScope.username).then(
function success(data){
console.log(data);
return data;
},
function error(data){
console.error(data);
}
);
}
Also notice that .success() and .error() are deprecated, you should use .then() and pass the success and error functions.

Passing service data to controller ng

When I get the data from the service using getAllGenres everything works fine, but when I try to pass the parameter vm.id the data is of type undefined. Please help. P.S. from an Angular new comer
genresService:
angular.module('movies.services', [])
.factory('genresService', ['$http', function ($http) {
function callback () {
// TODO: Implement callback function
}
function getAllGenres (callback) {
var url = '/api/genres';
$http.get(url)
.success(function (data) {
callback(data);
})
.error(function (error) {
console.log(error);
})
}
function getGenreById (id) {
var url = '/api/genres/' + id;
console.log(id);
$http.get(url)
.success(function (data) {
callback(data);
})
.error(function (error) {
console.log(error);
})
}
return {
getAllGenres: getAllGenres,
getGenreById: getGenreById
};
}]);
GenresController:
.controller('GenresController', ['$scope', 'genresService',
function ($scope, genresService) {
var vm = this;
genresService.getAllGenres(function (data) {
vm.genres = data;
});
}])
GenresDetailsController:
.controller('GenreDetailsController', ['$routeParams', 'genresService',
function ($routeParams, genresService) {
var vm = this;
vm.id = $routeParams.id;
genresService.getGenreById(vm.id, function (data) {
console.log(data);
})
}]);

How can I use angularjs promise chain in a service and controller?

I found this plnkr link on the web but I need use it with 2 or 3 more ajax calls which doesn't require an argument from the first ajax call. How can I do it with error handling?
var app = angular.module("app", []);
app.service("githubService", function($http, $q) {
var deferred = $q.defer();
this.getAccount = function() {
return $http.get('https://api.github.com/users/haroldrv')
.then(function(response) {
// promise is fulfilled
deferred.resolve(response.data);
return deferred.promise;
}, function(response) {
// the following line rejects the promise
deferred.reject(response);
return deferred.promise;
});
};
});
app.controller("promiseController", function($scope, $q, githubService) {
githubService.getAccount()
.then(
function(result) {
// promise was fullfilled (regardless of outcome)
// checks for information will be peformed here
$scope.account = result;
},
function(error) {
// handle errors here
console.log(error.statusText);
}
);
});
http://plnkr.co/edit/kACAcbCUIGSLRHV0qojK?p=preview
You can use $q.all
var promises=[
$http.get(URL1),
$http.get(URL2),
$http.get(URL3),
$http.get(URL4)
];
$q.all(promises).then(function(response){
console.log('Response of Url1', response[0]);
console.log('Response of Url2', response[1]);
console.log('Response of Url3', response[2]);
console.log('Response of Url4', response[3]);
}, function(error){
});
I have forked your plunkr with $q
First you should make deferred variable local for each ajax call you want to return a promise. So, you have to create 2-3 functions (as many as your ajax calls) and keep them in an array. Then you should use:
$q.all([ajax1,ajax2,ajax3]).then(function(values){
console.log(values[0]); // value ajax1
console.log(values[1]); // value ajax2
console.log(values[2]);}); //value ajax3
example:
function ajax_N() {
var deferred = $q.defer();
http(...).then((response) => {
deferred.resolve(response);
}, (error) => {
deferred.reject(error);
});
return deferred.promise;
}
$q.all([
ajax_1,ajax_2,ajax_3
]).then(function(values) {
console.log(values);
return values;
});
Use $q.all in this case. It will call getAccount and getSomeThing api same time.
var app = angular.module("app", []);
app.service("githubService", function($http, $q) {
return {
getAccount: function () {
return $http.get('https://api.github.com/users/haroldrv');
},
getSomeThing: function () {
return $http.get('some thing url');
}
};
});
app.controller("promiseController", function($scope, $q, githubService) {
function initData () {
$q.all([githubService.getAccount(), githubService.getSomeThing()])
.then(
function (data) {
$scope.account = data[0];
$scope.someThing = data[1];
},
function (error) {
}
);
}
});

How to set a variable from an $http call then use it in the rest of the application WITHOUT making the whole application asynchronous

I have this data
{
"config": {
"RESTAPIURL": "http://myserver/myrestsite"
}
}
and I have this factory that reads that data
'use strict';
angular.module('myApp').factory('api',
["$http", "$q",
function ($http, $q) {
function _getConfiguration() {
var deferred = $q.defer();
$http.get('/scripts/constants/config.json')
.success(function (data) {
deferred.resolve(data);
})
.error(function (data, status) {
deferred.reject(data, status);
});
return deferred.promise;
}
function _restApiUrl() {
// this doesn't work either. _getConfiguration() doesn't resolve here.
return _getConfiguration().RESTAPIURL + '/api/';
}
return {
URL: _restApiUrl
}
}
]
);
Then to use it
'use strict';
angular.module('myApp').factory('AuthService', function ($http, $q, api,NotificationService) {
function _get(creds) {
var deferred = $q.defer();
$http({method: 'GET', url: api.URL() + api.AUTH, headers: {
'Authorization': 'Basic '+creds}
})
.success(function (data, status, results, headers) {
deferred.resolve(results);
})
.error(function (data, status) {
NotificationService.redirect(status);
deferred.reject(data, status);
});
return deferred.promise;
}
return {
get:_get
};
});
So when I'm using it I am doing api.URL() and it's not working.
It used to be hard coded URL so to call it used to be api.URL. I really don't want to go through the whole app and convert everything to api.URL().then(...). That would suck.
So how can I nail down this value as a "property" instead of an asynchronous promise that has to be called over and over?
Call it once, fine. Get the value. Put it somewhere. Use the value. Don't ever call the $http again after that.
EDIT
This is turning up to be one of the most successful questions I've ever asked, and I am gratefully going through each answer in turn. Thank each one of you.
Adding a bit to what #ThinkingMedia was saying in the comment, with ui-router when defining controllers you can add a resolve parameter.
In it you can specify some promises that have to resolve before the controller is instantiated, thus you are always sure that the config object is available to the controller or other services that the controller is using.
You can also have parent/child controllers in ui-router so you could have a RootController that resolves the config object and all other controllers inheriting from RootController
.state('root', {
abstract: true,
template: '<ui-view></ui-view>',
controller: 'RootController',
resolve:{
config: ['api', function(api){
return api.initialize();
}
}
});
and your api factory:
angular.module('myApp').factory('api',
["$http", "$q",
function ($http, $q) {
var _configObject = null;
function initialize() {
return $http.get('/scripts/constants/config.json')
.then(function (data) {
_configObject = data;
return data;
});
}
// you can call this in other services to get the config object. No need to initialize again
function getConfig() {
return _configObject;
}
return {
initialize: initialize,
getConfig: getConfig
}
}
]
);
I would pass a callback to the getURL method, and save the URL when it returns. Then I would attach any subsequent requests to that callback. Here I am assuming that you are doing something similar with api.AUTH that you don't have a reference to in your code.
Pass a callback to the getURL method in the api service.
angular.module('myApp').factory('api', ["$http", "$q",
function ($http, $q) {
function _getConfiguration() {
var deferred = $q.defer();
$http.get('/scripts/constants/config.json')
.success(function (data) {
deferred.resolve(data);
})
.error(function (data, status) {
deferred.reject(data, status);
});
return deferred.promise;
}
return {
getURL: function (cb) {
var that = this;
if (that.URL) {
return cb(that.URL);
}
_.getConfiguration().then(function (data) {
that.URL = data.config.RESTAPIURL + "/api";
cb(that.URL);
});
}
}
}]);
And in your AuthService, wrap your _get inside a callback like this:
angular.module('myApp').factory('AuthService', function ($http, $q, api, NotificationService) {
function _get(creds) {
var deferred = $q.defer();
var getCallback = function (url) {
$http({
method: 'GET',
url: url + api.AUTH,
headers: {
'Authorization': 'Basic ' + creds
}
})
.success(function (data, status, results, headers) {
deferred.resolve(results);
})
.error(function (data, status) {
NotificationService.redirect(status);
deferred.reject(data, status);
});
};
api.getURL(getCallback);
return deferred.promise;
}
return {
get: _get
};
});
Why don't you initialize the factory when the app is loading and put the variable onto another property? Something like this:
angular.module('myApp').factory('api', ["$http", "$q",
function ($http, $q) {
// store URL in a variable within the factory
var _URL;
function _initFactory() {
var deferred = $q.defer();
$http.get('/scripts/constants/config.json')
.success(function (data) {
// Set your variable after the data is received
_URL = data.RESTAPIURL;
deferred.resolve(data);
});
return deferred.promise;
}
function getURL() {
return _URL;
}
return {
initFactory: _initFactory,
URL: getURL
}
}
]
);
// While the app is initializing a main controller, or w/e you may do, run initFactory
//...
api.initFactory().then(
// may not need to do this if the URL isn't used during other initialization
)
//...
// then to use the variable later
function _get(creds) {
var deferred = $q.defer();
$http({method: 'GET', url: api.URL + api.AUTH, headers: {
'Authorization': 'Basic '+creds}
})
.success(function (data, status, results, headers) {
deferred.resolve(results);
})
return deferred.promise;
}
I see you haven't used any $resource's here, but I'm hoping you have a good understanding of them:
in factories/delay-resource.js:
'use strict'
angular.module('myApp').factory('delayResource', ['$resource', '$q',
function($resource, $q){
var _methods = ['query', 'get', 'delete', 'remove', 'save'];
var shallowClearAndCopy = function(src, dst) {
dst = dst || {};
angular.forEach(dst, function(value, key){
delete dst[key];
});
for (var key in src) {
if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
dst[key] = src[key];
}
}
return dst;
}
var delayResourceFactory = function(baseUrlPromise, url, paramDefaults){
var _baseUrlPromise = baseUrlPromise,
_url = url,
_paramDefaults = paramDefaults;
var DelayResource = function(value){
shallowClearAndCopy(value || {}, this);
};
_methods.forEach(function(method){
DelayResource[method] = function(params, successCB, errCB, progressCB){
if (angular.isFunction(params)) {
progressCB = successCB;
errCB = errHandlers;
successCB = params;
errHandlers = params = null;
}
else if (!params || angular.isFunction(params)){
progressCB = errCB;
errCB = successCB;
successCB = errHandlers;
params = {};
}
var _makeResultResource = function(url){
var promise = $resource(url, _paramDefaults)[method](params);
(promise.$promise || promise).then(
function successHandler(){
var data = arguments[0];
if (isInstance){
if (angular.isArray(data))
for (var i = 0; i < data.length; i++)
data[i] = new DelayResource(data[i])
else if (angular.isObject(data))
data = new DelayResource(data)
}
successCB.apply(successCB, arguments)
resultDelay.resolve.apply(resultDelay.resolve, arguments)
},
function(err){
errCB.apply(errCB, arguments)
resultDelay.reject.apply(resultDelay.reject, args)
},
function(){
progressCB.apply(progressCB, arguments)
resultDelay.notify.apply(resultDelay.notify, arguments)
}
)
}
var isInstance = this instanceof DelayResource,
resultDelay = $q.defer();
if (!angular.isString(_baseUrlPromise) && angular.isFunction(_baseUrlPromise.then))
_baseUrlPromise.then(
function successCb(apiObj){
_makeResultResource(apiObj.RESTAPIURL + _url)
},
function successCb(){
throw 'ERROR - ' + JSON.stringify(arguments, null, 4)
})
else
_makeResultResource(_baseUrlPromise.RESTAPIURL + _url);
return resultDelay.promise;
};
DelayResource.prototype['$' + method] = function(){
var value = DelayResource[method].apply(DelayResource[method], arguments);
return value.$promise || value;
}
});
return DelayResource;
}
return delayResourceFactory;
}]);
This will be the base factory that all requests to that REST API server will go through.
Then we need a factories/api-resource.js:
angular.module('myApp').factory('apiResource', ['delayResource', 'api', function (delayResource, api) {
return function (url, params) {
return delayResource(api.URL(), url, params);
};
}])
Now all factories created will just have to call the apiResource to get a handle on a resource that will communicate with the REST API
Then in a file like factories/account-factory.js
angular.module('myApp').factory('AuthRoute', ['apiResource', 'api', function (apiResource, api) {
return apiResource(api.AUTH);
}]);
Now in factories/auth-service.js:
'use strict';
angular.module('myApp').factory('AuthService', ['$q', 'AuthRoute', 'NotificationService', function ($q, AuthRoute, api, NotificationService) {
function _get(creds) {
var deferred = $q.defer();
AuthRoute.get()
.then(
function successCb(results){
deferred.resolve(results);
},
function errCb(){
// cant remember what comes into this function
// but handle your error appropriately here
//NotificationService.redirect(status);
//deferred.reject(data, status);
}
);
return deferred.promise;
}
return {
get:_get
};
}]);
As you can imagine, I haven't been able to test it yet, but this is the basis. I'm going to try create a scenario that will allow me to test this. In the mean time, feel free to ask questions or point out mistakes made
Late Addition
Forgot to add this:
'use strict';
angular.module('myApp').factory('api', ["$http", "$q", function ($http, $q) {
var restApiObj,
promise;
function _getConfiguration() {
if (restApiObj)
return restApiObj;
if (promise)
return promise;
promise = $http.get('/scripts/constants/config.json')
.then(function (data) {
restApiObj = data;
promise = null;
return data;
},
function (data, status) {
restApiObj = null;
promise = null;
});
return promise;
}
return {
URL: _getConfiguration
}
}]);
Continuing with the ui-router scenario
.state('member-list', {
url: '/members?limit=&skip='
templateUrl: '/views/members/list.html',
controller: 'MemberListCtrl',
resolve:{
members: ['$stateParams', 'MembersLoader', function($stateParams,MembersLoader){
return MembersLoader({skip: $stateParams.skip || 0, limit: $stateParams.limit || 10});
}
}
});
factory
.factory('MemberRoute', ['apiResource', function(apiResource){
return apiResource('/members/:id', { id: '#id' });
}])
.factory('MembersLoader', ['MembersRoute', function(MembersRoute){
return function(params){
return MemberRoute.query(params);
};
}])
.factory('MemberFollowRoute', ['apiResource', 'api', function(apiResource, api){
return apiResource(api.FOLLOW_MEMBER, { id: '#id' });
}])
controller
.controller('MemberListCtrl', ['$scope', 'members', 'MemberRoute', 'MemberFollowRoute', function($scope, members, MemberRoute, MemberFollowRoute){
$scope.members = members;
$scope.followMember = function(memberId){
MemberFollowRoute.save(
{ id: memberId },
function successCb(){
//Handle your success, possibly with notificationService
},
function errCb(){
// error, something happened that doesn't allow you to follow memberId
//handle this, possibly with notificationService
}
)
};
$scope.unfollowMember = function(memberId){
MemberFollowRoute.delete(
{ id: memberId },
function successCb(){
//Handle your success, possibly with notificationService
},
function errCb(){
// error, something happened that doesn't allow you to unfollow memberId
//handle this, possibly with notificationService
}
)
};
}]);
With all this code above, you will never need to do any sort of initialization on app start, or in some abstract root state. If you were to destroy your API config every 5 mins, there would be no need to manually re-initialize that object and hope that something isn't busy or in need of it while you fetch the config again.
Also, if you look at MembersRoute factory, the apiResource abstracts/obscures the api.URL() that you were hoping not to have to change everywhere. So now, you just provide the url that you want to make your request to, (eg: /members/:id or api.AUTH) and never have to worry about api.URL() again :)

How to implement a generic HTTP service in AngularJS?

In my angular module I wrote a generic http handler for all my ajax requests.'
I was expecting that I could use the service across controllers, but my problem is the promise seems to be global.
Once ControllerOne uses the mapi_loader service, when I load AnotherController (by ng-click="go('/$route_to_load_another_controller')"), AnotherController is loaded a promise that has already returned from ControllerOne even though the URL they fetch are totally different.
So I guess my question is how do I write a service I could use across controllers? Do I really need to write a separate service for each controller where their only difference in code is the URL passed for $http.jsonp?
angular.module('myAppControllers',[])
.service('mapi_loader', ['$http', function($http) {
var promise;
var myService = {
fetch: function(url) {
if ( !promise ) {
promise = $http.jsonp(url)
.then(function (response) {
return response.data.nodes;
});
}
return promise;
}
};
return myService;
}])
.controller('ControllerOne', ['$scope', 'mapi_loader', function ($scope, mapi_loader) {
mapi_loader
.fetch("http://host.com/mapi_data_for_controller_one?callback=JSON_CALLBACK")
.then(function(data) {
$scope.useme = data;
});
}])
.controller('AnotherController', ['$scope', 'mapi_loader', function ($scope, mapi_loader) {
mapi_loader
.fetch("http://host.com/mapi_data_for_another_controller?callback=JSON_CALLBACK")
.then(function(data) {
$scope.useme = data;
});
}])
;
try something like this
angular.module('myAppControllers',[])
.service('mapi_loader', function($http) {
var alreadyLoading = {};
return {
fetch: function(url) {
if ( url in alreadyLoading ) {
return alreadyLoading[url];
}
return alreadyLoading[url] = $http.jsonp(url)
.then(function (response) {
delete alreadyLoading[url];
return response.data.nodes;
});
}
};
})
.controller('ControllerOne', function ($scope, mapi_loader) {
...
})
.controller('AnotherController', function ($scope, mapi_loader) {
...
});

Resources