Sencha ExtJS benutzt zur Speicherung der State-Information der UI-Komponenten die Instanz Ext.state.Manager. Der bekommt eine Instanz zugewiesen, die drei verschiedene Operationen implementieren muß.
Hier eine Beispielimplementation des StateProvider. Die Daten werden in Objekten – verwaltet durch ein Store – mit den Attributen key und value gespeichert:
/**
* Created by mf on 16.12.22.
*/
Ext.define('Ctm.RemoteStateProvider', {
extend: 'Ext.state.Provider',
// Der Store muss auf autoSync = false und autoLoad = false definiert sein
config: {
store: null
},
constructor: function (config) {
var me = this;
Ext.apply(me, config);
me.state = {};
me.mixins.observable.constructor.call(me);
},
set: function (name, value) {
let me = this;
let pos, row;
if ((pos = me.store.find('key', name)) > -1) {
row = me.store.getAt(pos);
row.set('value', me.encodeValue(value));
}
else {
me.store.add({
key: name,
value: me.encodeValue(value)
});
}
if(me.store.isSyncing) return;
me.store.sync();
me.fireEvent('statechange', me, name, value);
},
get: function(name, defaultValue) {
let me = this;
let pos, row;
if ((pos = me.store.find('key', name)) > -1) {
row = me.store.getAt(pos);
return me.decodeValue(row.get('value'));
}
else {
return defaultValue;
}
},
clear: function(name) {
let me = this;
let pos;
if ((pos = this.store.find('name', name)) > -1) {
me.store.removeAt(pos);
me.store.sync();
me.fireEvent('statechange', this, name, null);
}
}
});
Dazu gehört ein Store, der die Daten in der Gemstone/S speichern soll. Er muß alle 4 CRUD Operationen implementieren. In PUM kann man den Store definieren auf ein DomainObjekt mit den Attributen key und value, die beide eine Zeichenkette speichern sollten. Eine gute Position ist eine 1:n Assoziation von einer Klasse, die den ApplicationUser darstellt.
Diesen Store sollte man in der JS-Anwendung früh definieren und ein load() ausführen. Eine statische Definition in einem ViewModel im ersten Fenster ist zu spät. Der Store muß nach einem erfolgreichem Login erzeugt werden, damit eine korrekte URL durch das Framework erzeugt wird.
uiStateStore = new ctm.store.CTMAPIUIStateValueList(); uiStateStore.load( function(records, operation, success) { Ext.state.Manager.setProvider(Ext.create('Ctm.RemoteStateProvider',{ store: uiStateStore })); // Nun geht es mit der Anwendung los ... me.redirectTo("....") });
Um z.B. ein GRID mit der state-Verwaltung zu beauftragen, kann man ein GRID wie folgt definieren (Attribute stateful und stateId definieren):
{
xtype: 'gridpanel',
stateful: true,
stateId: 'sessiongrid'
}
Auf der Gemstone-Seite muß man einen Store definieren, eine Domain- und eine API-Klasse und die notwendigen Calls für alle Aktionen. Es folgen Parameter und Rückgabe-Hilfsklassen. Hier eine Beispielimplementierung der vier Methoden unter Gemstone/S:
Read:
apiUiCTMAPIUIStateValueListRead: aCTMAPIPRUIStateValueListStore options: aMSKRestCallOptions | aCTMAppUser | aCTMAppUser := aMSKRestCallOptions session getAppUser. ^(self restAnswerClass okRequestFor: aMSKRestCallOptions restCallInstance result: (CTMAPIUIStateValueListStoreRL withDomainList: aCTMAppUser getUiStateValues apiClass: CTMAPIUIStateValue total: aCTMAppUser getUiStateValues )) successWithoutCommitCall; yourself
Create:
apiUiCTMAPIUIStateValueListCreate: aCTMAPIUIStateValueListStorePL
options: aMSKRestCallOptions
| list aCTMAppUser |
aCTMAppUser := aMSKRestCallOptions session getAppUser.
list := aCTMAPIUIStateValueListStorePL getItems collect: [
:eachCTMAPIUIStateValue |
| newCTMUIStateValue newCTMAPIUIStateValue tmpString |
newCTMUIStateValue := CTMUIStateValue newInitialized.
(tmpString := eachCTMAPIUIStateValue copyTo: newCTMUIStateValue) isNil
ifFalse:[
CTMEnumErrorDefinitionLocaleErrorDefinition
errCodeGeneralErrorThrowSignal: aMSKRestCallOptions
with1Args:(Array with: tmpString asString) ] .
aCTMAppUser addUiStateValues: newCTMUIStateValue.
(CTMAPIUIStateValue copyFrom: newCTMUIStateValue)
setSyscid: eachCTMAPIUIStateValue getSyscid;
yourself
].
^(self restAnswerClass
okRequestFor: aMSKRestCallOptions restCallInstance
result: (CTMAPIUIStateValueListStoreRL withAPIList: list total: list))
successWithCommitCall;
yourself
Update:
apiUiCTMAPIUIStateValueListUpdate: aCTMAPIUIStateValueListStorePL
options: aMSKRestCallOptions
| aCTMAppUser list |
aCTMAppUser := aMSKRestCallOptions session getAppUser.
list := aCTMAPIUIStateValueListStorePL getItems collect: [
:eachCTMAPIUIStateValue |
| aCTMUIStateValue tmpString |
(aCTMUIStateValue := aCTMAppUser getUiStateValues
detect: [ :each | each getGop = eachCTMAPIUIStateValue getGop]
ifNone:[ nil ]) isNil
ifTrue:[
CTMEnumErrorDefinitionLocaleErrorDefinition
errCodeGeneralErrorThrowSignal: aMSKRestCallOptions
with1Args: (Array with: 'UIState Item not found') ].
(tmpString := eachCTMAPIUIStateValue copyTo: aCTMUIStateValue) isNil
ifFalse:[
CTMEnumErrorDefinitionLocaleErrorDefinition
errCodeGeneralErrorThrowSignal: aMSKRestCallOptions
with1Args: (Array with: 'UIState not copyable') ] .
aCTMUIStateValue
].
^(self restAnswerClass
okRequestFor: aMSKRestCallOptions restCallInstance
result: (
CTMAPIUIStateValueListStoreRL
withDomainList: list
apiClass: CTMAPIUIStateValue
total: list))
successWithCommitCall;
yourself
Delete:
apiUiCTMAPIUIStateValueListDestroy: aCTMAPIUIStateValueListStorePL options: aMSKRestCallOptions | aCTMAppUser | aCTMAppUser := aMSKRestCallOptions session getAppUser. aCTMAPIUIStateValueListStorePL getItems do: [ :eachCTMAPIUIStateValue | | aCTMUIStateValue | (aCTMUIStateValue := aCTMAppUser getUiStateValues detect: [ :each | each getGop = eachCTMAPIUIStateValue getGop] ifNone:[ nil ]) isNil ifTrue:[ CTMEnumErrorDefinitionLocaleErrorDefinition errCodeGeneralErrorThrowSignal: aMSKRestCallOptions with1Args: (Array with: 'UIState Item not found') ] ifFalse:[ aCTMAppUser removeUiStateValues: aCTMUIStateValue ]. ]. ^(self restAnswerClass okRequestFor: aMSKRestCallOptions restCallInstance result: (CTMAPIGeneralResult newInitialized setSuccess: true ; yourself)) successWithCommitCall; yourself