Entw.: Komplexe Picker

Komplexe Picker

Komplexere Picker lassen sich mit dem Framework Sencha Touch recht einfach realisieren. Oft besteht die Notwendigkeit zusammengsetzte Schlüssel zu verarbeiten. Das folgende Beispiel zeigt die Arbeitsweise eines Pickers, mit dem sich ein zusammengsetzter Schlüssel einem Eingabefeld zugeordnen lässt. Der zusammengsetzte Schlüssel besteht aus 3 Einzelschlüssel. Die Abhängigkeiten der Einzelschlüssel untereinander sind hierarchisch.

Der hier vorgestellte Picker wird im Eisenbahnwagondispositionssystem HERMES verwendet. Die Gleise in einem Werk besitzen eine 4-stellige Kennzeichnung. Diese Kennzeichnung besteht aus folgenden 3 Einzelschlüssel:
- Hauptbezirk (HBz - 1. Stelle)
- Unterbezirk (UBz - 2. Stelle)
- Gleisnummer (Nr - 3. + 4. Stelle)
In einem Hauptbezirk befinden sich Unterbezirke. In den Unterbezirken befinden dann die Gleise.

Randbedingungen

Der RESTServer liefert die Gleisdaten als Tabelle. Die Tabelle ist nach den Schlüsseln aufsteigend sortiert.

Picker-Variante 1Picker

Auswählbar sind alle Gleise.



Picker-Variante 2Picker

Hier können nur die Standgleise ausgewählt werden.


Definition des Modells und des Store's

Folgender Code zeigt das zugrunde gelegte Datenmodell und die Datenspeicherung in einem sogenannten Store.

Ext.regModel("Gleise", {
    fields : [
        { name : "hbz",  type : "string" },
        { name : "ubz",  type : "string" },
        { name : "nr",   type : "string" },
        { name : "value",type : "string" }
    ]
});
var storeGleise = new Ext.data.Store({
    model : "Gleise",
    data  : [
        { hbz : "*" ,  ubz : "*",  nr : "*  - Alle Gleise",  value: "0000n" },
        { hbz : "1" ,  ubz : "*",  nr : "*  - Alle Gleise des Hauptbezirks 1",  value: "1000n" },
        { hbz : "1" ,  ubz : "1",  nr : "*  - Alle Gleise des Bezirks 11",  value: "1100n" },
        { hbz : "1" ,  ubz : "1",  nr : "01 - Fahrgleis 1101",  value: "1101n" },
        { hbz : "1" ,  ubz : "1",  nr : "02 - Standgleis 1102",  value: "1102j" },
        { hbz : "1" ,  ubz : "1",  nr : "03 - Fahrgleis 1103",  value: "1103n" },
        { hbz : "1" ,  ubz : "1",  nr : "04 - Standgleis 1104",  value: "1104j" },
        { hbz : "1" ,  ubz : "1",  nr : "05 - Standgleis 1105",  value: "1105j" },
        { hbz : "1" ,  ubz : "1",  nr : "06 - Standgleis 1106",  value: "1106j" },
        { hbz : "1" ,  ubz : "1",  nr : "07 - Standgleis 1107",  value: "1107j" },
        { hbz : "1" ,  ubz : "2",  nr : "*  - Alle Gleise des Bezirks 12",  value: "1200n" },
        { hbz : "1" ,  ubz : "2",  nr : "01 - Standgleis 1201",  value: "1201j" }
    ]
});

Anlegen der Gleis-Picker in JavaScript

In einem Eingabeformular werden i.d.R. die einzelnen Eingabefelder deskriptiv definiert. Die einzelnen Klassenobjekte werden also nicht durch dem Anwenderprogrammierer explizit angelegt. Dies erledigt das Framework Sencha Touch für den Programmierer.

items : [
    {
        xtype        : "gleispickerfield",
        label        : "Gleis",
        store        : storeGleise,
        name         : "gleis",
        itemWidth    : 400
    },
    {
        xtype        : "gleispickerfield",
        label        : "Reines Standgleis",
        store        : storeGleise,
        name         : "standgleis",
        nurStandgleis: true,
        itemWidth    : 400
    }
]

Die Implementierung in JavaScript

Folgender Code zeigt im groben die Implementierung der beiden Klassen GleisPicker und GleisPickerField. Zu beachten ist die verwendete Filtertechnik. Filter auf Store's sind Bestandteile des Frameworks Sencha Touch. Die Abhängigkeitsfunktionalität zwischen den Einzelschlüssel wurde nur über die Filtertechnik realisiert.

Ext.hadv.GleisPicker = Ext.extend(Ext.Picker, {
  slotNames: [ 'hbz', 'ubz', 'nr'  ],
  titles   : [ 'HBz', 'UBz', 'GlNr' ],
  hbz: [],
  ubz: [],
  nurStandgleis: false,
  constructor: function(config) {
  },
  initComponent: function() {
  },
  afterRender: function() {
  },
  createSlot: function(slotName,lstHBz,lstUBz) {
  },
 
  setFilter: function(art,valHBz,valUBz) {
    this.store.clearFilter();
    this.store.filterHBz = valHBz;
    this.store.filterUBz = valUBz;
    var tmpStKz = 'n';
    if (this.nurStandgleis)
      tmpStKz = 'j';
    this.store.filterStKz= tmpStKz;
    switch (art) {
      case 1:
        this.store.filter({
          fn: function(r) {
            var tmpVal = r.get('value');
            if (r.store.filterStKz == 'j') {
              return (tmpVal[0] == r.store.filterHBz) &&
                     (tmpVal[1] == r.store.filterUBz) &&
                     (tmpVal[4] == r.store.filterStKz);
            }
            else {
              return (tmpVal[0] == r.store.filterHBz) &&
                     (tmpVal[1] == r.store.filterUBz);
            }
          }
        });
        break;
        case 2:
         this.store.filter({
           fn: function(r) {
             var tmpVal = r.get('value');
              return (tmpVal[0] == r.store.filterHBz) && 
               (tmpVal[1] == '0');
           }
        });
	    break;
	  case 3:
        this.store.filter({
           fn: function(r) {
	  	      var tmpVal = r.get('value');
	          return (tmpVal[0] == '0') && 
			         (tmpVal[1] == '0') && 
			         (tmpVal[2] == '0') && 
				     (tmpVal[3] == '0');
           }
        });
	    break;
	}
  },
 
  onSlotPick: function(slot, value) {
    var tmpName = slot.name,
        slotHBz, slotUBz, slotNr;
    if (tmpName === "hbz" || tmpName === "ubz") {
      slotHBz = this.child('[name=hbz]');
      slotUBz = this.child('[name=ubz]');
      slotNr  = this.child('[name=nr]');
	  if ((slotHBz.selectedIndex > 0) && (slotUBz.selectedIndex > 0)) {
	    var tmpObjHBz = slotHBz.store.data.items[slotHBz.selectedIndex];
	    var tmpObjUBz = slotUBz.store.data.items[slotUBz.selectedIndex];
		var tmpHBz = tmpObjHBz.data['text'];
		var tmpUBz = tmpObjUBz.data['text'];
		this.setFilter(1,tmpHBz,tmpUBz);
 	  }
	  else if ((slotHBz.selectedIndex > 0) && (slotUBz.selectedIndex == 0)) {
	    var tmpObjHBz = slotHBz.store.data.items[slotHBz.selectedIndex];
		var tmpHBz = tmpObjHBz.data['text'];
		slotNr.store.filterHBz = tmpHBz;
		this.setFilter(2,tmpHBz,'');
	  }
	  else {
		this.setFilter(3,'','');
	  }
      slotNr.scroller.updateBoundary(true);
    }
    Ext.hadv.GleisPicker.superclass.onSlotPick.apply(this, arguments);
  },
  getValue: function() {
  },
  setValue: function(value, animated) {
  }
});
Ext.reg('gleispicker', Ext.hadv.GleisPicker);
 
Ext.hadv.GleisPickerField = Ext.extend(Ext.form.Field, {
  ui: 'select',
  saveConfig: {}, 
  picker: null,
  destroyPickerOnHide: false,
  constructor: function(config) {
  },
  initComponent: function() {
  },
  getPicker: function() {
  },
  onMaskTap: function() {
  },
  onPickerChange : function(picker, value) {
  },
  onPickerHide: function() {
  },
  setValue: function(value, animated) {
  },
  getValue: function(format) {
  },
  onDestroy: function() {
  }
});
Ext.reg('gleispickerfield', Ext.hadv.GleisPickerField);