Wiki Markup |
---|
Requirements: |
...
# The mobile solution need to have the headless client type |
...
# Needs a form with name "offline_data" |
...
# The form offline_data needs a method ws_read(version,name) |
...
# If the data shown in mobile app is user specific the form offline_data needs a method ws_authenticate(useruid,password) |
...
# The mobile_service and rest_ws plugin to be installed |
...
h3. Constructing a offline data package for the mobile client |
...
The ws_read(version,name) on the offline_data form has to return an OfflineDataDescription (=JSON) object filled with foundset data the developer wants the mobile client to retrieve. |
...
An OfflineDataDescription instance is created with: |
...
{code |
} var retval = plugins.mobileservice.createOfflineDataDescription('data_'); {code} Note: the argument is optional and is used to call rest endpoints for row data at forms starting with this prefix |
...
Then we need to provide a list of relation names which should be used in traversal to find all data, like: |
...
{code |
} var traverse = new Array(); traverse.push('accountmanager_to_companies'); traverse.push('companies_to_contacts'); {code} Lastly we have instruct the OfflineDataDescription to collect the data, starting with root foundset (containing records) and return. |
...
{code |
} retval.addFoundSet(fs_contact, traverse); return retval; {code} Note: For each record in the provided (root) foundset the specified relations are traversed and all data taken. |
...
h3. User specific data |
...
In order to provide a mobile client with user specific data the ws_authenticate(useruid,password) method should be added: |
...
{code |
} function ws_authenticate(useruid,password) { //TODO find user and check password against pwhash column if (password == 'demo')//static demo check { var retval = new Object(); retval.username = useruid; return retval; } return false; } {code} In the ws_read method we can utilize the authenticate username variable via |
...
{code |
} var authenticate_info = questionParams.ws_authenticate[0]; globals.username = authenticate_info.username; {code} Here the authenticate username is put into a global variable which in turn can be used like: |
...
{code |
} //prepare personal data var fs_contact = globals.contact_data$username;//global related foundset using username global var, containing the account manager contact {code} Full ws_read method for personalized data. |
...
{code |
} function ws_read(version,name) { var questionParams = arguments[arguments.length-1]; //create return value var retval = plugins.mobileservice.createOfflineDataDescription('data_'); //setting the key for user_select relation var authenticate_info = questionParams.ws_authenticate[0]; globals.username = authenticate_info.username; //prepare personal data var fs_contact = globals.contact_data$username;//global related foundset using username global var, containing the account manager contact /** * @type {Array<String>} */ var traverse = new Array(); traverse.push('accountmanager_to_companies'); traverse.push('companies_to_contacts'); retval.addFoundSet(fs_contact, traverse); return retval; } {code} h3. Providing/retrieving entity(=table) row data |
...
Row/record data is retrieved in separate calls for each entity, for example for "orders" row data results in a call to "orders" form is made on ws_read method. |
...
Note: If a prefix is provided in the offlinedata the call will endup at prefix+entityname, example for prefix "data_" the call happens on form "data_orders" |
...
ws_read is with a list of pks it wants as row data for, example code: |
...
{code |
} function ws_read(version,method) { var questionParams = arguments[arguments.length-1]; if (method == 'list') { /** * @type {String} */ var ids = questionParams.ids[0]; if (ids != null && ids != '') { /** * @type {Array<Object>} */ var idsa = ids.split(',', -1); if (idsa.length > 0) { foundset.loadAllRecords(); var json = plugins.mobileservice.getRowDescriptions(foundset, idsa) return json; } } } throw 404; } {code} TIP: since ws_read for entities is likely the same, it might be beneficial to create a base form containing this logic and extend from this form |
...
ws_update is called for changes made by mobile client, example code: |
...
{code |
} function ws_update(data,version,pk) { if (foundset.find()) { foundset.contact_id = pk; var count = foundset.search(); if (count > 0) { var rec = foundset.getRecord(1); rec.name_first = data.name_first; rec.name_last = data.name_last; rec.email = data.email; rec.phone_cell = data.phone_cell; databaseManager.saveData(rec); } foundset.loadAllRecords(); } } {code} ws_create is called for new records on the mobile client, example code: |
...
{code |
} function ws_create(data,version,pk) { var rec = foundset.getRecord(foundset.newRecord()); rec.name_first = data.name_first; rec.name_last = data.name_last; rec.email = data.email; rec.phone_cell = data.phone_cell; rec.company_id = data.company_id; databaseManager.saveData(rec); foundset.loadAllRecords(); } {code} Note: the retrieved PK (and derived FK's) is always UUID's if the underlaying datamodel is not UUID based, keep and apply a mapping\! h5. Other pages {list-siblings}\\ |