I am having some problem in vuejs in executing a function/method sequentially.
I have three functions like:
MethodA: function(){
if(x = 1){
value1 = 2;
}
if (x ==2){
value2 = 4;
}
this.MethodB();
}
MethodB: function(){
Total value = value1 + value2;
}
MethodC: function (){
this.$http.get('api/getvalue').then(function(response){
this.set('somedata', response.data);
response.data.forEach(para){
if(para.id == 1){
this.MethodA();
}
if(para.id == 2){
this.MethodA();
}
}
});
}
ready: function(){
this.MethodC();
}
I would like to execute this.MethodB() only after MethodC and MethodA has completely executed. How can I do this?
You can use Javascript Promises with Vue.js methods:
methodA: function() {
return new Promise(function(resolve, reject) {
//run all your methodA code here
...
resolve('MethodA finished');
});
},
methodB: function() {
return new Promise(function(resolve, reject) {
//run all your methodB code here
...
resolve('MethodB finished');
});
},
methodC: function() {
//run your methodC code
}
Now, to run methodC only when methodA and methodB are finished, you can use the promises .then and chain them together. For ex:
ready: function() {
//save `this` to a variable just to make it easier to be accessed within the chain
let self = this;
//run methodA, then methodB...only then, methodC
self.methodA.then(function(resultA) {
console.log(resultA);
return self.methodB();
}).then(function(resultB) {
console.log(resultB);
self.methodC();
});
}
Note: if you running AJAX calls within methodA or methodB, make sure to resolve the promise only when you receive a response. In your ex.:
this.$http.get('api/getvalue').then(function(response){
...
resolve('My method is now complete');
}
Related
I tried to maka a QUnit async test for checking ajax update.
I read of QUnit.asyncTest here
https://www.sitepoint.com/test-asynchronous-code-qunit/
but if i try this i get a
TypeError: QUnit.asyncTest is not a function
thats the complete source: https://gist.github.com/232457b002e5363439aece7535600356
of course i new by using QUnit and used JavaScript not for long time.
that a snippet of the part where the error happens:
function max() {
var max = -Infinity;
for (var i = 0; i < arguments.length; i++) {
if (arguments[i] > max) {
max = arguments[i];
}
}
return max;
}
// https://www.sitepoint.com/test-asynchronous-code-qunit/
//TypeError: QUnit.asyncTest is not a function
QUnit.asyncTest('max', function (assert) {
expect(1);
window.setTimeout(function() {
assert.strictEqual(max(3, 1, 2), 3, 'All positive numbers');
QUnit.start();
}, 0);
});
this test gives no syntax error but gives old date:
QUnit.test('usersInnerHTMLlength_Is24', function(assert) {
// problem: this not reads the updates done by ajax. means that are old data:
let innerHTMLlength = $("#users").html().toString().length;
assert.equal(innerHTMLlength, 24);
});
May its not possible to check ajax with QUnit?
I thougt this when i have read here:
QUnit testing AJAX calls
I use it inside a Wordpress Plugin
That sitepoint article is very old (by web standards). You'll need to use the newer syntax found on the documentation website:
function someAsyncThing(value) {
return new Promise(function (resolve, reject) {
setTimeout(function() {
if (value > 5) {
resolve(value);
} else {
reject(new Error("bad value"));
}
}, 500);
});
}
QUnit.test( "some async thing success test", function( assert ) {
// This is what tells QUnit the test is asynchronous
// "done" here will be a callback function used later
var done = assert.async();
// Now call your Async code, passing in a callback...
someAsyncThing(10)
.then(function(result) {
// do your assertions once the async function ends...
assert.equal(result, 10)
// Now tell QUnit you're all done with the test
done();
})
// we can pass the "done" callback from above into catch() to force a test failure
.catch(done);
});
QUnit.test( "some async thing FAILURE test", function( assert ) {
var done = assert.async();
someAsyncThing(4)
.then(function() {
done(new Error("we should NOT succeed with a value < 5"));
})
.catch(function(err) {
assert.equal(err.message, "bad value")
});
});
I'm trying to save the return value of $http service inside my controller, but I get "undefined" like response
In my controller, I call a service that uses the $http:
//this returns undefined
vm.user_instruments = instruments.getInstruments();
My service:
function instruments($http){
this.getInstruments = function(){
$http.get('url/').
then(function(response) {
/*this console.log print the response,
but this value I can't get it in my controller*/
console.log(response.data);
return response.data;
}, function(error) {
return error.data;
});
}
}//end service
So, what am I doing wrong? My purpose is that the controller be ignorant of any details of HTTP
Several problems . First your service function isn't returning anything .... return $http from it.
this.getInstruments = function(){
// return the request promise
return $http.get('url/').
then(function(response) {
return response.data;
}, function(error) {
return error.data;
});
}
Then in controller assign the scope inside a promise callback:
instruments.getInstruments().then(function(data){
vm.user_instruments = data
});
you have two options to do this:
1. return the promise to the controller and use the promise in the controller
function service ($http) {
this.request = function () {
return $http.request({ /*...*/ });
};
}
function controller (service) {
service.request().then(function (resp) {
console.log(resp);
});
}
2. send callback to service and return the data to the callback
function service ($http) {
this.request = function (callback) {
return $http.request({ /*...*/ }).then(function (resp) {
callback(null, resp);
}, function (err) {
callback(err);
});
};
}
function controller (service) {
service.request(function (err, resp) {
if (err) return console.log(err);
console.log(resp);
});
}
the popular option is to use promises, so use option 1 :)
Try this way
Service:
function instruments($http){
this.get = function(callback){
$http.get('/url').success(function(res){
callback(res);
});
}
} /* end service */
Controller:
instruments.get(function(res){
vm.instruments = res;
});
It should work.
PS: typed in mobile.
Is it possible to execute the same HTTP request more than once in AngularJS? i.e. without re-defining the same request twice?
var retry = false;
var req = $http.get( 'ajax.php?a=StartSession&ref=' + code );
req.success(function(res) {
alert(res);
});
req.error( function(res) {
if(retry == false)
//run request again req.get(); ?
retry = true;
});
The previous answer is good in terms of reusing it as service. But it looks like you really want to abstract out the retry logic as well. Here is how i would do that.
app.service('SessionService', ['$http', '$q', function($http, $q){
var _this = this;
var _maxRetryCount = 5; //Just have a maxRetryCount
this.StartSession = function (code, retries){
//if you dont pass retry take the maxretryCount
retries = angular.isUndefined(retries) ? _maxRetryCount : retries;
return $http.get('ajax.php?a=StartSession&ref=' + code)
.then(function(result) {
//process and return the result
return result.data;
}, function (errorResponse) {
//If retries left decrement count and make the call again.
if(retries) {
return _this.StartSession(code, --retries); //here we are returning the promise
}
//All tried done Now Fail or return some data
return $q.reject('oops failed after retries');
});
}
}]);
And just inject SessionService anywhere say in yourcontroller:-
SessionService.StartSession(code).then(function(result){
//handle Result
}).catch(function(){
//handle fail condition
});
Plnkr
It's what services and factories were made for:
app.factory("dataFactory", ["$http", function($http) {
return {
call: function(code) {
return $http.get( 'ajax.php?a=StartSession&ref=' + code )
}
}
}]);
Inject and use
app.controller("myCtrl", ["dataFactory", function(dataFactory) {
var code = "myCode";
dataFactory.call(code).success(function(res) {
//gotcha
});
}]);
Is it possible to use $q to fire ajax requests synchronously in AngularJS?
I have a long list of vehicles, each vehicle has events associated with them and I need to retrieve the eventdetails of each event when the user expands the listing.
Right now, if the user expands the listing, I am firing up to 15 calls asynchronously and it seems to be causing issues with the API I'm consuming, so I'd like to see if performance is improved if I wait for each request finishes before firing the next.
I'm attempting to implement $q to delay the next request until the previous is finished, however I can't seem to wrap my head around using the service, here is what I currently have:
// On click on the event detail expander
$scope.grabEventDetails = function(dataReady, index) {
if (dataReady == false) {
retrieveEventDetails($scope.vehicles[index].events);
}
}
var retrieveEventDetails = function(events) {
// events is array
var deferred = $q.defer();
var promise = deferred.promise;
var retrieveData = function(data) {
return $http({
url: '/api/eventdetails',
method: 'POST',
data: {
event_number: data.number
},
isArray: true
});
}
_.each(events, function(single_event) {
promise.then(retrieveData(single_event).success(function(data) {
console.log(data);
}));
});
}
This is still firing asynchronously, Where am I going wrong with this?
I understand firing the requests synchronously isn't the best idea, at the moment I just want to see if performance is improved with the API at all.
You don't need $q to implement a promise as $http returns one.
_.each fires all the callbacks without especially waiting the promise.
All you do is call retrieveData for all events whenever your promise is resolved, and since you don't do a first call, it shouldn't even be working
You could do some recursive call like this :
var retrieveEventDetails = function(events) {
var evt = events.shift();
$http({
url: '/api/eventdetails',
method: 'POST',
data: {
event_number: evt.number
},
isArray: true
}).then(function(response){
console.log(response.data);
retrieveEventDetails(events);
});
}
I do think you should use $q as some other part of your application might need to get a promise.
A good example would be $routeProvider resolve option.
I made a little demo in plunker.
Solution:
retrieveData function should return a function (which returns a promise) instead of a just a promise.
That way we can create a promise chain: promise.then(fn).then(fn).then(fn).then(null,errorFn)
We must resolve the first promise to kick the chain.
var retrieveEventDetails = function(events) {
// events is array
var deferred = $q.defer();
var promise = deferred.promise;
var retrieveData = function(data) {
return function(){
return $http({
url: '/api/eventdetails',
method: 'POST',
data: {
event_number: data.number
},
isArray: true
})
}
}
deferred.resolve();
return events.reduce(function(promise, single_event){
return promise.then(retrieveData(single_event));
}, promise);
}
I'm not sure you even need $q here. In this example, each piece of data is registered in the controller as soon as it comes back from the call.
Live demo (click).
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, myService) {
$scope.datas = myService.get();
});
app.factory('myService', function($http) {
var myService = {
get: function() {
var datas = {};
var i=0;
var length = 4;
makeCall(i, length, datas);
return datas;
}
}
function makeCall(i, length, datas) {
if (i < length) {
$http.get('test.text').then(function(resp) {
datas[i] = resp.data+i;
++i;
makeCall(i, length, datas);
});
}
}
return myService;
});
Here's a way using $q.all() that you can wait for all of the data to come through before passing it to the controller: Live demo (click).
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, myService) {
myService.get().then(function(datas) {
$scope.datas = datas;
})
});
app.factory('myService', function($q, $http) {
var myService = {
get: function() {
var deferred = $q.defer();
var defs = [];
var promises = [];
var i=0;
var length = 4;
for(var j=0; j<length; ++j) {
defs[j] = $q.defer();
promises[j] = defs[j].promise;
}
makeCall(i, length, defs);
$q.all(promises).then(function(datas) {
deferred.resolve(datas);
});
return deferred.promise;
}
}
function makeCall(i, length, defs) {
if (i < length) {
$http.get('test.text').then(function(resp) {
defs[i].resolve(resp.data+i);
++i;
makeCall(i, length, defs);
})
}
}
return myService;
});
I have a controller and factory defined as below.
myApp.controller('ListController',
function($scope, ListFactory) {
$scope.posts = ListFactory.get();
console.log($scope.posts);
});
myApp.factory('ListFactory', function($http) {
return {
get: function() {
$http.get('http://example.com/list').then(function(response) {
if (response.data.error) {
return null;
}
else {
console.log(response.data);
return response.data;
}
});
}
};
});
What confuses me is that I get the output undefined from my controller, and then the next line of console output is my list of objects from my factory. I have also tried changing my controller to
myApp.controller('ListController',
function($scope, ListFactory) {
ListFactory.get().then(function(data) {
$scope.posts = data;
});
console.log($scope.posts);
});
But I receive the error
TypeError: Cannot call method 'then' of undefined
Note: I found this information on using a factory through http://www.benlesh.com/2013/02/angularjs-creating-service-with-http.html
You need to either use a callback function or just put a return before $http.get...
return $http.get('http://example.com/list').then(function (response) {
if (response.data.error) {
return null;
} else {
console.log(response.data);
return response.data;
}
});
$http.get is asynchronous so at the time you try to access it (inside your controller) it may not have data (hence you get undefined).
To solve this I use .then() after I call the factory method from my controller. Your factory then would look something like:
myApp.factory('ListFactory', function($http) {
return {
get: function() {
$http.get('http://example.com/list');
}
};
});
And your controller:
myApp.controller('ListController', function($scope, ListFactory) {
ListFactory.get().then(function(response){
$scope.posts = response.data;
});
// You can chain other events if required
});
Hope it helps