/**
*@class oData
*@classdesc Extends OData Model and offers API to IndexedDB.
*@extends sap/ui/model/odata/v2/ODataModel
*/
sap.ui.define([
"sap/ui/model/odata/v2/ODataModel"
], function(ODataModel)
{
"use strict";
return ODataModel.extend("ui5.offlineFunct.model.oData", {
_oComponent: null,
constructor: function(sServiceURL, mParameters, oComponent)
{
this._oComponent = oComponent;
ODataModel.prototype.constructor.call(this, sServiceURL, mParameters);
},
…
sap.ui.define([
"sap/ui/model/json/JSONModel"
], function(Model)
{
"use strict";
var _instance = void 0;
var version = 0;
var IndexedDb = Model.extend("ui5.offlineFunct.model.IndexedDb", {
/**
*@description Constructor
*@memberOf IndexedDb
*@param {object} oComponent - Owner Component
*/
constructor: function(oComponent)
{
if (window.indexedDB === null)
{
console.error("Offline store not supported!");
return null;
}
this._oComponent = oComponent;
Model.prototype.constructor.call(this, {
"_meta": []
});
var request = indexedDB.open("localStorage");
request.onsuccess = function(oEvent)
{
IndexedDb._db = oEvent.target.result;
version = IndexedDb._db.version;
IndexedDb._db.close();
};
IndexedDb._instance = this;
},
"models":{
"i18n":{
"type":"sap.ui.model.resource.ResourceModel",
"settings":{
"bundleName":"ui5.offlineFunct.i18n.i18n"
}
}
},
models.createServiceModel(sServiceUrl, null, this)
.then(function(oServiceModel)
{
oServiceModel.setUseBatch(false);
console.log("Backend connection established");
var networkModel = that.getModel("network");
oServiceModel.attachRequestSent(function()
{
networkModel.setBusy(true);
});
oServiceModel.attachRequestCompleted(function()
{
networkModel.setBusy(false);
networkModel.setProperty("/connected", true)
});
oServiceModel.attachRequestFailed(function()
{
networkModel.setProperty("/connected", false)
})
})
createServiceModel: function(sServiceUrl, mParameters, oComponent)
{
return new Promise(function(resolve, reject)
{
try
{
var oServiceModel = new oData(sServiceUrl, null, oComponent);
oComponent.setModel(oServiceModel);
oServiceModel.setUseBatch(true);
oServiceModel.metadataLoaded()
.then(function()
{
resolve(oServiceModel);
});
} catch (err)
{
reject(err);
}
});
},
/**
*@description Load Data
*@memberOf View1
*/
onLoadTable: function()
{
this.getView()
.getModel("TableModel")
.load("/Table1Set")
.then(function(data)
{
console.log("BE and INDEXEDDB loaded")
})
.catch(function(err)
{
console.error("Either BE or INDEXEDDB could not be loaded");
})
},
/**
*@description Loads data from SPATH
* through extended odata Model from both backend and Indexed DB
*@param {string} sPath - Path to Data to be loaded
*/
load: function(sPath)
{
var oData = this.oComponent.getModel(),
that = this;
return new Promise(function(resolve, reject)
{
oData.read(sPath, {
success: function(response)
{
that.setProperty("/results", response.results);
resolve(response.results);
},
error: function(err)
{
reject(err);
}
});
});
},
/**
*@description extended READ
* on read (error and success) the local INDEXEDDB is also read with the supplied sPath from the original
* OData Call. Merges data from indexed to to "Results"
*@memberOf oData
*@param {String} sPath - A string containing the path to the data which should be retrieved.
* The path is concatenated to the service URL which was specified in the model constructor. Eg. '/CustomerSet'
*@param {Object} mParameters - Optional parameter map, see API
*/
read: function(sPath, mParameters)
{
var that = this;
mParameters.success = (function(success)
{
function fnExtend(data, oData)
{
that._readSuccessCallback(data, oData, this)
.then(function(localData)
{
data.results ? data.results = data.results.concat(localData.data) : localData.data ? data.results = localData.data : '';
success(data, oData);
});
}
return fnExtend.bind(sPath);
}.bind(sPath))(mParameters.success);
mParameters.error = (function(error)
{
function fnExtend(data)
{
that._readErrorCallback(data, this.path, this.params)
.then(function(localData)
{
if (localData.data) localData.params.success(data);
error(data);
});
}
return fnExtend.bind({
path: sPath,
params: mParameters
});
}.bind(sPath))(mParameters.error);
ODataModel.prototype.read.call(this, sPath, mParameters)
},
…
/**
*@description Extended READ Success function. Tries to read path from BE AND IndexedDB
*@memberOf oData
*@param {Object} data - Data resulting from call
*@param {Object} oData - Overhead Data
*@param {String} sPath - called Path
*/
_readSuccessCallback: function(data, oData, sPath)
{
console.log("Server READ SUCCESS: %s ", sPath);
return this._loadFromIndexedDB(sPath, {})
},
…
/**
*@description loads a Table from the indexed DB
*@param {String} sPath - called Path
*@param {Object} oParams - success / error
*@return {promise}
*/
_loadFromIndexedDB: function(sPath, oParams)
{
var that = this;
return new Promise(function(resolve)
{
that._oComponent.getIndexDb()
.getTable(sPath)
.then(function(data)
{
resolve({
data: data,
params: oParams
})
});
})
},
/**
*@description loads data from indexeddb.
* Adds attribute "_origin": "indexeddb" to indexeddb
*@memberOf IndexedDb
*@param {String} sTable - Name of table that is read
*@param {boolean} bIncludeRemove - in cludes entries with property _http === "remove"
*@returns {array} - read table with extra attribute "_origin": "indexeddb"
*/
getTable: function(sTable, bIncludeRemove)
{
var that = this;
return new Promise(function(resolve, reject)
{
var request = indexedDB.open("localStorage", version);
request.onsuccess = function(oEvent)
{
IndexedDb._db = oEvent.target.result;
if (!that._tableAvailable(sTable))
{
IndexedDb._db.close();
return resolve([]);
}
var objectStore = IndexedDb._db.transaction([sTable], "readwrite").objectStore(sTable);
var request = objectStore.openCursor();
var aTable = [];
request.onsuccess = function(event)
{
var cursor = event.target.result;
if (cursor)
{
cursor.value._origin = "indexeddb";
if (bIncludeRemove || cursor.value._http === 'create') aTable.push($.extend(cursor.value, cursor.value));
cursor.continue();
}
else
{
IndexedDb._db.close();
resolve(aTable);
}
};
};
});
},
oTableModel.save(data, "/Table1Set")
.then(function()
{
that.onLoadTable();
})
.catch(function()
{
that.onLoadTable();
})
;
/**
*@description Creates new table entry
*@param {Object} data - data to be saved
*@param {String} sPath - Path to be saved to
*@returns {promise}
*/
save: function(data, sPath)
{
var oData = this.oComponent.getModel();
return new Promise(function(resolve, reject)
{
oData.create(sPath,
data,
{
success: function(resp)
{
resolve(resp)
},
error: function(err)
{
reject(err);
}
})
})
},
/**
*@description extended CREATE
*@memberOf oData
*@param {String} sPath - A string containing the path to the collection
* where an entry should be created.
* The path is concatenated to the service URL which was specified in the
* model constructor.
*@param {Object} oData - Data of the entry that should be created.
*@param {Object} mParameters - Optional parameter map, refer to API
*/
create: function(sPath, oData, mParameters)
{
oData = this._trimPayload(oData);
var that = this;
mParameters.success = (function(success)
{
function fnExtend(data, oData)
{
success(data, oData);
that._createSuccessCallback(data, oData, this)
}
return fnExtend.bind({
path: sPath,
data: oData
});
}.bind({
path: sPath,
data: oData
}))(mParameters.success);
mParameters.error = (function(error)
{
function fnExtend(data)
{
that._createErrorCallback(data, this)
.then(function()
{
error();
});
}
return fnExtend.bind({
path: sPath,
data: oData
});
}.bind({
path: sPath,
data: oData
}))(mParameters.error);
ODataModel.prototype.create.call(this, sPath, oData, mParameters)
},
…
/**
*@description Extended CREATE ERROR function
* Stores data in local Index DB
*@memberOf oData
*@param {Object} data - Data resulting from call
*@param {Object} mParameters - 1: called sPath, 2:saved object
*/
_createErrorCallback: function(data, mParameters)
{
console.error("CREATE ERROR: %s \n Property %s not Set", data.message, sPath);
var sPath = mParameters.path;
var oUnsavedData = mParameters.data;
oUnsavedData._http = 'create';
return this._oComponent.getIndexDb()
.create(oUnsavedData, sPath);
},
/**
*@description stores data in indexeddb and creates Table if necessary
* adds property _http to save the intended http action (CRUD)
*@memberOf IndexedDb
*@param {object} data - data to be stored in indexed db
*@param {String} sTable - Name of table that data is added to
*/
create: function(data, sTable)
{
var that = this;
sTable = that._trim(sTable)[1];
return Promise.resolve()
.then(that._createTable.bind(that, sTable))
.then(that._createData.bind(that, data, sTable))
.catch(function(err)
{
console.error("Data could not be written to INDEXEDDB");
})
},
…
/**
*@description creates a new table
*@memberOf IndexedDb
*@param {String} sTable - Name of table that data is added to
*/
_createTable: function(sTable)
{
if (!this._tableAvailable(sTable)) version++;
var request = indexedDB.open("localStorage", version);
var indexedDbKey = 'Id';
return new Promise(function(resolve, reject)
{
request.onupgradeneeded = function(oEvent)
{
IndexedDb._db = oEvent.target.result;
var objectStore = IndexedDb._db.createObjectStore(sTable, {
autoIncrement: false,
keyPath: indexedDbKey
});
objectStore.createIndex('_http', '_http', {unique: false});
request.onsuccess = function(evt)
{
resolve();
};
request.onerror = function(oError)
{
IndexedDb._db.close();
reject();
};
};
request.onsuccess = function(oEvent)
{
IndexedDb._db = oEvent.target.result;
resolve();
};
})
},
…
/**
*@description create indexdb entry
*@memberOf IndexedDb
*@param {object} data - data to be stored in indexed db
*@param {String} sTable - Name of table that data is added to
*/
_createData: function(data, sTable)
{
return new Promise(function(resolve, reject)
{
var oTransaction = IndexedDb._db.transaction(sTable, "readwrite");
var oDataStore = oTransaction.objectStore(sTable);
oDataStore.add(data);
IndexedDb._db.close();
resolve();
})
},
/**
*@description Deletes entry
*@memberOf View1
*@param {object} oEvent
*/
deleteEntry: function(oEvent)
{
var that = this;
var sPath = oEvent.getSource()
.getBindingContext("TableModel").sPath;
var oObj = this.getView()
.getModel("TableModel")
.getProperty(sPath);
this.getView()
.getModel("TableModel")
.remove(oObj)
.then(function(data)
{
that.onLoadTable();
})
.catch(function(error)
{
that.onLoadTable();
})
},
/**
*@description removes table entry
*@returns {promise}
*/
remove: function(data)
{
var oData = this.oComponent.getModel();
var sPath = oData.createKey("/Table1Set", {
"Id": data.Id
}
);
return new Promise(function(resolve, reject)
{
oData.remove(sPath,
{
success: function(resp)
{
resolve(resp)
},
error: function(err)
{
reject(err);
}
},
)
})
/**
*@description extended REMOVE
*@memberOf oData
*@param {String} sPath - A string containing the path to the collection where an entry should be created.
* The path is concatenated to the service URL which was specified in the model constructor.
*@param {Object} mParameters - Optional parameter map, refer to API
*/
remove: function(sPath, mParameters)
{
var that = this;
mParameters.success = (function(success)
{
function fnExtend(data, oData)
{
success(data, oData);
that._removeSuccessCallback(data, oData, this);
}
return fnExtend.bind({path: sPath});
}.bind({path: sPath}))(mParameters.success);
mParameters.error = (function(error)
{
function fnExtend(data)
{
error(data);
that._removeErrorCallback(data, this.path);
}
return fnExtend.bind({path: sPath});
}.bind({path: sPath}))(mParameters.error);
ODataModel.prototype.remove.call(this, sPath, mParameters)
},
…
/**
*@description Extended REMOVE ERROR function.
* Stores items that are to be deleted in indexedDB
*@param {Object} data - Data resulting from call
*@param {String} sPath - called Path, including Key
*/
_removeErrorCallback: function(data, sPath)
{
return this._oComponent.getIndexDb()
.remove(sPath)
},
/**
*@description Checks if affected tableentry exists. triggers creation of table if needed.
* if data cannot be found in indexed db, the system asumes that the entry needs to be saved
* to be deleted later
*@memberOf IndexedDb
*@param {String} sPath - Name of Spath that is read (most of it is also the iDB table name)
*/
remove: function(sPath)
{
var that = this;
var sKey = this._getKeys(sPath);
var sShortPath = this._trim(sPath)[0];
return Promise.resolve()
.then(that._createTable.bind(that, sShortPath))
.then(that._removeData.bind(that, sShortPath, sKey, false))
.catch(function(err)
{
that._removeData(sShortPath, sPath, true)
.then(function()
{
console.log("deleteToken successfully written to IndexedDB")
})
.catch(function(err)
{
})
})
},
…
/**
*@description Removes entry from indexed DB
* if entry is not found, affected key and table are stored to be dealt with later
*@memberOf IndexedDb
*@param {String} sTable - Name of Spath that is read (most of it is also the iDB table name)
*@param {String} sKey - spath Key
*@param {Boolean} bDeleteToken - remove a delete token from INDEXED DB (_http: remove).
* if false, its assumed that a create entry is to be delete after it was successfully send to the backend
*/
_removeData: function(sTable, sKey, bDeleteToken)
{
var that = this;
var bIndexedEntryFound = false;
return new Promise(function(resolve, reject)
{
var request = indexedDB.open("localStorage", version);
request.onsuccess = function(oEvent)
{
IndexedDb._db = oEvent.target.result;
var objectStore = IndexedDb._db.transaction([sTable], "readwrite").objectStore(sTable);
var request = objectStore.openKeyCursor(sKey);
request.onsuccess = function(oEvent)
{
var cursor = oEvent.target.result;
if (cursor)
{
bIndexedEntryFound = true;
objectStore.delete(cursor.primaryKey);
cursor.continue();
}
else
{
if (!bIndexedEntryFound)
{
if (bDeleteToken)
{
that.create({ //...create indexeddb table with delete token, if it doesnt exist
'Id': sKey,
_http: 'remove'
}, sTable)
.then(function(data)
{
IndexedDb._db.close();
resolve();
})
}
else
{
reject();
}
}
else
{
resolve(); //entry found + deleted
}
}
};
}
})
},
/**
*@description Synchronize
*@memberOf View1
*/
processLocalData: function(eEvent)
{
var sPath = "/Table1Set";
var that = this;
this.getOwnerComponent()
.getIndexDb()
.processLocalData(sPath)
.then(function(data)
{
that.onLoadTable();
})
/**
*@description After the system is back online, this will synchronize local storage with Backend data
*@memberOf IndexedDb
*@param {String} sTable - Name of table that data is add
*@return {promise}
*/
processLocalData: function(sTable)
{
var that = this;
return new Promise(function(resolve, reject)
{
that.getTable(sTable, true)
.then(function(data)
{
data.forEach(function(line)
{
var sKey = line.Id;
if (line._http === 'create')
{
that._oComponent.getModel()[line._http](sTable, line, {
success: function()
{
that._removeData(sTable, sKey, false)
.then(function()
{
resolve()
});
},
error: reject
})
}
else if (line._http === 'remove')
{
that._oComponent.getModel()[line._http](line.Id, {
success: function()
{
that._removeData(sTable, sKey)
.then(function()
{
resolve()
});
},
error: reject
})
}
});
});
})
},
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
8 | |
5 | |
5 | |
4 | |
4 | |
4 | |
4 | |
4 | |
3 | |
3 |