Arbeiten mit Daten in der Konsole¶
Manager¶
Die Verwaltung der aktuellen Daten kann über den Manager Alvine.Package.Console.Host.dataManager
erfolgen.
Nachdem ein Connector angelegt wurde, kann ein Arbeitskontext WorkingContext
mit der Methode
Manager.init(connector, key, path)
erstellt werden.
Der connector
definiert die Verbindung zur API.
Der Parameter key
dient zum Zugriff auf die ausgewählten Daten.
Das path
definiert den Ausschnitt, der aktuell Verfügbar sein soll. Dies ist wichtig, wenn zum Beispiel
eine komplette Sammlung mit vielen Datensätzen geladen wird, aber nur ein Datensatz aktuell
bearbeitet werden soll.
Das Ergebnis von Manager.init(connectorOrUrl, key, path)
ist ein Objekt vom Typ WorkingContext
.
Das WorkingContext
-Objekt kann jederzeit über den Schlüssel key
mit der Methode Manager.getWorkingContext(key)
abgerufen werden.
Mit Manager.removeWorkingContext(key)
wird ein WorkingContext
-Objekt aus dem Manager entfernt. Dabei wird das
WorkingContext zurückgesetzt (alle Daten entfernt). Zugriffe auf Referenzen eines entfernten Contextes erzeugen
eine Exception.
var url = "/api/account/whoami?facet=personal,addresses,relationship";
var connector = new Alvine.Package.Console.Platform.Storage.Connector(url);
var datakey = 'myDataKey';
var promise = Alvine.Package.Console.Host.dataManager.init(connectorOrUrl, datakey);
promise.then(workingcontext => {
// code
});
Mit der Methode Manager.post(connector)
werden die aktuellen im Connector gespeicherten
Daten an die API übertragen.
Hinweis
Die Daten aus der Registry und des WorkingContext werden nicht übertragen. Diese müssen
vorher mit WorkingContext.commit()
eingereicht werden.
Nach dem Senden können die Daten des Connector mit der Methode Manager.reset(connector, full)
zurückgesetzt werden.
Es wird die Historie gelöscht und falls full
gleich true
ist, wird auch das gespeicherte Datum gelöscht.
Hinweis
Der aktuellen Daten und die Historie des WorkingContext sind davon nicht betroffen.
Mit der Methode Manager.getOpenHistories(filter)
lassen sich alle offenen Änderungen abrufen. Über den Parameter filter
lassen sich die gewünschten Ergebnismengen holen.
Manager.getOpenHistories({
path: 'example.path.to.dataset',
context: '/example/context',
key: 'mykey'
});
Die Filter path
und context
werden auf gleichheit geprüft, beim Schlüsselfilter key
wird auf den Anfang der Zeichenkette geprüft.
Mit dem Filter key
würden im obigen Beispiel einträge mit Schlüsseln mykey1
, mykey2
, mykey
usw zurückgegeben. Hingegen würde
ein Eintrag mit dem Schlüssel _mykey
nicht zurückgegeben.
Connector¶
Ein Connector legt die Verbindung zwischen der Server-API und der Konsole fest. Im folgenden Beispiel
wird ein Connector auf die whoami
-API definiert. Werden Daten über den Connector von der API abgerufen, so
speichert der Connector diese im localStorage
mit dem Prefix cc
.
Ein Connector verfügt über eine Historie der Änderungen. Bei einer Änderung der Daten im Browser
wird eine Historie mit dem Prefix cch
im localStorage
angelegt.
url = "/api/account/whoami?facet=personal,addresses";
connector = new Alvine.Package.Console.Platform.Storage.Connector(url);
Das Connector-Objekt implementiert die Observer-Schnittstelle mit Connector.attachObserver(observer)
,
Connector.containsObserver(observer)
, Connector.detachObserver(observer, recursive)
, Connector.detachObserver(recursive)
,
Connector.preventNotification(recursive)
, Connector.promoteNotification()
und Connector.notifyObserver()
.
Ein Connector besitzt eine eindeutige ID, die über die Methode Connector.getID()
geholt werden
kann. Diese eindeutige ID wird aus der URL erzeugt und ist für jede URL eindeutige. Zwei Connectoren
mit der gleichen URL haben die gleiche ID und verwenden den gleichen Speicherplatz im localStorage
.
Dadurch kann auch beim Neuladen der Seite auf die Daten des Connectors zugegriffen werden.
Falls die Daten in einer anderen Form, als vom Server geliefert, gebraucht werden, so kann man die Daten mit jeweils einem Lese- und einem Schreib-Callback bearbeiten.
Die mit Connector.setReadDataCallback(callback)
gesetzte Funktion callback
wird nach dem Empfangen der
Daten aufgerufen und muss das gewünschte Dataset zurückgeben. Vor dem Senden an den Server wird die mit
Connector.setWriteCallback(callback)
gesetzte Funktion aufgerufen.
Connector.setReadDataCallback(dataset => {
// do something
return dataset;
});
Connector.setWriteDataCallback((dataset, history) => {
// do something
return dataset;
});
Über die Methode Connector.setWriteCallback(callback)
kann das Senden der Daten selber implementiert werden.
Werden die Daten über Manager.post(connector)
abgeschickt und ein Write-Callback wurde definiert, so wird der
Funktion der aktuelle Datensatz, die URL und die Historie übergeben.
Als Rückgabewert sollte ein Promise zurückgegeben werden. Das Promise ist dann das Ergebnis von Manager.post(connector)
.
Connector.setWriteCallback((dataset, url history) => {
// do something
// Wichtig: Als Array zurückgeben (erster Eintrag ist das Dataset, zweiter die URL)
return new Promise;
});
Historie¶
Die Historie ist eine Collection
mit den Diffs der Änderungen.
Jeder Eintrag entspricht einer Änderung.
kind - indicates the kind of change; will be one of the following:
N - indicates a newly added property/element
D - indicates a property/element was deleted
E - indicates a property/element was edited
A - indicates a change occurred within an array
path - the property path (from the left-hand-side root)
lhs - the value on the left-hand-side of the comparison (undefined if kind === 'N')
rhs - the value on the right-hand-side of the comparison (undefined if kind === 'D')
index - when kind === 'A', indicates the array index where the change occurred
item - when kind === 'A', contains a nested change record indicating the change that occurred at the array index
Die Methode History.getKey()
gibt den Schlüssel, History.getUrl()
die Url der dazugehörigen Connection,
History.getPath()
den Pfad zum Datensatz und History.getContext()
den Context zurück.
WorkingContext¶
Ein WorkingContext
wird vom Manager
über Manager.init(connectorOrUrl, key, path)
erstellt und kapselt den Zugriff
auf die aktuellen Arbeitsdaten. Das Ergebnis von Manager.init()
ist ein Promise mit dem WorkingContext als Wert.
Alle initialisierten Kontexte lassen sich über die Methode Manager.getWorkingContexts()
holen. Das Ergebnis
ist eine Alvine.Types.Map
mit den WorkingContext
Objekten.
var workingcontexts=Alvine.Package.Console.Host.dataManager.getWorkingContexts();
Ein einzelner Kontext lässt sich über den key
und die Methode Manager.getWorkingContext(key)
holen.
var datakey = 'myDataKey';
var workingcontext=Alvine.Package.Console.Host.dataManager.getWorkingContext(datakey);
Mit der Methode WorkingContext.activate(registryKey)
wird eine Arbeitskopie der Daten in
der Alvine.Registry
mit dem Schlüssel registryKey
angelegt.
var registryKey='formdata';
workingcontext.activate(registryKey);
// Überprüfen der aktivierung
Alvine.Registry.get(registryKey);
Änderungen können mit der Methode WorkingContext.revert(registryKey)
rückgängig gemacht werden. Wird ein registryKey
übergeben, so wird neben der internen Datenstruktur das Ergbenis auch in die Registry rückgängig gemacht.
Fertige Änderungen können schließlich mit WorkingContext.commit()
an den Connector
übergeben werden.
Die aktuelle Historie lässt sich mit WorkingContext.getHistory()
auslesen.
Mit der Methode WorkingContext.applyDiff(history, revert)
kann eine Historie auf einen WorkingContext angewendet werden.
Wird für den Parameter revert
der Wert true
übergeben, so wird linke und rechte Seite in der Historie Seite getauscht.
Beispiel¶
Das folgende Beispiel lädt die erste Seite der Produktdaten mit drei Einträgen. Die Daten werden dann in
die Registry in den Schlüssel liste
übertragen
var page = 1;
var url = "/api/commerce/item/search?q=iid>0&fields=iid,name,masterNumber&count=10&page="+page;
var registryListKey = 'list';
var connector = new Alvine.Package.Console.Platform.Storage.Connector(url);
Alvine.Package.Console.Host.dataManager.init(connector, 'items', 'dataset').then(workingContext => {
workingContext.activate(registryListKey);
console.log('workingContext', workingContext);
}).catch(e => console.log('error', e));
Als nächstes soll der Zugriff auf einen Datensatz erfolgen. Unter der Annahme das ein Produkt mit der Produktnummer (iid)
500
enthalten ist, erfolgt die initialisierung dieser Daten mittels folgendem Code.
var iid = 500;
var registryFormKey = 'formdata';
Alvine.Package.Console.Host.dataManager.init(connector, 'item-'+iid, 'dataset.'+iid).then(workingContext => {
workingContext.activate(registryFormKey);
}).catch(e => console.log('error', e));
Jetzt befinden sich die Daten des Produktes iid=500
in der Registry mit dem Schlüssel formdata
.
Änderungen an den Daten erfolgen über direkte Manipulation der Daten in der Registry. In der folgenden
Anweisung wird das Feld name
auf den wert test
gesetzt.
Alvine.Registry.get('formdata').set('name','test');
Dies führt dazu, dass im localStorage
-Objekt ein Historie-Objekt angelegt wird. Wird die Seite neu geladen
und die Registry über den obigen Code initialisiert, so hat der Schlüssel name
den Wert test
.
Zu diesem Zeitpunkt ist der Wert auf dem Server noch immer eine leere Zeichenkette.
Soll eine Auswahl der geänderten Daten - zum Beispiel in einem Dropdown - angezeigt werden, so kann
auf die Daten mittels der Methode Manager.getOpenHistories(filter)
zugegriffen werden. Im folgende Code
werden die Änderungen geholt und die Registry auf die Werte des ersten Eintrags gesetzt.
// Änderungen im selben Kontext auslesen
var opens = Alvine.Package.Console.Host.dataManager.getOpenHistories({context: Alvine.Package.Console.Host.getLocation().getContext()});
// opens ist vom Type Alvine.Types.Collection
// Alle Schlüssel der Änderungen ausgeben
opens.each(history => {
console.log(history.getKey());
});
// Einen Eintrag auswählen und Daten holen
var index = 0;
var selected = opens.getIndex(index);
Alvine.Package.Console.Host.dataManager.init(connector, selected.getKey(), selected.getPath()).then(workingContext => {
console.log('workingContext', workingContext);
workingContext.activate('formdata');
}).catch(e => console.log('error', e));
Wir können die Änderungen jetzt mittels workingContext.revert()
zurücknehmen. In diesem Fall ändert sich
die Registry nicht automatisch. Hierzu ist ein gesonderter Befehl workingContext.activate(registryFormKey)
notwendig.
Hinweis
Andere WorkingContext-Objekte die auf die gleichen Daten zugreifen, haben zu diesem Zeitpunkt noch die Originaldaten und nicht die hier gemachten Änderungen.
Um die Änderungen in die Hauptdaten zu integrieren wird die Methode workingContext.commit()
aufgerufen.
Ab diesem Zeitpunkt haben alle WorkingContext-Objekte auf diese Daten zugriff.
Zu diesem Zeitpunkt ist der Wert auf dem Server noch immer eine leere Zeichenkette.
Jetzt erfolgt die Übertragung an den Server mittels Manager.post(connector)
.
workingContext.commit().then(() => {
connector.setWriteCallback((currentData, url, history) => {
var set;
set = new Set;
history.each(h => {
h.forEach(d => {
var iid, path;
path = d.path;
// Path ist in diesem Fall data.dataset.data.<iid>.data.....
if(!path||!path[3]) {
return;
}
iid = path[3];
// Alle zu speichernden IID sammeln
if(iid>0) {
set.add(iid);
}
});
});
var items = currentData.get('dataset');
promises = [];
set.forEach(iid => {
var item = items.get(iid);
// Hier die geänderten Daten dann an den Server senden
promises.push(
fetch("/api", {
method: "POST",
mode: "cors",
//...
}));
});
return Promise.all(promises);
});
Alvine.Package.Console.Host.dataManager.post(connector)
.then(result => {
console.log(result);
// Historie löschen
//Alvine.Package.Console.Host.dataManager.reset(connector);
})
.catch(e=>console.log(e));
}).catch(error => {
});
UML-Diagramm¶
Im folgenden ist der Ablauf und das Zusammenspiel der einzelnen Objekte als Sequenzdiagramm abgebildet.