Visual Builder allows you to create applications that can continue to function even when your device is disconnected from the network. To do that Visual Builder leverages the Oracle JET offline persistence toolkit. This toolkit enables your application to cache data on the client and serves it back from the cache when you are issuing the same REST call but your device doesn't have access to the server. It also allows you to "execute" operations such as adding records while offline - storing those requests on the client again - and then automate replaying them back when you are connected.
In the demo video below I show how to add some of these capabilities to your application. It's important to note that adding offline capabilities requires knowledge in JavaScript coding and an understanding of the offline toolkit. This is not a simple drag and drop operation - so approach carefully. Leverage the extensive logging that the offline persistence can do and monitor it's messages in the browser's dev tools console to see what is happening when. In the video you'll also see how to clear the cache on the client which is sometimes needed to see the real functionality.
The Visual Builder developer guide has a section that explains the concepts of offline persistence, and provides a basic starting point code sample. In the video below, I start from that code sample, then I modify it to change the cache strategy and implement support for POST operations and synch data changes on demand.
Here is a breakdown of things you'll see in the video (allowing you to skip to the section you need):
As you can see the interaction with the offline persistence layer is managed in the application's JavaScript area. Below is the code used in my app, with the parts that I change from the doc sample explained below.
define([ 'vbsw/helpers/serviceWorkerHelpers', /** * Add the following entries to include the toolkit classes that you'll use. More information about these * classes can be found in the toolkit's API doc. See the link to the API doc in the paragraph before * this sample file. * */ 'persist/persistenceManager', 'persist/defaultResponseProxy', 'persist/persistenceUtils', 'persist/fetchStrategies', /** * Add the following entry to enable console logging while you develop your app with the toolkit. */ 'persist/impl/logger' ], function(ServiceWorkerHelpers, PersistenceManager, DefaultResponseProxy, PersistenceUtils, FetchStrategies, Logger) { 'use strict'; function AppModule() {} function OfflineHandler() { /** * Enable console logging of the toolkit for development testing */ Logger.option('level', Logger.LEVEL_LOG); Logger.option('writer', console); var options = { /** * The following code snippets implements the toolkit's CacheFirstStrategy. This strategy * checks the application's cache for the requested data before it makes a request to cache * data. The code snippet also disables the background fetch of data. */ requestHandlerOverride: { handlePost: handlePost }, fetchStrategy: FetchStrategies.getCacheIfOfflineStrategy({ backgroundFetch: 'disabled' }) }; this._responseProxy = DefaultResponseProxy.getResponseProxy(options); } OfflineHandler.prototype.handleRequest = function(request, scope) { /** * (Optional). Write output from the OfflineHandler to your browser's console. Useful to help * you understand the code that follows. */ console.log('OfflineHandler.handleRequest() url = ' + request.url + ' cache = ' + request.cache + ' mode = ' + request.mode); /** * Cache requests where the URL matches the scope for which you want data cached. */ if (request.url.match( 'https://yourserver.oraclecloudapps.com/ords/shay' )) { return this._responseProxy.processRequest(request); } return PersistenceManager.browserFetch(request); }; OfflineHandler.prototype.beforeSyncRequestListener = function(event) { return Promise.resolve(); }; OfflineHandler.prototype.afterSyncRequestListener = function(event) { return Promise.resolve(); }; AppModule.prototype.createOfflineHandler = function() { /** Create the OfflineHandler that makes the toolkit cache data URLs */ return Promise.resolve(new OfflineHandler()); }; AppModule.prototype.isOnline = function() { return ServiceWorkerHelpers.isOnline(); }; AppModule.prototype.forceOffline = function(flag) { return ServiceWorkerHelpers.forceOffline(flag).then(function() { /** if online, perform a data sync */ if (!flag) { return ServiceWorkerHelpers.syncOfflineData(); } return Promise.resolve(); }).catch(function(error) { console.error(error); }); }; AppModule.prototype.dataSynch = function() { return ServiceWorkerHelpers.syncOfflineData(); }; // custom implementation to handle the POST request var handlePost = function(request) { if (ServiceWorkerHelpers.isOnline()) {} return PersistenceUtils.requestToJSON(request).then(function( requestData) { console.log('Inside PersistenceUtils'); console.log(requestData); requestData.status = 202; requestData.statusText = 'OK'; requestData.headers['content-type'] = 'application/json'; requestData.headers['x-oracle-jscpt-cache-expiration-date'] = ''; // if the request contains an ETag then we have to generate a new one var ifMatch = requestData.headers['if-match']; var ifNoneMatch = requestData.headers['if-none-match']; if (ifMatch || ifNoneMatch) { var randomInt = Math.floor(Math.random() * 1000000); requestData.headers['etag'] = (Date.now() + randomInt).toString(); requestData.headers['x-oracle-jscpt-etag-generated'] = requestData.headers['etag']; delete requestData.headers['if-match']; delete requestData.headers['if-none-match']; } return PersistenceUtils.responseFromJSON(requestData); }); }; return AppModule; });
To highlight the major changes in the code done in the video:
Keep in mind that offline caching should be used with an understanding of the risks involved. For example, your cached data can get stale and not match the real situation in the server.So while caching can be a nice performance boost, you should probably use it for data that is not frequently changing. Also since data is cached on the client, other people could access it if they get access to the client. Don't cache sensitive data that you don't want falling into the wrong hands if you loose your device.
We are planning to add support for configuring caching of BOs in an upcoming release.
For further questions use our forum:
https://cloudcustomerconnect.oracle.com/resources/e610f4723c/summary