Array returning undefined in Vue from indexedDB - arrays

The console.log in my indexedDB works and returns the result that I want; an array of objects that is currently in the store. So my code there is correct. I'm going to use this information to build a table. However, in Vue it returns undefined. I'm trying to set the leagues array in Vue to equal the result array that indexedDB gives, but it returns undefined.
This is the code in Vue:
<script>
import * as db from "../db/db.js";
export default {
name: "leaguesTable",
data: function() {
return {
leagues: []
};
},
created: function() {
this.leagues = db.getAllInStore("meta", "leagues");
console.log(this.leagues);
}
};
</script>
This is my indexedDB code:
function getAllInStore(dbName, storeName) {
let db;
var request = indexedDB.open(dbName, 1);
request.onerror = function(event) {
alert("Database error" + event.target.errorCode);
};
request.onsuccess = function(event) {
db = event.target.result;
let tx = db.transaction(storeName, "readonly");
tx.onerror = function(event) {
alert("Transaction error" + event.target.errorCode);
};
let store = tx.objectStore(storeName);
let result = store.getAll();
tx.oncomplete = function() {
alert("This should work");
console.log(result.result);
return result.result;
};
};
}

In your created hook you need to make sure to return a value from db.getAllInStore so that this.leagues assumes that value.
Next, In the getAllInStore function result.result gets returned from the transaction but not within onComplete or the enclosing getAllInStore function.
Since the db uses event hooks like onError and onComplete, Returning the request won't give you the result of the call to the db. In order to return the value of an async operation in javascript, typically callbacks or promises are used. The example below makes use of promises to solve the issue.
Vue JS:
<script>
import * as db from "../db/db.js";
export default {
name: "leaguesTable",
data: function() {
return {
leagues: []
};
},
// async is necessary to use await
created: async function() {
// await is es2016 syntactic sugar for retrieving the value of a promise
this.leagues = await db.getAllInStore("meta", "leagues");
console.log(this.leagues);
}
};
</script>
IndexDB:
function getAllInStore(dbName, storeName) {
// resolve param is a function that signifies a successful operation
// reject param is a function that should be called whenever a check or error occurs
return new Promise((resolve, reject) => {
let db;
let request = indexedDB.open(dbName, 1);
request.onerror = (event) => reject(event);
request.onsuccess = (event) => {
db = event.target.result;
let tx = db.transaction(storeName, "readonly");
request.onerror = (event) => reject(event);
let store = tx.objectStore(storeName);
let result = store.getAll();
tx.oncomplete = (result) => resolve(result.result);
};
});
}
Further Reading:
https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous
https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Promises

Related

AngularJS: Passing Promise, yet Cannot read property 'finally' of undefined

In my angular app, I have 2 methods save() and saveTriggers(). saveTriggers() updates all records by calling a web service (C#). I want to make sure that a block of code is executed after all records are updated in saveTriggers() and control is returned to save(). I believe I need to pass something from the saveTriggers() to make finally block execute. I tried various things, nothing works. Using .then() also gives the same error. I am not that good at JS. Can you please guide me.
vm.updatedTriggers = []; // IDs are pushed in
vm.saveTriggers = function () {
if (vm.updatedTriggers.length === 0) {
vm.close();
} else {
vm.saving = true;
vm.save()
.finally(function () { // ERROR - Cannot read property 'finally' of undefined
console.log("Saved all. Closing..."); // Never REACHES here
vm.saving = false;
vm.updated = true;
$uibModalInstance.close(true);
});
}
};
vm.save = function () {
//vm.saving = true;
for (var i = 0; i < vm.updatedTriggers.length; i++) {
var trigger = vm.triggers.find(t => t.id === vm.updatedTriggers[i]);
var input = {
id: trigger.id,
target: trigger.target,
targetInfo: vm.targetData,
event: trigger.event,
eventQuantity: trigger.eventQuantity,
eventQuantityExtra: trigger.eventQuantityExtra
};
rpmService.editDeviceTrigger(input);
/*.finally(function () {
console.log("Updated event"); // Reaches here
vm.updated = true;
return Promise.resolve(2);
});*/ // Commenting this also doesn't help
}
return Promise.resolve(2);
};
rpmService.editDeviceTrigger(input)
public async Task EditDeviceTrigger(EditDeviceTriggerInput input) {
// calls other methods with await
// Doesn't return anything
}
EDIT: Updated Code: I got rid of the error, but the output is not is expected series.
vm.saveTriggers = function () {
vm.saving = true;
vm.save().then
(function success() {
console.log("Returned Result ");
console.log("Saved all. Closing..."); // These lines are executed before the event is upated
vm.saving = false;
$uibModalInstance.close(true);
});
};
vm.save = function () {
var deferred = $q.defer();
for (var i = 0; i < vm.updatedTriggers.length; i++) {
var trigger = vm.triggers.find(t => t.id === vm.updatedTriggers[i]);
var input = {
id: trigger.id,
....
};
rpmService.editDeviceTrigger(input)
.finally(function () {
console.log("Updated event"); // Successfully updates all events
vm.updated = true;
});
}
deferred.resolve();
return deferred.promise;
};
OUTPUT:
Returned Result
Saved all. Closing...
Updated event
EXPECTED OUTPUT:
Updated event
Returned Result
Saved all. Closing...
Thanks.
Usually you dont need $q.defer-related things, but u can do same using it if u want.
Here I guess you just need to collect all your save promises and return new resulting one using $q.all:
vm.save = function () {
const myAwesomePromises = []
for (var i = 0; i < vm.updatedTriggers.length; i++) {
...
const savePromise = rpmService.editDeviceTrigger(input);
savePromise.finally(() => console.log('edit device finally'));// <-- not sure u need this
myAwesomePromises.push(savePromise);
}
return $q.all(myAwesomePromises).finally(() => console.log('All edit device finally'));
};

An empty array is returned when calling $http.get it within a service [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I want to get the name from an array that is being generated from $http.get, however this is returning an empty array. When i do a console it see the array populated however when i loop inside the array to get the value of name property based on whether an id is equal to a certain, the array is empty.
In my controller i have a service call that shall return the name value.
var params = { Id: $scope.Id, SettingId: $scope.SettingId };
$scope.selectedUserName = helloService.getSelectedUserName($scope.UserId, params);
In my service
I have used the getUserList function to populate the list of user in a dropdown and it works by generating the array with the values.
However When i got another page , i want to be able to display the name of the selected user, so I wanted to use the same getUserList function to retrieve the name
this.getUserList = function (val) {
var usersObj = [];
var url = "/api/v1/hello/getusers";
var params = { Id: val.Id, SettingId: val.SettingId };
var config = { params: params };
var promise = $http.get(url, config)
.then(function (response) {
angular.forEach(response.data, function (key, value) {
angular.forEach(key, function (k, index) {
usersObj[index] = ({ userId: k.userId, name: k.name});
});
});
},
function errorCallback(response) {
console.log("Unable to perform get request");
throw response;
});
var usersList = usersObj;
return usersObj;
};
this.getSelectedUserName = function (id, param) {
var name = "";
var userList =this.getUserList(param);
angular.forEach(userList, function (value, key) {
if (value.userId == id)
name = value.name;
});
return name;
}
Array length is 0 but if i do a console.log(userList) before the loop , the array display the list of user data
this.getSelectedUserName = function (id, param) {
var name = "";
var userList =this.getUserList(param);
console.log(userList) ;
angular.forEach(userList, function (value, key) {
if (value.userId == id)
name = value.name;
});
return name;
}
Thank you for kind responses.
Please see screenshot
This is simple Javascript, not specific to Angular. You can do
userList.forEach(user => {
if(user.userId === id) {
name = user.name;
}
});
return name;
you can try like this.
here we are using a async await.
Service
this.getUserList = function (val) {
var usersObj = [];
var url = "/api/v1/hello/getusers";
var params = { Id: val.Id, SettingId: val.SettingId };
var config = { params: params };
return new Promise((resolve, reject) => {
$http.get(url, config)
.then(function (response) {
angular.forEach(response.data, function (key, value) {
angular.forEach(key, function (k, index) {
usersObj[index] = ({ userId: k.userId, name: k.name});
});
});
},
function errorCallback(response) {
console.log("Unable to perform get request");
throw response;
});
var usersList = usersObj;
resolve(usersObj);
});
};
this.getSelectedUserName = async function (id, param) {
var name = "";
var userList = await this.getUserList(param);
console.log(userList);
angular.forEach(userList, function (value, key) {
if (value.userId == id)
name = value.name;
});
return name;
}
let me know if it is working or not.
EDIT:
If you're only trying to match one id in the array of users you don't even need to loop:
anArray = source.filter(source => source.toLowerCase().indexOf(id) === 0);
or
anObject = source.find(obj => obj.id === id);
Which Angular version is this? Your tag denotes 2.+ but you have $scope there which is ng1.x
Why can't you use ngFor in your view since you already have your arrays. You don't need to sort them in the control.
component
this.getSelectedUserName = function (id, param) {
let name = ""; // should be array if you want to add unames to it
let userList = this.getUserList(param);
// what is `angular` here? And why loop here? Use ngFor in view.
angular.forEach(userList, function (value, key) {
if (value.userId == id){
name = value.name; // will be overwritten each time
// should be name.push(value.name); // but loop in view instead
}
});
// this.users = name; // for your original sorted version
this.users = userList;
}
In your view
<li *ngFor="let user of users; index as i;>
{{user.name}}
</li>

dealing with an array of objects with promises

I am trying to make a node express app where I fetch data from different url's making a call to node-fetch to pull the body of some pages and other information about certain url endpoints. I want to then render a html table to display this data through an array of information. I am having trouble with the call to render the information as all the functions are asynchronous making it difficult to make sure all the promise calls have been resolved before making my call to render the page. I have been looking into using bluebird and other promise calls of .finally() and .all() but they don't seem to work on my data as it is not an array of promise calls, but an array of objects. Each object was 4 promise calls to fetch data relating to a column of my table all in one row. Is there a function or specific way to render the page after all promises are resolved?
var express = require('express');
var fetch = require('node-fetch');
fetch.Promise = require('bluebird');
var router = express.Router();
const client = require('../platform-support-tools');
function makeArray() {
var registry = client.getDirectory();
var data_arr = [];
for (var i = 0; i < registry.length; i++) {
var firstUp = 0;
for (var j = 0; i < registry[i]; j++) {
if (registry[i][j]['status'] == 'UP') {
firstUp = j;
break;
}
}
var object = registry[i][firstUp];
data_arr.push({
'name': object['app'],
'status': object['status'],
'swagUrl': object['homePageUrl'] + 'swagger-ui.html',
'swag': getSwag(object),
'version': getVersion(object['statusPageUrl']),
'timestamp': getTimestamp(object['statusPageUrl']),
'description': getDescription(object['healthCheckUrl'])
});
}
return data_arr;
}
function getSwag(object_in) {
var homeUrl = object_in['homePageUrl'];
if (homeUrl[homeUrl.length - 1] != '/'){
homeUrl += '/';
}
var datum = fetch(homeUrl + 'swagger-ui.html')
.then(function (res) {
return res.ok;
}).catch(function (err) {
return 'none';
});
return datum;
}
function getVersion(url_in) {
var version = fetch(url_in)
.then(function(res) {
return res.json();
}).then(function(body) {
return body['version'];
}).catch(function (error) {
return 'none';
});
return version;
}
function getTimestamp(url_in) {
var timestamp = fetch(url_in)
.then(function(res) {
return res.json();
}).then(function(body) {
return body['timestamp'];
}).then(function (res) {
return body['version'];
}).catch(function (error) {
return 'none';
});
return timestamp;
}
function getDescription(url_in) {
var des = fetch(url_in)
.then(function(res) {
return res.json();
}).then(function(body) {
return body['description'];
}).catch(function (error) {
return 'none';
});
return des;
}
/* GET home page. */
router.get('/', function (req, res, next) {
var data_arr = makeArray();
Promise.all(data_arr)
.then(function (response) {
//sorting by app name alphabetically
response.sort(function (a, b) {
return (a.name > b.name) ? 1 : ((b.name > a.name) ? -1 : 0);
});
res.render('registry', {title: 'Service Registry', arr: response})
}).catch(function (err) {
console.log('There was an error loading the page: '+err);
});
});
To wait on all those promises, you will have to put them into an array so you can use Promise.all() on them. You can do that like this:
let promises = [];
for (item of data_arr) {
promises.push(item.swag);
promises.push(item.version);
promises.push(item.timestamp);
promises.push(item.description);
}
Promise.all(promises).then(function(results) {
// all promises done here
})
If you want the values from all those promises, back into the object that's a bit more work.
let promises = [];
for (item of data_arr) {
promises.push(item.swag);
promises.push(item.version);
promises.push(item.timestamp);
promises.push(item.description);
}
Promise.all(promises).then(function(results) {
// replace promises with their resolved values
let index = 0;
for (let i = 0; i < results.length; i += 4) {
data_arr[index].swag = results[i];
data_arr[index].version = results[i + 1];
data_arr[index].timestamp = results[i + 2];
data_arr[index].description = results[i + 3];
++index;
});
return data_arr;
}).then(function(data_arr) {
// process results here in the array of objects
});
If you had to do this more often that just this once, you could remove the hard coding of property names and could iterate all the properties, collect the property names that contain promises and automatically process just those.
And, here's a more general version that takes an array of objects where some properties on the objects are promises. This implementation modifies the promise properties on the objects in place (it does not copy the array of the objects).
function promiseAllProps(arrayOfObjects) {
let datum = [];
let promises = [];
arrayOfObjects.forEach(function(obj, index) {
Object.keys(obj).forEach(function(prop) {
let val = obj[prop];
// if it smells like a promise, lets track it
if (val && val.then) {
promises.push(val);
// and keep track of where it came from
datum.push({obj: obj, prop: prop});
}
});
});
return Promise.all(promises).then(function(results) {
// now put all the results back in original arrayOfObjects in place of the promises
// so now instead of promises, the actaul values are there
results.forEach(function(val, index) {
// get the info for this index
let info = datum[index];
// use that info to know which object and which property this value belongs to
info.obj[info.prop] = val;
});
// make resolved value be our original (now modified) array of objects
return arrayOfObjects;
});
}
You would use this like this:
// data_arr is array of objects where some properties are promises
promiseAllProps(data_arr).then(function(r) {
// r is a modified data_arr where all promises in the
// array of objects were replaced with their resolved values
}).catch(function(err) {
// handle error
});
Using the Bluebird promise library, you can make use of both Promise.map() and Promise.props() and the above function would simply be this:
function promiseAllProps(arrayOfObjects) {
return Promise.map(arrayOfObjects, function(obj) {
return Promise.props(obj);
});
}
Promise.props() iterates an object to find all properties that have promises as values and uses Promise.all() to await all those promises and it returns a new object with all the original properties, but the promises replaced by the resolved values. Since we have an array of objects, we use Promise.map() to iterate and await the whole array of those.

My service is returning the function's text and not an object

I have a service to share an object in my app... I want to post that object to the mongo db but when I call the function that should return the object it gives me the function's text.
The service is here:
angular.module('comhubApp')
.service('markerService', function () {
this.markers = [];
this.newMarker = { title: '',
description: '',
lat: '',
lon: '',
user: '',
created_at: '' };
// This is supposed to return the marker object
this.newMarker = function () {
return this.newMarker;
};
this.setTitle = function (title) {
this.newMarker.title = title;
console.log('title service set: ' + title);
};
this.setDescription = function (description) {
this.newMarker.description = description;
console.log('Description service set: ' + description);
};
this.setLat = function (lat) {
this.newMarker.lat = lat;
console.log('lat service set: ' + lat);
};
this.setLon = function (lon) {
this.newMarker.lon = lon;
console.log('lon service set: ' + lon);
};
this.reset = function () {
this.newMarker = { title: '',
description: '',
lat: '',
lon: '',
user: '',
created_at: ''};
}
this.setMarkers = function (markers) {
this.markers = markers;
}
this.markers = function () {
return this.markers;
}
this.addMarker = function (marker) {
//todo append marker
}
});
newMarker returns:
this.newMarker = function () {
return this.newMarker;
};
The Controller using the service is here
$scope.addMarker = function() {
if($scope.newMarker.title === '') {
console.log('newMarker title is empty');
return;
}
markerService.setTitle($scope.newMarker.title);
markerService.setDescription($scope.newMarker.description);
console.log(markerService.newMarker());
// $http.post('/api/markers', { name: $scope.newMarker });
// $scope.newMarker = '';
};
$scope new marker is form data.. i tried to put that right into my service with no success. Instead I out the form data into the controller then push it to the service. If there is a better way to do that please let me know.
If this service is bad in any other way let me know I am new to all this and so I followed another answer I saw on here.
You are overriding your object with function. Just give them different names and it should work just fine.
this.newMarker = { ... };
this.getNewMarker = function () { return this.newMarker };
EDIT:
You should also always create new instance from marker. Otherwise you just edit the same object all the time. Here is example I made. Its not best practice but hope you get the point.
angular.module('serviceApp', [])
.factory('Marker', function () {
function Marker() {
this.title = '';
this.descrpition = '';
}
// use setters and getters if you want to make your variable private
// in this example we are not using these functions
Marker.prototype.setTitle = function (title) {
this.title = title;
};
Marker.prototype.setDescription = function (description) {
this.description = description;
};
return Marker;
})
.service('markerService', function (Marker) {
this.markers = [];
this.getNewMarker = function () {
return new Marker();
}
this.addMarker = function (marker) {
this.markers.push(marker);
}
})
.controller('ServiceCtrl', function ($scope, markerService) {
$scope.marker = markerService.getNewMarker();
$scope.addMarker = function () {
markerService.addMarker($scope.marker);
$scope.marker = markerService.getNewMarker();
}
$scope.markers = markerService.markers;
});
You could also create Marker in controller and use markerService just to store your object.
And working demo:
http://jsfiddle.net/3cvc9rrs/
So, that function is the problem. I was blindly following another example and it was wrong in my case. The solution is to remove that function and access markerService.newMarker directly.
I am still a big enough noob that I am not sure why the call was returning the function as a string. It seems to have something to do with how it is named but it is just a guess.

Add variable to an existing json object

request = myService.getCases();
request.then(
function(payload) {
$scope.cases = payload.data;
var time = Math.floor((Date.now() - Date.parse($scope.cases[i].date_case_modified))/(60000*60*24));
$scope.cases.duration.push(time);
}
});
Inside the controller I am trying to tack on the cases.duration onto the cases object but it wont add it onto the object that is returned. Any ideas?
I think you just need to introduce a forEach as shown here:
request = myService.getCases();
request.then(
function(payload) {
$scope.cases = payload.data;
angular.forEach($scope.cases, function (el) {
var time = Math.floor((Date.now() - Date.parse(el.date_case_modified))/(60000*60*24));
el.duration = time;
});
}
});
Hope this helps

Resources