Can't change value of $scope after get request - angularjs

I am looking for a possible solution to the below, I have an array that has an assigned watcher. My issue is changing the $scope after receiving a response from a http get request. $scope is always undefined, I need to change the value there in real time. Any suggestions would be greatly appreciated!
$scope.$watch('user.tags', function (newVal, oldVal) {to invalid until the
$scope.tags_valid = true;
// Loop through array and validate length
for (var i = 0; i < $scope.user.tags.length; i++) {
if ($scope.user.tags[i].data.length != 24) {
$scope.tags_valid = false;
return;
}
// Check if already exists
$http.get("api/registration/TagExists", { params: { Tag_Id: $scope.user.tags[i].data } }).success(function (response) {
if (response == "true") {
// $scope is always undefined here
$scope.user.tags[i].is_valid = false;
$scope.tags_valid = false;
} else if (response == "false") {
// $scope is always undefined here
$scope.user.tags[i].is_valid = true;
}
});
}
}, true);

Actually what is undefined is the user tag at [i].
Because of the function scope of the variable, the i will be equal to the length of the array before any response from server arrives.
You could wrap the server call in a function that would accept the index or the actual tag as an argument. Like checkTag(tag) in which you make the call.
Example code:
function checkTag(tag) {
$http.get("api/registration/TagExists", { params: { Tag_Id: tag.data } }).success(function (response) {
if (response == "true") {
tag.is_valid = false;
$scope.tags_valid = false;
} else if (response == "false") {
tag.is_valid = true;
}
});
}

Related

AngularJS chaining promises - need to do work before the next 'then'

I am working on a promise chain. The first call is an $http call to check if a user exists, and then if it does, theres a bunch of .then() statements that run sequentially.
My question is this.. in that first call, i don't want to return the promise of the $http request because if the user doesn't exist, the results are just an empty array and the promise resolves, thus triggering the next action to look up information about the user. I wrote the following code...
(see the part in comments about being the important part i'm asking about)
$scope.checkIfUserExists = function() {
if (angular.isObject($scope.admin.Inductee.Contactor)) {
var handleFault = function( fault ) {
if (typeof(fault) === 'string') {
switch (fault.toUpperCase()){
case 'NODATA':
// Go ahead an save
$scope.pushInductee();
break;
case 'STATUS':
// just get the 'duplicate records check' sign off of there
// The save button is disabled by the critical error
$scope.hideSave = false;
break;
case 'ASSIGNED':
// just get the 'duplicate records check' sign off of there
// The save button is disabled by the critical error
$scope.hideSave = true;
break;
default:
$log.error(fault);
$location.path('/error/default');
}
} else {
$log.error(fault);
$location.path('/error/default');
}
};
$scope.getMatchingIndData()
.then($scope.procBusLogic)
.then($scope.pushInductee)
.catch(handleFault);
}
};
////HERE IS THE IMPORTANT PART I AM ASKING ABOUT
$scope.getMatchingIndData = function() {
var deferred = $q.defer();
var locals = {};
var checkUser = function(dupeJson){
var checkUserDeferred = $q.defer();
// abandoned promise replaced with my own
sttiJoinDataFactory.checkIfUserExistsNurseleader(dupeJson)
.then(function(results) {
var data = results.data;
if (angular.isArray(data) && data.length > 0){
var highestMatch = data[0];
for (var i = 0; i < data.length; i++) {
if (parseInt(data[i].Score) > parseInt(highestMatch.Score)) {
highestMatch = data[i];
}
}
checkUserDeferred.resolve(highestMatch);
} else {
// Reject the 'overall' promise here
// to effectively break the chain
return deferred.reject('NODATA');
}
})
.catch(function(fault) {
// Any other failure should break the chain
// of http requests at this point
return deferred.reject(fault);
});
return checkUserDeferred.promise;
},
loadindividual = function (highestMatch) {
return $http stuff about the highestmatch
// set data in locals
},
parallelLoadStatusAndInducteeData = function(individual) {
return another $http promise based on the last then()
// set data in locals
},
loadCeremonyData = function (inductees){
return another $http promise based on the last call then() // set data in locals
},
reportProblems = function( fault ) {
deferred.reject(fault);
};
checkUser($scope.generateDupJson())
.then(loadindividual, reportProblems)
.then(parallelLoadStatusAndInducteeData, reportProblems)
.then(loadCeremonyData, reportProblems)
.then(function() {
deferred.resolve(locals);
})
.catch( reportProblems );
return deferred.promise;
};
Must I take into account the abandoned promise, since I really need to promise to resolve when the data comes back, and i need to reject it if there is NODATA. This is handled in the calling function's chain.
Also, I'm aware of antipatterns here. I'm trying my best to not nest promises, maintain the chain, as well as handle exceptions.
Ok I have a few comments for you:
...
// revert if and return immediately
// to reduce indentation
if (typeof(fault) !== 'string') {
$log.error(fault);
$location.path('/error/default');
return;
}
switch (fault.toUpperCase()) {
...
You don't need deferred objects:
var checkUser = function(dupeJson){
// this is not abandoned because we are returning it
return sttiJoinDataFactory.checkIfUserExistsNurseleader(dupeJson)
.then(function(results) {
var data = results.data;
if (!angular.isArray(data) || data.length <= 0) {
return $q.reject('NODATA');
}
var highestMatch = data.reduce(function (highest, d) {
return parseInt(d.Score) > parseInt(highest.Score) ?
d : highest;
}, data[0]);
return highestMatch;
}); // you don't need catch here if you're gonna reject it again
}
...
checkUser(...)
// loadIndividual will be called
// after everything inside checkUser resolves
// so you will have your highestMatch
.then(loadIndividual)
.then(parallelLoadStatusAndInducteeData)
.then(loadCeremonyData)
// you don't need to repeat reportProblems, just catch in the end
// if anything rejects prior to this point
// reportProblems will be called
.catch(reportProblems)
...

NodeJS callback: How to make the call wait for mongodb query result

I have a registration dialog where when the user enters username and password I need to check the DB whether the user is present
or not. But when I am validation for the same my call does not hold back until I get the results from the server.
After searching for a while I got to know about callbacks. So I have added a call back inside this.isUser method.
And it is successful. But now doRegistration method is not synchronous with the isUser method.
How to make all my calls synchronous?
this.doRegistration = function(uname, pwd, confirmPwd) {
if(this.isUser(uname)) {
return "USER_EXISTS";
} else {
saveUser(uname, pwd);
return "SUCCESS";
}
};
this.isUser = function(username) {
var users = new Array();
getAllUsers('param', function(response) {
users = response;
console.log(users.length);
for(i = 0; i < users.length; i++) {
if(users[i].username === username) {
return true;
}
}
return false;
});
};
function getAllUsers(param, callback) {
loginFactory.AllUsers.query(function(response) {
if(response != undefined && response.length > 0) {
callback(response);
}
});
}
You may rewrite the code like following:
this.doRegistration = function(uname, pwd, confirmPwd, callBack) {
this.isUser(uname,function(flag) {
if(flag){
callBack("USER_EXISTS");
}
else {
saveUser(uname, pwd, function(err,result){
if(err){
callBack("SAVING_FAILED");
}
else {
callBack("SUCCESS");
}
});
}
});
};
this.isUser = function(username,callBack) {
var users = new Array();
getAllUsers('param', function(response) {
users = response;
console.log(users.length);
for(i = 0; i < users.length; i++) {
if(users[i].username === username) {
callBack(true);
}
}
callBack(false);
});
};
function saveUser(userName, pwd, callBack){
//code to save user
//chek for error in saving
if(err){
callBack(err,null)
}
else {
callBack(null, "success")
}
}
function getAllUsers(param, callback) {
loginFactory.AllUsers.query(function(response) {
if(response != undefined && response.length > 0) {
callback(response);
}
});
}
You may also define saveUser as a function with callback. Here it wont wait for saveUser method to complete.

Prevent multiple submits in angularjs

I'm looking for a AngularJS-based way to prevent multiple submits per task.
I don't need buttons to be disabled after submission or close the form and wait for the task to be completed. Instead, I need requests to be unique.
To be more detailed, I need $http.get and $http.post stop sending multiple same requests.
Any Ideas?
According to this article, you can use provider decorator.
NOTE: this approach is based on angular-api
https://gist.github.com/adambuczynski/354364e2a58786e2be71
UPDATE
I've changed a little part in your suggested solution, because returned promises have lost .success and .error and .then.
Just use this edited code to have all of those functions working:
.config(["$provide", function ($provide) {
$provide.decorator('$http', function ($delegate, $q) {
var pendingRequests = {};
var $http = $delegate;
function hash(str) {
var h = 0;
var strlen = str.length;
if (strlen === 0) {
return h;
}
for (var i = 0, n; i < strlen; ++i) {
n = str.charCodeAt(i);
h = ((h << 5) - h) + n;
h = h & h;
}
return h >>> 0;
}
function getRequestIdentifier(config) {
var str = config.method + config.url;
if (config.data && typeof config.data === 'object') {
str += angular.toJson(config.data);
}
return hash(str);
}
var $duplicateRequestsFilter = function (config) {
if (config.ignoreDuplicateRequest) {
return $http(config);
}
var identifier = getRequestIdentifier(config);
if (pendingRequests[identifier]) {
if (config.rejectDuplicateRequest) {
return $q.reject({
data: '',
headers: {},
status: config.rejectDuplicateStatusCode || 400,
config: config
});
}
return pendingRequests[identifier];
}
pendingRequests[identifier] = $http(config);
$http(config).finally(function () {
delete pendingRequests[identifier];
});
return pendingRequests[identifier];
};
Object.keys($http).filter(function (key) {
return (typeof $http[key] === 'function');
}).forEach(function (key) {
$duplicateRequestsFilter[key] = $http[key];
});
return $duplicateRequestsFilter;
})
}])
It could be a performance issue but following idea could solve your problem.
Store the each request URL and DATA as key value pair on a variable. URL should be KEY. For Same URL multiple submission can be stored in a Array.
Then for any new call check the URL if it present in your stored object, then compare the data with each object thorughly (deep check, it is costly though).
If any exact match found then stop the processing. As same request came.
Other wise proceed and don't forget to store this data also.
But it is costly since need to check the data which could be havy.
Note: At the time of storing the data you could convert it to JSON String so it will be easier to compare between String.
here is the Code Algo
YourService.call(url, params) {
var Str1 = JSON.stringify(params);
if(StoredObj[url]) {
for each (StoredObj[url] as Str){
if(Str === Str1) {
return;
}
}
}
else {
StoredObj[url] = []; //new Array
}
StoredObj[url].push(Str1);
Call $http then;
}

checkbox filter for json array in Angularjs

I have create a filter but this filter is not working with array inside array.
'http://plnkr.co/edit/oygy79j3xyoGJmiPHm4g?p=info'
Above plkr link is working demo.
app.filter('checkboxFilter', function($parse) {
var cache = { //create an cache in the closure
result: [],
checkboxData: {}
};
function prepareGroups(checkboxData) {
var groupedSelections = {};
Object.keys(checkboxData).forEach(function(prop) {
//console.log(prop);
if (!checkboxData[prop]) {
return;
} //no need to create a function
var ar = prop.split('=');
//console.log("ar is - "+ar);
if (ar[1] === 'true') {
ar[1] = true;
} //catch booleans
if (ar[1] === 'false') {
ar[1] = false;
} //catch booleans
/* replacing 0 with true for show all offers */
if(ar[0]=='SplOfferAvailable.text'){
ar[1]='true';
}else{
}
//make sure the selection is there!
groupedSelections[ar[0]] = groupedSelections[ar[0]] || [];
//at the value to the group.
groupedSelections[ar[0]].push(ar[1]);
});
return groupedSelections;
}
function prepareChecks(checkboxData) {
var groupedSelections = prepareGroups(checkboxData);
var checks = [];
//console.log(groupedSelections);
Object.keys(groupedSelections).forEach(function(group) {
//console.log("groupedSelections- "+groupedSelections);
//console.log("group- "+group);
var needToInclude = function(item) {
//console.log("item- "+item);
// use the angular parser to get the data for the comparson out.
var itemValue = $parse(group)(item);
var valueArr = groupedSelections[group];
//console.log("valueArr- "+valueArr);
function checkValue(value) { //helper function
return value == itemValue;
}
//check if one of the values is included.
return valueArr.some(checkValue);
};
checks.push(needToInclude); //store the function for later use
});
return checks;
}
return function(input, checkboxData, purgeCache) {
if (!purgeCache) { //can I return a previous 'run'?
// is the request the same as before, and is there an result already?
if (angular.equals(checkboxData, cache.checkboxData) && cache.result.length) {
return cache.result; //Done!
}
}
cache.checkboxData = angular.copy(checkboxData);
var result = []; // this holds the results
//prepare the checking functions just once.
var checks = prepareChecks(checkboxData);
input.every(function(item) {
if (checks.every(function(check) {
return check(item);
})) {
result.push(item);
}
return result.length < 10000000; //max out at 100 results!
});
cache.result = result; //store in chache
return result;
};
});
above code is for check box filter.
when i click on checkbox called "Availability" it does not filter the result.
Please help me out.
Thanks.
I think that the way you are navigating through json is wrong because if you put in this way it works
"Location": "Riyadh",
"AvlStatus": "AVAILABLE"
"Rooms": {.....
You have to go in some way through Rooms and right now I think you're not doing that

Working with promise - angularjs

How to rewrite this code, to get the desired o/p.
I would like to use the AgentReply object after filling in the data.
Inside the switch case, this object has data. But once outside, it is null again. Understood that it is because of the async,
But what should I do, to be able to use 'AgentReply' once it has data.
$scope.ActionItems = function (actionItem) {
var AgentReply = {};
switch (actionItem) {
case "SendOTP":
var SentStatus = "";
DataFactory.SendOTP('39487539847')
.then(function (response) {
SentStatus = JSON.parse(JSON.parse(response.data));
SendOTPFailed();
}, function (error) {
});
break;
}/*End of switch*/
function SendOTPFailed(){
if (SentStatus == "200") {
AgentReply = {
IsCustomer: false,
UserText: "Request Failed.",
}
}
}
if (Object.keys(AgentReply).length > 0) {
//do something with AgentReply
}
}
Just pass a function in to where the AgentReply is available, and define it underneath, ie:
$scope.ActionItems = function (actionItem) {
var AgentReply = {};
switch (actionItem) {
case "SendOTP":
var SentStatus = "";
DataFactory.SendOTP('39487539847')
.then(function (response) {
SentStatus = JSON.parse(JSON.parse(response.data));
if (SentStatus == "200") {
AgentReply = {
IsCustomer: false,
UserText: "Request Failed.",
}
}
doSomethingWithAgentReply(AgentReply);
}, function (error) {
});
break;
}
console.log(AgentReply); //null here
function doSomethingWithAgentReply(reply) {
if (Object.keys(reply).length > 0) {
//do something with AgentReply
}
}
}
If you need to use this code :
if (Object.keys(AgentReply).length > 0) {
//do something with AgentReply
}
}
Outside the .then() function :
DataFactory.SendOTP('39487539847')
.then(function (response) {
})
You can try this:
$scope.ActionItems = function (actionItem) {
var def = jQuery.Deferred();
var AgentReply = {};
switch (actionItem) {
case "SendOTP":
var SentStatus = "";
DataFactory.SendOTP('39487539847')
.then(function (response) {
SentStatus = JSON.parse(JSON.parse(response.data));
if (SentStatus == "200") {
AgentReply = {
IsCustomer: false,
UserText: "Request Failed.",
}
def.resolve(AgentReply);
}
console.log(AgentReply); //available here
}, function (error) {
def.reject(error);
});
return def.promise();
break;
}
//console.log(AgentReply); //null here
//if (Object.keys(AgentReply).length > 0) {
//do something with AgentReply
// }
//}
// This is unusable in this case.
The usage is:
var someActionItem = 'SomeActionItemInfo';
$scope.ActionItems(someActionItem)
.then(function(agentReply) {
if (Object.keys(agentReply).length > 0) {
//do something with agentReply
}
}, function(error));
EDIT:
$scope.ActionItems is the same function. What happening when you using promise?
First you defining the deffer object. var def = jQuery.Deferred(). This object is at jQuery, but all frameworks/libraryies that support promise working at the same way.
As you see, you returning def.promise(). That is the object which contain .then property. Because of that obj you can use $scope.ActionItems().then() method. That actually make def.promise().
And inside your async code (this code that consuming some time and it's not executed immediately) you defining def.resolve() or def.reject().
When the work is done. You calling def.resolve(withSomeData) and this will activate .then() method to the $scope.ActionItems.
For example:
var foo = null;
function doPromise() {
var def = jQuery.Deferred();
setTimeout(function(){
foo = 2;
def.resolve(foo + 1) // This will call the .then() method with foo + 1
}, 500);
return def.promise();
}
doPromise();
console.log(foo) // foo = null here. cuz the function waiting 500ms.
// Here the .then() method will be executed after ~500+ ms.
doPromise().then(function(fooValue) {
console.log(fooValue) // foo value = 3 here. cuz function is done
});

Resources