StateManagement, ExtJS, Widgets, Gemstone/S und PUM

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

This entry was posted in Smalltalk and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.