I have an idea why it's not working, but not exactly how to fix this, and I've looked at the various similar questions/answers on SO but I can not fix my issue.
I am trying to call a service on page load to list a table of data, or let's just call this a list of urls.
The model would simply be { urls } would should return an array of urls, where in the future I'd use ng-repeat.
My controller:
function UrlListCtrl($scope, UrlService){
$scope.urls = UrlService.getUrls();
}
Service:
function UrlService($http) {
return {
getUrls: getUrls
}
function getUrls() {
console.log('Fetching Urls...');
return $http.get('urls/get')
.then(function(response) {
console.log('response:', response.data);
this.urlsResults = response.data;
});
}
}
The service does run, however it doesn't update the view. How can I fix this?
$http.get() is an async operation that returns a promise. You have to wait for this promise to be fulfilled, and then you can get the data from the response.
In your code $scope.urls = UrlService.getUrls(); was assigning the promise, and not it's results to $scope.urls, and the view was never updated when the operation ended.
Controller:
function UrlListCtrl($scope, UrlService){
UrlService.getUrls().then(function(response) {
$scope.urls = response.data;
});
}
Service:
function UrlService($http) {
return {
getUrls: getUrls
}
function getUrls() {
console.log('Fetching Urls...');
return $http.get('urls/get');
}
}
Related
I have the following factory:
.factory('Request', ['$resource', 'general',
function ($resource) {
return $resource(baseURL + ':resourceName/', {}, {
get : {
method : 'GET',
isArray : true,
transformResponse : function (data, headers) {
return JSON.parse(data).data;
},
interceptor: {
responseError : function (data) {
gnrl.logIn({},false,function(){console.log("test");});
// ???? How to recall this request?
}
}
}
});
}
]);
What I'm trying to do is, if the user has some trouble in this request, the user should login again and the request should be executed again.
The request factory is called as follows:
Request.get(params, headers, function (res) {
//does some operation with results
});
I have seen some related questions but none of them could fit my situation completely.
It is solved using the answers referred in the question. As said, the main idea was using $promise instead of interceptor.
I have solved the problem as follows:
The following is a function in my service
self.doRequest = function(nOfRetries, params, successFunction) {
function fai(a) {
nOfRetries--;
self.logIn(function(){self.doRequest(nOfRetries);});
}
if (nOfRetries >= 0) {
Request.get(params).then(successFunction).catch(fai);
}
}
Login is just another function in the same service and as a callback function i send a request to this function.
doRequest is called as follows:
general.doRequest(3, params/*parameters used in request*/, function (res) {
/***cb function for your initially request*/
});
As last, you see the Request factory:
.factory('Request', ['$resource',
function ($resource) {
var res = $resource(baseURL + ':resourceName/', {}, {
get : {
method : 'GET'
}
});
return {
get : function (arguments) {
return res.get(arguments).$promise;
}
};
}
])
Note that any error (server-side or not) occurred in doRequest's success callback function will also lead to executing failure callback function.
.factory('ChartService', ['$http','$q',
function ChartService($http,$q) {
// interface
// implementation
var canceler = $q.defer();
function getTableData() {
return $http.post('http://202.429.115.52:9906/oo/api.php?request=getSubfunctionWiseHCAndSW').success(function (data) {
if (data.mm == "No Data Available"){
localData();
}
return data;
}).error(function(error){
alert("error")
canceler.resolve();
localData()
// console.log(error)
});
}
function localData(){
alert("loaddata")
return $http.get('vro/hcswc.json').success(function(response){
console.log(response+"==");
return response;
}).error(function(error){
console.log(error);
});
}
return {
getTableData:getTableData
}
}
]);
Error
XMLHttpRequest cannot load http://192.127.215.52:9906/api.php?request=getSubfunctionrection. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:63342' is therefore not allowed access. The response had HTTP status code 404.
App.js // resolve Code
.state('app.vro', {
url: "/vro/:isfirstActiveState",
views: {
'menuContent': {
controller: "vrobCtrl",
templateUrl: 'vrob/vrob.html'
}
} ,
resolve: {
tableData:function(ChartService){
return ChartService.getTableData();
}
}
})
Hello
I am trying to get data form service .I have one condition while calling webservice .the condition is if I will get error then I get request from local in other words if I get any error from server than I need to read json file from local . I need to use resolve and call my service and use that data on controller .my localData is not returning data to resolve .what is the problem in my code ..
here is one example
http://plnkr.co/edit/0y9V0m2hmsUBRXoeyjig?p=preview
I am getting error correct but it should load data from local json .why it is not loading data from local json
It is an anti-pattern to use $q to manage $http requests but in this situation I don't know another way:
function getTableData() {
var deferred = $q.defer();
$http.post('http://202.129.215.52:9906/mondelez/api.php?request=getSubfunctionWiseHCAndSW')
.then(
// resolve callback
function(data) {
deferred.resolve(data.data)
return data.data;
},
// reject callback make different request
function(err) {
$http.get('data.json').success(function(response) {
deferred.resolve(response);
}).error(function(error) {
deferred.reject()
});
})
return deferred.promise;
}
DEMO
I'm trying to chain a promise with Angular $resource.
I have the following factory:
angular.module('myApp').factory('Product', ['$resource', function ($resource) {
return $resource(
'/api/product/:name',
{ name: '#name' },
{ 'getSub': {
url: '/api/product/getSub/:name',
method: 'GET'}
}
);
}]);
I make multiple queries using my Product factory as such:
Product.query({'name': name}, function(product) {
Product.getSub({'name': product.name}, function(subItem) {
Product.getSub({'name':subItem.name}, function(childItem) {
//do stuff with child item
})
})
})
Is there a better way to do this? I feel like nesting all these calls is not a best practice.
You can chain the promises together!
Product.query({'name': name}).$promise
.then(function(product){
return Product.getSub({'name': product.name}).$promise;
})
.then(function(subItem){
return Product.getSub({'name': subItem.name}).$promise;
})
.then(function(item){
// etc
})
you can use waterfall of async library or implement it yourself.
here's sample code for your case.
async.waterfall([
function(callback) {
Product.query({'name': name}, function(product) {
callback(null, product);
})
},
function(product, callback) {
Product.getSub({'name': product.name}, function(subItem) {
callback(null, product, subItem);
})
},
function(product, subItem, callback) {
Product.getSub({'name':subItem.name}, function(childItem) {
var result = {};
result.childItem = childItem;
result.subItem = subItem;
result.product = product;
callback(null, result);
})
}
], function (err, result) {
//do stuff with result
});
If you want the requests to be done one after another (like you have in your example) you could do a recursive function like this:
in this example i want to upload a couple of images (calling a http route):
$scope.uploadImageLayout = function (currentIndex, numberOfItems) {
if (currentIndex === numberOfItems) {
// in here you could do some last code after everything is done
} else {
Upload.upload({
url: 'localhost:3000/ficheiros',
file: $scope.imagesToUpload[$scope.auxIndex].file
}).success(function (data, status, headers, config) {
if ($scope.auxIndex < numberOfItems) {
$scope.uploadImageLayout(currentIndex + 1, numberOfItems);
}
});
}
};
and the first time you call just do this:
$scope.uploadImageLayout(0, $scope.imagesToUpload.length);
in you case its the same but instead of the Upload.upload request you should have your request and catch the callback function(s).
A useful solution maybe use $q library
https://docs.angularjs.org/api/ng/service/$q
You can use the method $q.all() to send a lot of request and manage only one callback then() or make $q.defer() and resolve por reject your oun promises.
I currently answer this question from a mobile device and i can't make an example. Sorry about that.
If when I get home that mistake trains still try to help
Let's say my current route is /books and I make an $http call to get all of the books we want to show a user. Normally, the call would resolve quickly and the books would be ng-repeated into the DOM. When we have an error, though (such as a timeout or there are no books returned), we update a common, global view that will overlay the content view and display a message like, "There are no books available." The common view is handled via a service with methods like CommonView.showLoading(), CommonView.showError("There are no books available."), and CommonView.hide(), etc.
Recently, I discovered that if the $http is not resolved quickly, the user may leave and go to another route (maybe /dinosaurs). Eventually, when the $http ends up resolving or being rejected, the promise call to display that common, global view will happen, resulting in an error view being displayed when there shouldn't be one, and the error will make no sense to the user (ie, user is at /dinosaurs and the error screen pops up with "There are no books available.").
I've seen that you can cancel an $http with a timeout promise, but this still seems like it could lead to race conditions (maybe you call cancel after processing of the resolve() or reject() has begun). I think it would be messy to have to check that the current route matches the route the $http was initiated from.
It seems like there should be some standard way to destroy $http calls on a route change or from a controller's $destroy method. I'd really like to avoid adding a lot of conditionals all over my gigantic app.
I can't find a great way to stop the processing of my callback if it's already started, but here's the $http wrapper I made to try and stop delayed callbacks from getting called after route changes. It doesn't replicate all of the $http methods, just the ones I needed. I haven't fully tested it, either. I've only verified that it will work in normal conditions (normal bandwidth with standard calls, ie httpWrapper.get(url).success(cb).error(err)). Your mileage may vary.
angular.module('httpWrapper', []).provider('httpWrapper', function() {
this.$get = ['$rootScope','$http','$q', function($rootScope, $http, $q) {
var $httpWrapper = function(config) {
var deferred = $q.defer();
var hasChangedRoute = false;
var canceler = $q.defer();
var http = null;
var evListener = null;
var promise = deferred.promise;
if ((config || {}).timeout && typeof config.timeout === 'Object') {
// timeout promise already exists
canceler.promise = config.timeout;
} else {
angular.extend(config || {}, {
timeout: canceler.promise
});
}
http = $http(config)
.success(function(data, status, headers, config) {
// only call back if we haven't changed routes
if (!hasChangedRoute) {
deferred.resolve({data:data, status:status, headers:headers, config:config});
}
})
.error(function(data, status, headers, config) {
// only call back if we haven't changed routes
if (!hasChangedRoute) {
deferred.reject({data:data, status:status, headers:headers, config:config});
}
});
evListener = $rootScope.$on('$locationChangeStart', function(scope, next, current) {
hasChangedRoute = true;
canceler.resolve('killing http');
evListener(); // should unregister listener
})
promise.success = function(fn) {
promise.then(function(response) {
fn(response.data, response.status, response.headers, config);
});
return promise;
};
promise.error = function(fn) {
promise.then(null, function(response) {
fn(response.data, response.status, response.headers, config);
});
return promise;
}
return promise;
};
angular.forEach(['get', 'delete', 'head', 'jsonp'], function(method) {
$httpWrapper[method] = function(url, config) {
return $httpWrapper(
angular.extend(config || {}, {
method: method,
url: url
})
);
};
});
angular.forEach(['post', 'put'], function(method) {
$httpWrapper[method] = function(url, data, config) {
return $httpWrapper(
angular.extend(config || {}, {
method: method,
url: url,
data: data
})
);
};
});
return $httpWrapper;
}];
});
I'm really struggling with the promises here.
It's more than a day now, and I still can't figure it out.
I'm new to Angular and, more in general, to the promises "concept", so I'm sure there is something I'm missing, but I can't figure it out.
Basically, I'm calling a remote web service using a $post request, and in the success method I update some data on $rootScope
LoginService
this.login = function(url, request) {
return $http.post( url, request ).
success(function(data) {
if (data.return_code === 0) {
userData = {
name: data.name,
role: data.role
}
/*
* Inside this function, $rootScope gets
* updated with userData
*/
storage.update('user_details', userData)
}
else {
// non authorized user
}
return userData
}).
error(function(data, status) {
throw new Error()
})
}
Then, in the controller, I do something like
$scope.login = function(url, request) {
loginService.login(url, request).then(function(response) {
/* this is a ui.router redirection to the state 'home' */
$state.go('home')
})
}
The problem is that in the new page (the home state), $rootScope is not updated, unless I do a page reload, which "solves" the issue.
It seems to me that the call of the promise does not wait for it's completion for the page redirect, but even wrapping $state.go in a $timeout doesn't solve the issue...
I'm really lost, any help would be appreciated
If storage update is some async action you want to wait for until it is complete and it returns a promise then you can simply chain it:
this.login = function(url, request) {
return $http.post(url, request).then(function(data){
if (data.return_code === 0) {
var userData = {
name: data.name,
role: data.role
};
return storage.update('user_details', userData).then(function(){
return userData;
});
}
else {
}
})
};
In usage:
loginService.login(...).then(function(userData){
//You reach here only after storage.update was completed
$state.go('home')
});