I'm currently using Worklight 6.2 to develop an hybrid application for iOS, Android and Windows 8 thanks to Ionic Framework (based on AngularJS). I developed most of my app without any problem for now, but today I started thinking about using JSONStore to persist and secure my local data (my application can be used offline).
So before now, I used scopes and the root scope of Angular to update my DOM thanks to data binding, as we always do with Angular. For example, I create a new item with the HMI, and thanks to data binding, my objects are immediately displayed in my app without having to reload anything, and my Javascript objects are updated with the latests values.
Now that I want to plug JSONStore, I'm facing the following issue : what is the most consistent and reliable way for my app to have my JSONStore synchronized with my root scope so that every time I get new updates in my scope, JSONStore is also updated and thus, data is saved and secured on the device (and ready to be synchronized with my backend).
My absolute reference must be JSONStore as it is my official reliable solution for persistent data storage (and encryption). The best thing would be for my app to use my JSONStore collection "as the root scope", but as far as I know, it's not possible to do this with Angular (actually, I did not check, as it sounds very wrong). I ended up thinking that I need to maintain the 2 collections : my JSONStore and my root scope. Meaning that every time something is modified in the root scope, I have to apply it to the JSONStore. In order to facilitate maintainability, I think about observing my root scope's main object using the $watch listener, so that every time my root scope is modified, I crush my JSONStore with the latest value of my root scope. Something like this :
$scope.$watch(function(){
// Object to be watched
return $rootScope.myObject;
}, function(newValue, oldValue){
// Here, destroy the collection and init it again with the newValue object
});
I'm a little concerned about performances of watching this object during the whole lifecycle of my application. Even if the object will not be modified very often, the object can be "quite" big (50kb). Also, I'm not sure JSONStore really enjoys having its collections crushed every time. But not using this would mean identifying the "modification spots" to update JSONStore every time a modification is done to my root scope. This could work, but is really not optimal as we need to get used to duplicate source code (1 for the scope, 1 for the JSONStore).
Well, this is bothering me because I can't find a really viable solution, so if you guys have ideas / best practices about this, don't hesitate to share !
Thank you
The way to do it is to synchronise your scope to the JSONStore in the "background" and pull the data out when you need to for ex: on app start.
Watching JSONStore would require you to query the JSONStorage in the watch function which would be slow and wasteful.
As @cnandreu suggested you can interface with the JSONStorage via an angularjs service.
Here is a simple implementation with background synchronisation, taken from this article
|| note that the below code syncs the whole object, whereas if your object gets bigger I suggest to do some add/update and remove items functions in the service.||
In your controller:
// get the object
todoJsonStorage.get().then(function(res) {
$scope.todos = res;
});
// synchronise in the background
$scope.$watch('todos', function (newValue, oldValue) {
if (newValue !== oldValue) { // This prevents unneeded calls to the local storage
todoJsonStorage.put(todos);
}
}, true);
Your service is something like this:
/**
* Services that persists and retrieves TODOs from Worklight JSON Storage
*/
angular.module('todomvc')
.factory('todoJsonStorage', function ($q) {
'use strict';
var COLLECTION_NAME = 'todos';
var collections = {
todos: {
searchFields: {title:'string', completed:'boolean'}
}
};
var inited = false;
//checks if inited and if not inits
function initJSONStore(){
var initDeferred = $q.defer();
if (inited){
initDeferred.resolve();
} else {
//Initialize the collection
WL.JSONStore.init(collections).then(function () {
console.log("-> JSONStore init successful");
initDeferred.resolve();
}).fail(function (errorObject) {
console.log("-> JSONStore error: " + errorObject.msg);
});
return initDeferred.promise;
};
}
return {
get: function () {
var deferred = $q.defer();
initJSONStore().then(function(){
WL.JSONStore.get(COLLECTION_NAME).findAll().then(function (res) {
if (res.length > 0){
deferred.resolve(JSON.parse(res[0].json.data || '[]'));
} else {
deferred.resolve(res);
}
}).fail(function (errorObject) {
console.log("JSONStore findbyid error: " + errorObject.msg);
});
});
return deferred.promise;
},
put: function (todos) {
WL.JSONStore.get(COLLECTION_NAME).clear()
WL.JSONStore.get(COLLECTION_NAME).add({data:JSON.stringify(todos)});
}
};
});