AngularJS filter with a promise - javascript

I'm upgrading a filter from a static object to an object retrieved from our database, but I can't seem to get a proper return. This filter takes in an integer which represents a named location, it looks up the location with the key, and returns the name. After troubleshooting, I'm getting close as I can see the object from the database and I can see some of the lookups correctly inside of the then section, but it's not being returned at the end of the filter. Is there a better method on getting this with a filter?
stPartFieldFilters.js
angular.module('app').filter('partLocation', function(stPartMgmtSvc, $q) {
var locs;
function getLocations() {
if(!locs) {
var dfd = $q.defer();
stPartMgmtSvc.getLocations().then(function(res) { locs = res; dfd.resolve(locs); }, function(response) { dfd.reject(response.data.reason) });
return dfd.promise;
}
else {
var dfd = $q.defer();
dfd.resolve(locs);
return dfd.promise;
}
}
function getResults(loc, type) {
var lr = null; // this should be updated once a match is found below
getLocations().then(function(ls) {
if (loc || loc === 0) {
loc = loc.split(',');
if(typeof loc === 'object') {
var res = new Array;
loc.forEach(function(l) {
res.push(ls[l].name)
});
if(!type) { lr = res.toString().replace(',', '<br>'); } // this line provides the correct output; see below (LOCFLTRRES1)
else { lr = res.toString().replace(',', ', '); }
}
else { lr = ls[loc].name; }
}
else { lr = false; };
});
return lr; // return the updated result
}
return function(loc, type) {
return getResults(loc, type); //return the final result for the filter
}
});
inventory.jade
~~~
td.desktop-only {{part.partLoc | partLocation:1}}
~~~
Here is an image of the results of the database object.
Here is an image of the result inside of the filter. This should be set to lr and returned to the main filter function.
Here is the expected result.
Finally, here is the actual result.

Related

How to solve incorrect return value of javascript+firebase function due to order of operation is different when retrieving data?

I am currently implementing a javascript function to judge if user id and name matches or not.
function name_match(user_id, user_realname) {
var dbref = firebase.database().ref();
var namesref = dbref.child("names");
namesref.on("value", function(snapshot) {
snapshot.forEach(i => {
if(i.key == user_id && i.child("realname").val() == user_realname) {
return true;
}
});
});
return false;
}
However, regardless of input, it will initially return false. I think this is because it will go to "return false" while firebase data is loading.
So, even if eventually returns true, since the first-time return value is false, it causes a problem in like this (in another function).
function name_match2() {
var user_id = document.getElementById("user-id").value;
var user_realname = document.getElementById("user-realname").value;
if(!name_match(user_id, user_realname)) return -1;
return 0;
}
And it will return -1.
Can you explain how to solve this problem?
As explained by Daniel in the comment, outer function never returns true. The async solutions could be these :)
function name_match(user_id, user_realname) {
return new Promise(function (resolve, reject) {
var dbref = firebase.database().ref();
var namesref = dbref.child("names");
namesref.on("value", function(snapshot) {
var matched = false;
snapshot.forEach(i => {
if(i.key == user_id && i.child("realname").val() == user_realname) {
matched = true;
}
});
if (matched) {
resolve()
} else {
reject()
}
});
});
}
On the other side of calling function
name_match('userId', 'userName').then(function(){
//matched
}, function(){
//unmatched
});
Other way would be to use Callbacks:
function name_match(user_id, user_realname, cb) {
var dbref = firebase.database().ref();
var namesref = dbref.child("names");
namesref.on("value", function(snapshot) {
var matched = false;
snapshot.forEach(i => {
if(i.key == user_id && i.child("realname").val() == user_realname) {
matched = true;
}
});
cb(matched);
});
}
In this case:
name_match('userId', 'userName', function(matched) {
console.log(matched);
})
Here is a small adaptation of Suryapratap solution, using the once() method, which
"Listens for exactly one event of the specified event type, and then stops listening.", and
Returns a Promise
... instead of using the on() method which sets a listener.
function name_match(user_id, user_realname) {
var dbref = firebase.database().ref();
var namesref = dbref.child("names");
return namesref.once('value').then(function(snapshot) {
var matched = false;
snapshot.forEach(i => {
if(i.key == user_id && i.child("realname").val() == user_realname) {
matched = true;
}
});
return matched;
});
}

Unable to return result from promise

I am trying to read some data from 2 different tables and parse a CSV file before rendering an ejs file.
I can get the data from both tables and from the CSV file but I seem to be unable to return the result.
Pretty sure this is a problem with the way I handle async execution but I fail to see what I am doing wrong.
I've spent the last 2 days reading about this (including the threads around here) and browsing but somehow the answer still escapes me.
First file - usercms.js
app.get('/userscms', function(req, res)
{
existingUsers.getExistingUsers()
.then(function(appUsers)
{
//global users array
//I can display these in my ejs file
globalAppUsers = appUsers;
})
.then(existingUsersAttributesQlik.getExistingUsersAttributesQlik())
.then(function(usersQlikAttributes)
{
//global user attributes array
//undefined data
globalUsersQlikAttributes = usersQlikAttributes;
})
.then(existingSuppliers.parseSuppliersCSV())
.then(function(supplierData)
{
//the result I am expecting
//this prints undefined
console.log(supplierData);
}).then(function()
{
res.render('userscms.ejs',
{
users: globalAppUsers,
attributes: globalUsersQlikAttributes
});
});
});
Second function - getxistingUsers.js (identical to the getExistingUsersAttributesQlik, except for the query)
var userData = [];
var appUsers = [];
(function (exports)
{
exports.getExistingUsers = function ()
{
return promisemysql.createConnection(dbconfig.development).then(function(conn)
{
var result = conn.query("SELECT id, username, firstName, lastName, email, phone, lastLogin, isAdmin, isValid, isPhoneValid, accountCreationDateTime FROM Users");
conn.end();
return result;
}).then(function(rows)
{
return rows;
}).then(function(rows)
{
if (rows.length)
{
userData = [];
appUsers = [];
rows.forEach(function (elem)
{
userData.push(_.toArray(elem));
});
for (i = 0; i < userData.length; i++)
{
var appUser = new appUserModel.AppUser(
userData[i][0],
userData[i][1],
userData[i][2],
userData[i][3],
userData[i][4],
userData[i][5],
userData[i][6],
userData[i][7],
userData[i][8],
userData[i][9],
userData[i][10]);
appUsers.push(_.toArray(appUser));
}
return appUsers;
}
else
{
console.log("NOPE");
return null;
}
}).then(function(appUsers)
{
console.log(appUsers);
return appUsers;
});
};
})(typeof exports === 'undefined' ? this['getExistingUsers'] = {} : exports);
Third file - parseSuppliersCSV.js
var supplierData = [];
var suppliersData = [];
var csvCount = 0;
(function (exports)
{
exports.parseSuppliersCSV = function ()
{
return new Promise(function(resolve, reject)
{
var fileStream = fs.createReadStream("myCSV.csv");
var parser = fastCsv();
csvCount = 0;
supplierData = [];
suppliersData = [];
fileStream
.on("readable", function ()
{
var data;
while ((data = fileStream.read()) !== null)
{
parser.write(data);
}
})
.on("end", function ()
{
parser.end();
});
parser
.on("readable", function ()
{
var data;
while ((data = parser.read()) !== null)
{
if(csvCount >= 1)
{
csvCount++;
var arrayOfStrings = data[0].split(';');
var supplier = new supplierModel.Supplier(arrayOfStrings[0],arrayOfStrings[1]);
suppliersData.push(_.toArray(supplier));
}
else
{
csvCount++;
}
}
})
.on("end", function ()
{
console.log("done");
//all OK here
console.log(suppliersData);
//this doesn't seem to return anything
return suppliersData;
});
});
};
})(typeof exports === 'undefined' ? this['parseSuppliersCSV'] = {} : exports);
Any ideas what I am doing wrong? Am I approaching this the wrong way?
I'll take a guess here and assume the promise you created should resolve to something...instead of returning a value.
.on("end", function ()
{
console.log("done");
//all OK here
console.log(suppliersData);
//this doesn't seem to return anything
return resolve(suppliersData);
});

How to pass a promise to a controller in AngularJS

I'm having trouble understanding promises. I have a function $scope.startJob that when called, it calls areThereAvailableSegments in the SegmentsService. The idea is that areThereAvailableSegments should return true or false, so that I can handle this behavior in the controller, but not sure how to do this.
areThereAvailableSegments calls getSegments(), which requests text segments from the database. getSegments returns a promise, and I'm trying to handle this promise with .then in areThereAvailableSegments, but not sure how to pass to the controller.
Here's my Segments factory, that returns the $resource object, segmentsfactory.js:
angular.module('appApp')
.factory('SegmentsFactory', function ($resource) {
return $resource('http://localhost:3000/api/v1/segments/:id');
});
Here's my Segments service, segmentsservice.js:
angular.module('appApp')
.service('SegmentsService', function (SegmentsFactory) {
// gets segments from mongo
this.getSegments = function(jobId) {
return SegmentsFactory.query({ job_id: jobId }).$promise;
};
// should return true/false if there are available segments
this.areThereAvailableSegments = function(jobId) {
var dump = [];
// queries for all segments
this.getSegments(jobId)
.then(function(segments) {
// dumps segments status in a temp array
for (var i = 0; i < segments.length; i++) {
dump.push(segments[i].status);
}
// returns true if there is an 'available' in the temp array
if (dump.indexOf('available') >= 0) {
return true;
} else {
return false;
}
});
};
});
And here's my controller, main.js:
$scope.startJob = function() {
if (SegmentsService.areThereAvailableSegments(jobId)) {
console.log('there are available segments, then trigger texteditor');
} else {
console.log('no segments to translate');
}
};
When executing, console shows "no segments to translate", because SegmentsService is not returning true or false.
How to solve this?
the function areThereAvailibleSegments should return a promise as well.
And then in your controller you have a then case and have your if-else there.
this.areThereAvailableSegments = function(jobId) {
var dump = [];
// queries for all segments
return this.getSegments(jobId) //Return here
.then(function(segments) {
// dumps segments status in a temp array
for (var i = 0; i < segments.length; i++) {
dump.push(segments[i].status);
}
// returns true if there is an 'available' in the temp array
if (dump.indexOf('available') >= 0) {
return true;
} else {
return false;
}
});
};
since your success function returns true or false the promise from areThereAvailibleSegments-function will have that boolean.
Then in you controller:
SegmentsService.areThereAvailableSegments(jobId).then(function(available){
if (available) {
console.log('there are available segments, then trigger texteditor');
} else {
console.log('no segments to translate');
}
});
Theres no reason to have both the service and factory. Remove the factory and move that code into the service. As for your problem, you will have to use the $q module to return a promise. https://docs.angularjs.org/api/ng/service/$q
this.areThereAvailableSegments = function(jobId) {
var dump = [];
var deferred = $q.deferred;
// queries for all segments
this.getSegments(jobId)
.then(function(segments) {
// dumps segments status in a temp array
for (var i = 0; i < segments.length; i++) {
dump.push(segments[i].status);
}
// returns true if there is an 'available' in the temp array
if (dump.indexOf('available') >= 0) {
deferred.resolve(true);
} else {
deferred.resolve(false);
}
});
return deferred.promise;
};
You will then have to deal with the promise in the controller.
I would set it up like this...complete the promise in your factory and then call the data in the controller.
In your services js:
.factory('SegmentsFactory', function ($resource) {
var APIRequest = {};
APIRequest.getSegments = function(id){
segmentsAPI = $resource('http://localhost:3000/api/v1/segments/'+id'', ;
return segmentAPI.get().$promise.then(function(segments){
console.log(segments);
return segments;
});
};
return APIRequest;
}]);
And then in your controller:
var dump = [];
$scope.startJob = function() {
if (SegmentsService.areThereAvailableSegments) {
APIRequest.getSegments($scope.id).then(function(data){
if (data.error){
//error stuff here
} else {
if (data.segments.length > 0){
for(var i=0; i < segments.length; i++){
console.log(segments);
dump.push(segments[i].status);
}
};

Javascript Returning from within a nested function

I'm trying to return the output of the randomizer function within a route... I keep getting 'undefined' - but no idea what I'm doing wrong...
var randomizer = function() {
// A load of stuff happens here.. and functions that are needed by the pullOut function (I've removed for brevity)
var pullOut = function(pick) {
if (playerList.length !== pick) {
var random_item = getRandomItem(list, weight);
if (playerList.indexOf(random_item) == -1) { // doesn't exist. So add to array.
playerList.push(random_item);
}
pullOut(pick);
} else {
console.log(playerList)
return playerList;
}
}
return pullOut(pick);
}
router.route('/ordercreated')
.post(function(req, res) {
var orderedItems = req.body.line_items;
// I foreach through all the items - calling the randomizer function on each one...
_.forEach(orderedItems, function(n) {
Pack.findOne({
'product_id': n.variant_id
}, function(err, pack) {
if (err) {
return res.send(err);
}
if (pack) {
var list = [];
var weight = [];
_.forEach(pack.playerData, function(n) {
list.push(n.name);
weight.push(parseInt(n.chance));
});
console.log('randomizing', randomizer(pack.title, list, weight, n.qty, pack.pick));
}
});
});
res.sendStatus(200);
})
Your "pullOut" function calls itself, but it throws away the result of that call.
var randomizer = function() {
// A load of stuff happens here.. and functions that are needed by the
// pullOut function (I've removed for brevity)
var pullOut = function(pick) {
if (playerList.length !== pick) {
var random_item = getRandomItem(list, weight);
if (playerList.indexOf(random_item) == -1) { // doesn't exist. So add to array.
playerList.push(random_item);
}
return pullOut(pick); // <--- add return
} else {
console.log(playerList)
return playerList;
}
}
return pullOut(pick);
}
Without that return, when the function took that path through the main if statement it would return undefined.

Angular Nested Promise

I’m really struggling to write a complex function in Angular that depends on promises. This is my first time writing a promise and I'm still not sure I fully understand how to do what I want to do with my code.
I have a variable var query = searchQuery.getQuery() in a controller ProfileNavCtrl. Then in my searchQuery service, getQuery fetches the value of localStorage.getItem('searchQuery') and checks if it’s an empty string or null. If it’s not empty or null, it simply returns the value to the controller. The value should be an array of slugs like ['foo','foo-bar','foo-bar-baz'].
If it is null or empty, it executes an $http.get call to fetch a JSON object and parse it. This is where things break down for me. I need getQuery() to return the value from $http.get (if the initial value of query is null) so that the controller variable query is assigned that value. As it is now, query (in the controller) is always set to null or undefined.
The $http.get call also calls setQuery() so that the query is persisted and future calls are avoided.
Here is my controller:
app.controller('ProfileNavCtrl', ['$scope', '$http', '$location', '$q', 'searchQuery',
function($scope, $http, $location, $q, searchQuery){
var query = searchQuery.getQuery;
// do something with query
And here is my service:
app.service('searchQuery', ['$http', '$timeout', '$q', function($http, $timeout, $q){
var query = [];
this.getQuery = new Promise(function(){
var query = localStorage.getItem('searchQuery');
if(query == "" || query == [""] || query == null){
var slugArray = [];
var query = $http.get('/companies.json')
.then(function(resp) {
if(resp && resp.data) {
for(var i in resp.data) {
var result = resp.data[i];
if(resp.data[i].name){
slugArray.push(resp.data[i].name.toLowerCase().split(' ').join('-'));
}
}
setQuery(slugArray);
} else {
resetQuery();
}
}, function(err) {
resetQuery();
}).then(function(resp){
return resp;
})
return query;
} else {
return query;
};
}).then(function(success){
return success;
});
UPDATE: 2nd Attempt
Here is my controller code:
var getQuery = searchQuery.getQuery();
getQuery.then(function(query){
query = searchQuery.getQuery();
// Check if user is on main site or portal
if(location.pathname.split('/')[3] == null){
var currentProfile = location.pathname.split('/')[1];
} else {
var currentProfile = location.pathname.split('/')[3];
};
// Get the next/prev query element (if any)
console.log('6: ');
console.log(query);
var prev = query.slice(query.indexOf(currentProfile)-1)[0];
var next = query.slice(query.indexOf(currentProfile)+1)[0];
// Check if next/prev is undefined and if so, set to first/last element in query array
if(prev){
var prevProfile = prev;
} else {
var prevProfile = query.pop();
};
if(next){
var nextProfile = next;
} else {
var nextProfile = query[0];
};
$scope.goToPrev = function() {
// Check if user is on main site or portal
if(location.pathname.split('/')[3] == null){
var profileUrl = location.origin + '/' + prevProfile;
// window.location = profileUrl;
console.log(profileUrl);
} else {
var profileUrl = location.origin + '/' + location.pathname.split('/').slice(1,3).join('/') + '/' + prevProfile;
// window.location = profileUrl;
console.log(profileUrl);
}
};
$scope.goToNext = function() {
// Check if user is on main site or portal
if(location.pathname.split('/')[3] == null){
var profileUrl = location.origin + '/' + nextProfile;
// window.location = profileUrl;
console.log(profileUrl);
} else {
var profileUrl = location.origin + '/' + location.pathname.split('/').slice(1,3).join('/') + '/' + nextProfile;
// window.location = profileUrl;
console.log(profileUrl);
}
};
});
Here is my updated service:
this.getQuery = function(){
return new Promise(function(){
var query = localStorage.getItem('searchQuery');
if(query == "" || query == [""] || query == null){
var slugArray = [];
return $http.get('/companies.json')
.then(function(resp) {
if(resp && resp.data) {
for(var i in resp.data) {
var result = resp.data[i];
if(resp.data[i].name){
slugArray.push(resp.data[i].name.toLowerCase().split(' ').join('-'));
}
}
setQuery(slugArray);
} else {
resetQuery();
}
return slugArray;
}, function(err) {
resetQuery();
});
} else {
return query;
};
});
};
In Angular promises are provided through the $q service. See the documentation for more detail.
The basic outline to implement $q promise in your service is outlined below, I'll leave the detail on how to save to local storage etc to you:
this.getQuery = function(){
var deferred = $q.defer();
var query = localStorage.getItem('searchQuery');
if(query == "" || query == [""] || query == null){
$http.get('yoururl').then(function(resp) {
// assuming resp is an array, else do your parsing to get array
query = resp;
deferred.resolve(query);
}, function(err) {
query = null;
deferred.reject(err);
});
} else {
deferred.resolve(query);
};
return deferred.promise;
};
You can then use this in your controller like:
var query = null;
searchQuery.getQuery().then(function(result) {
query = result;
}, function(err) {
// Error occured
});

Categories