$http timing issues, AngularJS - javascript

Have two issues. I am trying to get a value from an $http response and populate a variable with it that should then update a couple DOM objects. Problem is that it seems to be having a timing issue where the function that called the $http service completes and then the variable gets updated but doesn't seem to update everywhere it should. I also tried putting a watch on the variable and it only seems to fire off when the page is initially loaded. I'ved been reading up all this all day and cant seem to find an answer that works.
app.controller('MainCtrl', ['$scope', '$http', 'waciServ', function($scope, $http, waciServ) {
"use strict";
$scope.currentSource = waciServ.activeSource;
$scope.$watch('waciServ.activeSource', function(newValue, oldValue) {
$scope.currentSource = newValue;
console.log('Watcher! ' + newValue);
}/*, true*/);
$scope.getActiveSource = function () {
$scope.currentSource = waciServ.getStringByName("active_device");
};
}]);
app.service('waciServ', function($http) {
var self = this;
this.waciIP = location.host;
this.activeSource = '';
this.getStringByName = function (name) {
$http.post("http://" + self.waciIP + "/rpc/", "method=GetVariableByName&param1=" + name + "&encoding=2")
.then (function (response) {
var was_error = self.read(response.data);
if (was_error == '1') { //active_device is not set
self.assignVariable(name, "none");
self.activeSource = "none";
return self.activeSource;
} else {
var varId = parseInt(self.read(response.data));
$http.post("http://" + self.waciIP + "/rpc/", "method=GetVariableValue&param1=" + varId + "&encoding=2")
.then (function (response) {
self.activeSource = self.read(response.data);
return self.activeSource;
});
}
}, function (error) {
console.log("error: " + error.data);
});
};
});
i can place a console.log right before the return fires and see that I have what I want, but another console.log placed in the function within the controller shows 'undefined'.
What gives? Thank you in advance.

You don't need to think about to use watcher.
Basically the problem is you are not returning promise from the service method. You should return promise of $http method calls from your service method .Thereafter use .then over method call to chain promise & put success & error function in it. (this answer is little similar with what you are asking but not exactly)
Service
self.getStringByName = function(name) {
//returned promise from here
return $http.post("http://" + self.waciIP + "/rpc/", "method=GetVariableByName&param1=" + name + "&encoding=2")
.then(function(response) {
var was_error = self.read(response.data);
if (was_error == '1') { //active_device is not set
self.assignVariable(name, "none");
self.activeSource = "none";
return self.activeSource; //returned data here to chain promise
} else {
var varId = parseInt(self.read(response.data));
//returned promise from here
return $http.post("http://" + self.waciIP + "/rpc/", "method=GetVariableValue&param1=" + varId + "&encoding=2")
.then(function(response) {
self.activeSource = self.read(response.data);
//returned data from here
return self.activeSource;
});
}
}, function(error) {
console.log("error: " + error.data);
});
};
Controller
app.controller('MainCtrl', ['$scope', '$http', 'waciServ', function($scope, $http, waciServ) {
"use strict";
$scope.currentSource = waciServ.activeSource;
$scope.getActiveSource = function () {
waciServ.getStringByName("active_device").then(function(source){
$scope.currentSource = source;
});
};
}]);

Related

AngularJS and angular factory

I have asked similar question before, this time I am stuck with recording data to the blockchain using Angular js and Angular Factory. Please see the code below and advise me where I am wrong
app.js
var app = angular.module('application', [])
app.controller('appController',function($scope, appFactory) {
$('success_create').hide()
$scope.recordData = function(){
appFactory.recordData($scope.data, function(data){
$scope.recordData = data
$("success_create").show()
})}}
app.factory('appFactory',function($http) {
var test = []
factory.recordData = function(data, errorCallback) {
test = data.field1+"-"+data.field2
$http.get('/record_data'+data).then(function(output) {
if (output) {
callback(output)
}).catch(function(error) {
errorCallback(error) })}
return factory
There are so many errors in you're code, that I was considering not to awnser.
But as I felt the need to help you, take the code below as a guide.
var app = angular.module('application', [])
app.controller('appController', function($scope, appFactory) {
// Use angular.element instead of the jQuery `$` selector
angular.element('success_create').hide();
$scope.recordData = function()
{
// The factory returns a promise,
// so you can do the same just as you would with $http
appFactory.recordData($scope.data).then(function(response) {
$scope.recordData = response.data;
angular.element("success_create").show()
});
}
});
app.factory('appFactory',function($http) {
// You define the var as array, but you assign a string later on
// So instead of var test = [] use var test = "" or just var test;
var test = ""; // Or var test;
var factory = {
recordData: function (data, errorCallback)
{
test = data.field1 + "-" + data.field2;
var promise = $http.get('/record_data' + data).then(function(output) {
return output.data;
});
// $http returns a promise, return this to your controller
// You can use the data it returns just like you
// would with the $http method
return promise;
}
}
// In your original code, you return the factory. But you never
// Defined the factory.
return factory;
});
Try out these simply tutorials to learn more about controllers, services ** and promises
https://www.w3schools.com/angular/angular_controllers.asp
https://www.w3schools.com/angular/angular_services.asp
https://docs.angularjs.org/api/ng/service/$q
** Confused about Service vs Factory
#Tabz: modified your code.
app.controller(appController,function($scope, appFactory) {
$("success_create").hide();
$scope.recordData = function(){
appFactory.recordData($scope.data, function(data){
$scope.recordData = data
$("success_create").show();
})
}
})
app.factory("appFactory", function($http) {
factory.recordData = function(data, errorCallback)
$http.get('/record_data'+data).then(function(output) {
if (output)
callback(output)
}).catch(function(error) {
errorCallback(error)
})};
return factory

How to abort/cancel promise for first call during 2 consecutive angular service calls?

When 2 consecutive service calls are made through $http angular service on dropdown item selection and assume first call took time to return and second call return before first call resolve then it shows data of first call for second item selection.
So is there any way to abort first promise if service call made again before first call resolves.
For demo purpose I created sample plunkar which has dropdown with few items I added explicit condition where on selection of first item it took little longer time then other items.
So select first item and immediately select second item to reproduce the scenario, check favorite books display on screen.
Any help appreciated!
Service code:
app.factory('bookService', function($http, $q, $timeout) {
return {
getBook: function(id) {
var promise = $q.defer();
var timeoutSpan = id == 1 ? 3000 : 500;
$http.get("favouriteBooks.json").success(function(data) {
$timeout(function() {
promise.resolve(data.filter(function(obj) {
return obj.id == id
}));
}, timeoutSpan);
}).error(function(msg) {
promise.reject(msg);
})
return promise.promise;
}
}
});
I found 2 ways to handle this scenario -
Case 1: Create global $q deffer object at service level and check whether this object has value/or not before making call to $http request. If this deffer object has value then resolved it explicitly. Plunkar code - code snippet
Service Code:
app.factory('bookService', function($http, $q, $timeout, bookConstants) {
var service = {};
service.mypromise = null;
service.getBook = function(id) {
if (service.mypromise) {
service.mypromise.resolve(bookConstants.EXPLICIT_CANCEL);
service.mypromise = null;
}
service.mypromise = $q.defer();
var timeoutSpan = id == 1 ? 3000 : 500;
$http.get("favouriteBooks.json").success(function(data) {
$timeout(function() {
if (service.mypromise) {
service.mypromise.resolve(data.filter(function(obj) {
service.mypromise = null;
return obj.id == id
}))
}
}, timeoutSpan);
}).error(function(msg) {
service.mypromise.reject(msg);
})
return service.mypromise.promise;
}
return service;
});
Case 2: Return $q deffer object as service response and maintain it at controller level. And in case of consecutive service call first check and explicitly resolve first service call then and proceed with other service call.
Plunkar code - code snippet
Sample Code:
$scope.getSelectedValue = function() {
var id = $scope.selitem.id
$scope.cancel();
var bookPromise = bookService.getBook(id);
$scope.requests.push(bookPromise);
bookPromise.promise.then(getBookSuccess)
.catch(errorCallback)
.finally(getBookComplete);
}
function getBookSuccess(favouriteBooks) {
if (favouriteBooks == 'User Cancelled') {
return;
}
var books = '';
angular.forEach(favouriteBooks, function(book) {
books += book.bookName + ' '
});
$scope.selectedvalues = 'Name: ' + $scope.selitem.name +
' Id: ' + $scope.selitem.id + ' Favourite Book(s): ' + books;
}
function errorCallback(errorMsg) {
console.log('Error Message: ' + errorMsg);
}
function getBookComplete() {
console.log('getBook Has Completed!');
}
$scope.cancel = function() {
if ($scope.requests) {
angular.forEach($scope.requests, function(request) {
request.resolve('User Cancelled');
clearRequest(request);
})
}
};
var clearRequest = function(request) {
$scope.requests.splice($scope.requests.indexOf(request), 1);
};
}]);
app.factory('bookService', function($http, $q, $timeout) {
var service = {};
service.getBook = function(id) {
var promise = $q.defer();
var timeoutSpan = id == 1 ? 3000 : 500;
$http.get("favouriteBooks.json").success(function(data) {
$timeout(function() {
promise.resolve(data.filter(function(obj) {
return obj.id == id
}))
}, timeoutSpan);
}).error(function(msg) {
promise.reject(msg);
})
return promise;
}
return service;
});
You can use the following to wait for two or more promises $q.all([promise1,promise2]).then(function(data){....});
To access data of first promise just use data[0] the second promise return value is saved in data[1] and so on...

Function in angular service returning data late?

I have a service that contains a function to set and return default user preferences. I am experiencing a strange problem that I believe has to do with the async nature of returning the data. I will show you the calls and then explain the problem.
The service is defined as follows:
var StateService = angular.module('StateService', [])
.service('HoldState', function ($http, $q) {
In the app.js I have the following injection:
var JBenchApp = angular.module('JBenchApp', [
'ngRoute',
'StateService'
]);
In the controller I also have the following injection:
JBenchApp.controller('CaseListCtrl', ['$scope', '$http', 'HoldState',
function ($scope, $http, HoldState) {
The following is the function I am calling:
this.getUserDefaults = function (UserID) {
var userDefaults = [], deferred = $q.defer();
$http.get('http://10.34.34.46/BenchViewServices/api/UserPreference/Default/' + UserID)
.then(function (response) {
userDefaults = response;
//var status = getStatus();
var status = localStorage.getItem('Status');
// If the status is 0 then we have not yet navigated anywhere so we will need to set the path values to be
// the same as the default. We do nothing if status is not 0 because it means we already have path values set
if (status == 0 || status == null) {
/**setTypeOfLaw(response.LitigationCode);
setCourthouse(response.LocID);
setDepartment(response.CourtRoom);**/
localStorage.setItem('LawType', response.LitigationCode);
localStorage.setItem('Building', response.LocID);
localStorage.setItem('Dept', response.CourtRoom);
localStorage.setItem('Status', 1);
}
alert("value: " + userDefaults.PreferenceID);
deferred.resolve(response);
}, function (response) {
console.log(response.status + " -- " + response.data + " -- " + response.statusText);
});
return deferred.promise;
};
The following is the code where I am seeing the strange issue:
$scope.fillDefaults = function () {
HoldState.getUserDefaults('dpeng').then(function (data) {
$scope.userDefaults = data;
//$scope.$apply();
});
}
$scope.fillDefaults();
The issue that I am seeing is that the function is called and entered. Processing then returns to the line $scope.userDefaults, but it winds up undefined. It seems that the code goes to through the $scope.fillDefaults function before the data is returned. I suspect this because the alert errors saying that it cannot find property PreferenceID of undefined. I have checked the API call through PostMan and it works just fine. What am I doing wrong here?
fillDefaults is a red herring; the problem is in getUserDefaults: you're assigning http.get's full response object to userDefaults, instead of the response data.
$http.get('http://10.34.34.46/BenchViewServices/api/UserPreference/Default/' + UserID)
.then(function (response) {
userDefaults = response;
should be
$http.get('http://10.34.34.46/BenchViewServices/api/UserPreference/Default/' + UserID)
.then(function (response) {
userDefaults = response.data;

AngularJS : Why when using back button on the browser my promises return before resolve with previous value?

Hi here is my code when I run the factory this one gets resolved before with the old data from the page before. if I put break points I get a hit on the callback before the promise is resolved
$scope.formElementsData = response; get there before deferred.resolve(formElements);.
// when I call it after using the back button
GetInputControlItemsService.getInputControlItems($routeParams.project +','+ $routeParams.templateid ).then(function(response) {
$scope.formElementsData = response;
});
app.factory('GetInputControlItemsService', [
'$http', '$q',
function($http, $q) {
var apiCall, deferred, factory, _getInputControlItems;
factory = {};
deferred = $q.defer();
apiCall = 'api/GetProjectInputControlItems/?projectAndTempletaeId=';
_getInputControlItems = function(projectAndTempletaeId) {
$http.get(webBaseUrl + apiCall + projectAndTempletaeId).success(function(formElements) {
deferred.resolve(formElements);
}).error(function(err) {
deferred.reject(err);
});
return deferred.promise;
};
factory.getInputControlItems = _getInputControlItems;
return factory;
}
])
Y also tried
GetInputControlItemsService.getInputControlItems($routeParams.project +','+ $routeParams.templateid ).then(function(response) {
return response;
}).then(function(response){
$scope.formElementsData = response;
});
and still not working any idea how to use this along with the back button of the browser ?
Also I notice if I call a factory inside a function for a search feature, it does the same thing it runs inside the callback before the promise is deferred.
So I end up calling it without promises and now it works here is the code without the factory service.
$scope.goSearch = function() {
if ($scope.searchKey !== '') {
$http.get(webBaseUrl + apiCall + $scope.searchKey).success(function(results) {
$scope.projects = results;
});
}
};

Angularjs Factory deferred's data disapearing

I'm trying to do a caching factory for http requests, so it doesn't make the server do a lot of work for the same request. But It seems my way of using deferred "swallows" the data, and I don't know why.
Console output for below:
data fetched:
Object {state: "OK", data: Object, errorMessage: null, exception: null}
success
undefined
ImportFactory:
factory("importFactory", function ($http, $q, loggingService) {
return{
fetchedData: [],
cacheTransport: function (transportsId, data) {
this.fetchedData.push({"transportsId": transportsId, "data": data});
},
getImport: function (transportsId) {
var factory = this;
var deferred = $q.defer();
var preFetchedTransport = this.findTransport(transportsId);
if (preFetchedTransport === null) {
console.log('fetching from backend');
return $http.post("/import/create/" + transportsId).then(function (data) {
console.log('data fetched:');
console.log(data);
factory.cacheTransport(transportsId, data);
deferred.resolve(data);
});
}
preFetchedTransport = deferred.promise;
return preFetchedTransport;
},
findTransport: function (transportsId) {
for (var i = 0; i < this.fetchedData.length; i++) {
var transportObj = this.fetchedData[i];
if (transportObj.transportsId === transportsId) {
return transportObj.data;
}
}
return null;
}
};
});
Controller
.controller('ImportController', function ($scope, $routeParams, importFactory){
$scope.transportId = $routeParams.id;
importFactory.getImport($scope.transportId).then(function (successData) {
console.log('success');
console.log(successData);
}, function (errorData) {
console.log('error');
console.log(errorData);
});
You basically need this: Demo here.
var cachedPromises = {};
return {
getStuff: function(id) {
if (!cachedPromises[id]) {
cachedPromises[id] = $http.post("/import/create/" + id).then(function(resp) {
return resp.data;
});
}
return cachedPromises[id];
}
};
Now, when you fetch that data, you can manipulate and it will be changed when you access it in the future.
myService.getStuff(whatever).then(function(data) {
data.foo = 'abc';
});
//elsewhere
myService.getStuff(whatever).then(function(data) {
console.log(data.foo); // 'abc'
});
Here's a demo that does this, as well as a view updating trick (bind the object to the view before the data comes in), and an idea of how you could change the data separately from the cache, in case you want to have the original data and the changing data. http://jsbin.com/notawo/2/edit
Remember to avoid that nasty promise anti-pattern. If you already have a promise, use that instead of creating another with $q. $http already returns a promise and that promise is sufficient for whatever you need if you use it properly.
just change the loop condition look like this and then test i think your function and defer is work fine but the loop does not sent the correct data
for(var i = 0; i < this.fetchedData.length; i++) {
if (this.fetchedData[i].transportsId === transportsId) {
return this.fetchedData[i].data;
}
}
return null;
}
The reason you are getting undefined is you are not returning anything from the $http.post().then() !
Also in your getImport() function you are returning an empty promise when the transport is already cached. You need to resolve it to your already cached transport object.
getImport: function (transportsId) {
var factory = this;
var deferred = $q.defer();
var preFetchedTransport = this.findTransport(transportsId);
if (preFetchedTransport === null) {
console.log('fetching from backend');
return $http.post("/import/create/" + transportsId).then(function (data) {
console.log('data fetched:');
console.log(data);
factory.cacheTransport(transportsId, data);
return data; //this was missing
});
}
// resolve it with transport object if cached
deferred.resolve(preFetchedTransport);
return deferred.promise;
},

Categories

Resources