CRUD Operation in JSONStore using MobileFirst Platform 7.1 - ibm-mobilefirst

I'm new to MFP and I'm trying to perform a basic CRUD operation. Nothing is happening after the following code is executed. I will highly appreciate if i can get some help. Thank you.
main.js
function wlCommonInit () {
var collections = {
people : {
searchFields: {name: 'string', age: 'integer'}
}
};
WL.JSONStore.init(collections).then(function (collections) {
// handle success - collection.people (people's collection)
}).fail(function (error) {
alert("alert" + error);
// handle failure
});
var collectionName = 'people';
var options = {};
var data = {name: 'yoel', age: 23};
WL.JSONStore.get(collectionName).add(data, options).then(function () {
// handle success
}).fail(function (error) {
// handle failure
});
// to display results using query yoel
var query = {name: 'yoel'};
var collectionName = 'people';
var options = {
exact: false, //default
limit: 10 // returns a maximum of 10 documents, default: return every document
};
WL.JSONStore.get(collectionName).find(query, options).then(function (results) {
// handle success - results (array of documents found)
}).fail(function (error) {
// handle failure
});
}//end wlCommonInit

JSONStore is asynchronous. With the code you wrote you cannot be sure of the order it is run.
The JavaScript code is most likely calling one of your add() or find() before your init() happens.

I would suggest you not writing the code within wlCommonInit because JSONStore may not be loaded yet. You could try tying it to a event like a button press or just put it into a function then call it in the console. Also, like #Chevy Hungerford has said, JSONStore is asynchronous so utilize the promises by chaining.
var collections = {
people : {
searchFields: {name: 'string', age: 'integer'}
}
};
// to display results using query yoel
var query = {name: 'yoel'};
var options = {
exact: false, //default
limit: 10 // returns a maximum of 10 documents, default: return every document
};
var collectionName = 'people';
var data = [{name: 'yoel', age: 23}]; //best if the data is an array
WL.JSONStore.init(collections).then(function (collections) {
// handle success - collection.people (people's collection)
return WL.JSONStore.get(collectionName).add(data);
})
.then(function (res){
return WL.JSONStore.get(collectionName).find(query, options)
})
.then(function (res){
//handle success - getting data
})
.fail(function (error) {
alert("alert" + error);
// handle failure
});

Related

Nuxtjs data() variable cannot change

I hava a simple nuxt/vue app. It uses an API to call filters and data, but the fetch hook dont change the variable. Here's my code:
data() {
return {
filters: {},
fulldata: [],
filterName: '',
}
},
async fetch({app}) {
app.$axios.$get('/api/oklevelek').then(data => {
this.fulldata = data
this.filters = data.possibleFilters
console.log(this.fulldata) //data shows
})
console.log('adatok')
console.log(this.fulldata) //undefined
},
fetchOnServer: true,
methods: {
test() { //test method for data
console.log('test')
console.log(this.fulldata) //undefined
},
},
Thanks for your time and appreciate your help!
You need to await the response from your fetch call. At the moment, you’re trying to console.log this.fulldata immediately after calling the api, before the data has had chance to return.
async fetch({app}) {
app.$axios.$get('/api/oklevelek').then(data => {
this.fulldata = data
this.filters = data.possibleFilters
console.log(this.fulldata) //data shows
})
console.log('adatok')
// this runs immediately after the api call
console.log(this.fulldata) //undefined
}
This should help:
async fetch({app}) {
// add await here
let { data } = await app.$axios.$get('/api/oklevelek')
// now this code waits for the api call to finish
this.fulldata = data
this.filters = data.possibleFilters
console.log(this.fulldata)
console.log('adatok')
console.log(this.fulldata)
}

How to update nested Objects

I want to use the $push method to push an object into a nested array. But i cant get it to work that you can dynamically get the right object inside of the array. Let me explain better by showing the code.
This is my Schema:
var StartedRaceSchema = new mongoose.Schema({
waypoints: {
type: Object,
name: String,
check_ins: {
type: Object,
user: {
type: Object,
ref: 'User'
}
}
}
});
When you check in on a waypoint, it has to be pushed in the correct waypoints nested Check_ins
This is the code for the update:
StartedRace.findByIdAndUpdate(req.params.id,
{ $push: { 'waypoints.1.check_ins': req.body.user } },
function (error) {
if (error) {
console.log(error)
res.send({
success: false,
error: error
})
} else {
res.send({
success: true
})
}
}
)
As you can see i can only get it to work with fields like:
'waypoints.1.check_ins'
That 1 needs to be dynamically because it gets send within the parameters.
But i can not get it to work dynamically, only hard coded.
Does anyone know how to do this?
Populate the collection with a list of check_ins enumerated by their ids.
waypoints.check_ins = {
...waypoints.check_ins,
[response.id]: response
}
You'd then have a list of check_ins that can referenced by their ids.
You could try this syntax instead of the dot notation:
let id = req.params.id;
StartedRace.findByIdAndUpdate(req.params.id,
{ $push: { waypoints: { id: { check_ins: req.body.user } } } }, { new : true } )
.exec()
.then(race => console.log(race))
.catch(err => err);
I used a Promise, but it's the same with a callback.

How to use realm with async fetch request?

I have meet this situation for my requirements:
step 1. save data to local db which in the mobile phone (realm)
step 2. upload the local data to the server, and the server will return the data ids if success
step 3. delete the records in local db by the returned ids which get by step2
Realm.open({schema:[MySchame],encryptionKey:getRealmKey()})
.then(realm =>{
realm.write(() => {
// 1. get all step data from db
let objetcs = realm.objects('MySchema');
// 2. upload obtained data to server
if(objetcs.length > 0){
let recordArr = [];
for (let o of steps){
recordArr.push(o.get());
}
uploadDataToServer(recordArr,(res)=>{
//3. filter the uploaded steps and delete them
let uploadedSteps = realm.objects('MySchema').filtered('id=$0',res.id);
if(uploadedSteps.length > 0){
realm.delete(uploadedSteps);
}
});
}
});
realm.close();
})
.catch(error =>{
console.log(error);
});
but this is not works as expected, it seems DB is closed too early than networks success callback.
Thanks for any ideas.
Finally ,I use realm like this:
let realm = new Realm({schema:[JOB_SCHEMA.jobTrack],encryptionKey:getRealmKey()});
let objects = realm.objects('JobTrack');
realm.beginTransaction();
realm.delete(objects);
realm.commitTransaction();
realm.close();
First create a service like one below
import repository from "./realmConfig";
let CatalogsService = {
findAll: function () {
return repository.objects("CatalogsModel");
},
save: function (catalogs) {
repository.write(() => {
repository.create("CatalogsModel", catalogs);
});
},
delete: function () {
repository.write(() => {
let all = repository.objects("CatalogsModel");
repository.delete(all);
});
},
update: function (catalogs, callback) {
if (!callback) return;
repository.write(() => {
callback();
catalogs.updatedAt = new Date();
});
}
};
module.exports = CatalogsService;
where my realmConfig file is as
import Realm from "realm";
class CatalogsModel extends Realm.Object { }
CatalogsModel.schema = {
name: "CatalogsModel",
primaryKey: "id",
properties: {
id: "string",
name: "string",
url: "string",
status: "int"
}
};
class OffersModel extends Realm.Object { }
OffersModel.schema = {
name: "OffersModel",
primaryKey: "id",
properties: {
id: "string",
name: "string",
url: "string",
status: "int",
machineId: "string",
machineName: "string"
}
};
export default new Realm({
schema: [CatalogsModel, OffersModel],
schemaVersion: 1,
deleteRealmIfMigrationNeeded: true
});
Now import Service.js where you are calling async server call and do your job. For reference see below code
import CatalogService from './path/to/CatalogService .js'
//get objects
var catalogs = CatalogsService.findAll();
// fire async function , I prefer axios for network calls
Axios.post("SERVER_URL", {
data: catalogs
})
.then(function (response) {
if (response.success)
CatalogsService.delete()
}
I assume you can easily modify findAll() and delete() method as per your need

TransactionInactiveError: Failed to execute 'get' on 'IDBObjectStore': The transaction is inactive or finished

This seems to be a Safari only bug. It does not occur in Chrome as far as I can tell. I have a very standard IndexedDB setup. I call initDb, save the result, and that provides me a nice way to make calls to the DB.
var initDb = function() {
// Setup DB. whenDB is a promise we use before executing any DB requests so we know the DB is fully set up.
parentDb = null;
var whenDb = new Promise(function(resolve, reject) {
var DBOpenRequest = window.indexedDB.open('groceries');
DBOpenRequest.onsuccess = function(event) {
parentDb = DBOpenRequest.result;
resolve();
};
DBOpenRequest.onupgradeneeded = function(event) {
var localDb = event.target.result;
localDb.createObjectStore('unique', {
keyPath: 'id'
});
};
});
// makeRequest needs to return an IndexedDB Request object.
// This function just wraps that in a promise.
var request = function(makeRequest, key) {
return new Promise(function(resolve, reject) {
var request = makeRequest();
request.onerror = function() {
reject('Request error');
};
request.onsuccess = function() {
if (request.result == undefined) {
reject(key + ' not found');
} else {
resolve(request.result);
}
};
});
};
// Open a very typical transaction
var transact = function(type, storeName) {
// Make sure DB is set up, then open transaction
return whenDb.then(function() {
var transaction = parentDb.transaction([storeName], type);
transaction.oncomplete = function(event) {
console.log('transcomplete')
};
transaction.onerror = function(event) {
console.log('Transaction not opened due to error: ' + transaction.error);
};
return transaction.objectStore(storeName);
});
};
// Shortcut function to open transaction and return standard Javascript promise that waits for DB query to finish
var read = function(storeName, key) {
return transact('readonly', storeName).then(function(transactionStore) {
return request(function() {
return transactionStore.get(key);
}, key);
});
};
// A test function that combines the previous transaction, request and read functions into one.
var test = function() {
return whenDb.then(function() {
var transaction = parentDb.transaction(['unique'], 'readonly');
transaction.oncomplete = function(event) {
console.log('transcomplete')
};
transaction.onerror = function(event) {
console.log('Transaction not opened due to error: ' + transaction.error);
};
var store = transaction.objectStore('unique');
return new Promise(function(resolve, reject) {
var request = store.get('groceryList');
request.onerror = function() {
console.log(request.error);
reject('Request error');
};
request.onsuccess = function() {
if (request.result == undefined) {
reject(key + ' not found');
} else {
resolve(request.result);
}
};
});
});
};
// Return an object for db interactions
return {
read: read,
test: test
};
};
var db = initDb();
When I call db.read('unique', 'test') in Safari I get the error:
TransactionInactiveError: Failed to execute 'get' on 'IDBObjectStore': The transaction is inactive or finished
The same call in Chrome gives no error, just the expected promise returns. Oddly enough, calling the db.test function in Safari works as expected as well. It literally seems to be that the separation of work into two functions in Safari is somehow causing this error.
In all cases transcomplete is logged AFTER either the error is thrown (in the case of the Safari bug) or the proper value is returned (as should happen). So the transaction has NOT closed before the error saying the transaction is inactive or finished is thrown.
Having a hard time tracking down the issue here.
Hmm, not confident in my answer, but my first guess is the pause that occurs between creating the transaction and starting a request allows the transaction to timeout and become inactive because it finds no requests active, such that a later request that does try to start is started on an inactive transaction. This can easily be solved by starting requests in the same epoch of the javascript event loop (the same tick) instead of deferring the start of a request.
The error is most likely in these lines:
var store = transaction.objectStore('unique');
return new Promise(function(resolve, reject) {
var request = store.get('groceryList');
You need to create the request immediately to avoid this error:
var store = transaction.objectStore('unique');
var request = store.get('groceryList');
One way to solve this might be simply to approach the code differently. Promises are intended to be composable. Code that uses promises generally wants to return control to the caller, so that the caller can control the flow. Some of your functions as they are currently written violate this design pattern. It is possible that by simply using a more appropriate design pattern, you will not run into this error, or at least you will be able to identify the problem more readily.
An additional point would be your mixed use of global variables. Variables like parentDb and db are just going to potentially cause problems on certain platforms unless you really are an expert at async code.
For example, start with a simple connect or open function that resolves to an open IDBDatabase variable.
function connect(name) {
return new Promise(function(resolve, reject) {
var openRequest = indexedDB.open(name);
openRequest.onsuccess = function() {
var db = openRequest.result;
resolve(db);
};
});
}
This will let you easily compose an open promise together with code that should run after it, like this:
connect('groceries').then(function(db) {
// do stuff with db here
});
Next, use a promise to encapsulate an operation. This is not a promise per request. Pass along the db variable instead of using a global one.
function getGroceryList(db, listId) {
return new Promise(function(resolve, reject) {
var txn = db.transaction('unique');
var store = txn.objectStore('unique');
var request = store.get(listId);
request.onsuccess = function() {
var list = request.result;
resolve(list);
};
request.onerror = function() {
reject(request.error);
};
});
}
Then compose it all together
connect().then(function(db) {
return getGroceryList(db, 'asdf');
}).catch(error);

How to pull data from SqlDataAdapter and store it in a JsonStore collection in MobileFirst

I have a SqlDataAdapter and I want to store it in a JsonStore collection in MobileFirst and Display it in table form. I have tried using Load() method but its not working.
this is my resultSetCollection.js file
;(function () {
WL.JSONStore.init({
resultSet : {
searchFields: {"EMP_NAME":"string","EMP_ID":"integer"}
}
}, {
// password : 'PleaseChangeThisPassword'
})
.then(function () {
return WL.Client.invokeProcedure({
adapter : 'EmployeeList',
procedure : 'getEmployeeLists',
parameters : []
});
})
.then(function (responseFromAdapter) {
alert('responseFromAdapter:' + JSON.stringify(responseFromAdapter.invocationResult.resultSet));
var accessor = WL.JSONStore.get('resultSet');
var data=responseFromAdapter.invocationResult.resultSet;
var changeOptions = {
replaceCriteria : ['EMP_ID', 'EMP_NAME'],
addNew : true,
markDirty : false
};
return accessor.change(data, changeOptions);
})
.then(function (response) {
console.log(response);
//Here I want to retrieve the collection and display it in a table
})
.fail(function (errObj) {
WL.Logger.ctx({pretty: true}).error(errObj);
});
}());
An adapter procedure request from a client application will have a response object in its success and failure callbacks. So lets assume that the request was successfull and data was returned from the backend server.
Lets also assume you have a JSONStore initialized and properly setup with a collection. You then only need to get the collection and add data to it.
The below example takes the full response from an HTTP adapter request and puts it as-is into a collection. You will of course need to create a better setup for your specific scenario...
Note that the code is not optimised and performance or with 100% proper logic. It's just a demonstration flow.
Tested in MobileFirst Platform Foundation 7.0.0.00.
main.js:
var collectionName = 'mydata';
var collections = {
mydata : {
searchFields : {data: 'string'},
}
};
function wlCommonInit(){
WL.JSONStore.init(collections).then(
function() {
var resourceRequest = new WLResourceRequest("/adapters/myadapter/getStories", WLResourceRequest.GET);
resourceRequest.send().then(resourceRequestSuccess, resourceRequestFailure);
}
);
}
function resourceRequestSuccess(response) {
WL.JSONStore.get(collectionName).add(response).then(
function(){
WL.Logger.info("successfully added response to collection");
displayDataFromCollection();
},
function() {
alert("failed adding response to collection");
}
);
}
function resourceRequestFailure() {
alert ("failure");
}
If you then like to fetch the data from the JSONStore and display it in the HTML, you could do something like this:
// get a specific item from the stored response and display it in a table
function displayDataFromCollection() {
WL.JSONStore.get(collectionName).findAll().then(
function(result) {
$("#mytable").append("<tr><td>" + result[0].json.responseJSON.rss.channel.title + "</td></tr>");
},
function() {
alert ("unable to display collection");
}
);
}
The index.html looks like this:
<table id="mytable">
</table>