Utilizzo del widget SpinWheelTimePicker in un custom control XPages

xpages mobile custom control spinwhelltimepicker

  • 0 commenti
SCENARIO: vogliamo visualizzare una pagina in cui siano presenti N widget SpinWheelTimePicker. N è sconosciuto a priori ed è determinato da N documenti domino in cui dovrà essere scritto l’orario selezionato sul corrispondente widget. La scrittura dovrà avvenire senza l’utilizzo di bottoni al termine della rotazione dello spinwheel. All’apertura della pagina i widget dovranno riportare l’orario eventualmente già memorizzato nel campo domino o, in mancanza di esso, l’orario corrente.
Nell’immagine sottostante riporto un esempio con 3 documenti e 3 SpinWheelTimePicker (il campo accanto agli SpinWheel serve solo a verificare la corretta selezione, può essere nascosto o eliminato).
Image:Utilizzo del widget SpinWheelTimePicker in un custom control XPages

SOLUZIONE PROPOSTA

Nella xpage (o custom control) da visualizzare abbiamo un repeater che effettua un ciclo sui documenti che contengono le informazioni da visualizzare. Al suo interno inseriamo un custom control (parametrizzato) che si occuperà della creazione e gestione dello spinwheel per ogni elemento processato dal repeater

<xp:repeat id="repeatH"
       rows="30" var="collCH" indexVar="indiceH">
<xp:this.value>codice di selezione dei documenti da processare</xp:this.value>
              <xp:panel>
                      <xp:this.data>
                              <xp:dominoDocument
                                      var="docCUSTOMH" formName="FESEMPIO"
                                      ignoreRequestParams="true"
                                       documentId="#{javascript:collCH}">
</xp:dominoDocument>
</xp:this.data>
                      <!—- altro codice -->
                      <xc:ccTimePickerCustom
                              docNotes="#{docCUSTOMH}" indice="#{indiceH}">
                       </xc:ccTimePickerCustom>
      <!—- altro codice -->
              </xp:panel>
      </xp:repeat>

I due parametri per il custom control sono:
1) il documento domino processato dal repeater in quel ciclo
2) l’indice del ciclo che il repeater sta eseguendo

Vediamo come costruire il ccTimePickerCustom.
1) alla sua creazione dobbiamo attribuire le 2 proprietà che riceveranno i parametri in ingresso.
Quella per il documento notes
Image:Utilizzo del widget SpinWheelTimePicker in un custom control XPages



e quella per l’indice
Image:Utilizzo del widget SpinWheelTimePicker in un custom control XPages


2) Creiamo la struttura del custom control dove inseriremo i vari elementi. E’ una semplice tabella.

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
      xmlns:xe="http://www.ibm.com/xsp/coreex">
     
             
              <xp:table>
              <xp:tr>
                      <xp:td>
                              <!-- SPAZIO PER IL WIDGET -->
                      </xp:td>
                      <xp:td>
                              <!-- SPAZIO PER I CAMPI DA VISUALIZZARE E DI SERVIZIO-->
                      </xp:td>
              </xp:tr>
      </xp:table>
</xp:view>

3) Analizzando la documentazione Dojo relativa al widget (http://dojotoolkit.org/reference-guide/1.8/dojox/mobile/SpinWheelTimePicker.html) vediamo che esso può essere inserito in modo dichiarativo o programmatico.
Il metodo dichiarativo

è estremamente immediato ma, proseguendo nello sviluppo, si incorre in un problema: dovendo far sì che il widget mostri subito il valore eventualmente salvato nel documento domino a cui si riferisce, all’apertura della pagina è necessario eseguire le operazioni JS che permetterebbero questa operazione ma, essendo il widget Dojo, esso viene caricato DOPO l’esecuzione del JS che dovrebbe impostarne il valore che, di conseguenza, genera un errore perché il widget è “null” all’atto dell’esecuzione.
Dopo alcune prove per cercare di eseguire il codice dopo il caricamento del widget, ho optato per la soluzione programmatica che consente un maggior controllo su quello che accade in pagina.
Procediamo quindi a posizionare nel punto in cui si vorrà visualizzare il widget un
che farà da placeholder per il widget.

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
      xmlns:xe="http://www.ibm.com/xsp/coreex">
              <xp:table>
              <xp:tr>
                      <xp:td>
                             
<xp:div id="spinDiv1"></xp:div> <!-- PLACEHOLDER -->
                      </xp:td>
                      <xp:td>
                              <!-- SPAZIO PER I CAMPI DA VISUALIZZARE E DI SERVIZIO-->
                      </xp:td>
              </xp:tr>
      </xp:table>
</xp:view>

e inseriamo il campo che conterrà il valore per inizializzare il widget e per salvare il valore modificato

<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
      xmlns:xe="http://www.ibm.com/xsp/coreex">
     
             
              <xp:table>
              <xp:tr>
                      <xp:td>
                             
<xp:div id="spinDiv1"></xp:div> <!-- PLACEHOLDER -->
                      </xp:td>
                      <xp:td>
                              <xp:inputHidden id="oraSelezionata"
                                      value="#{compositeData.docNotes.VALORE}">
                                      <xp:eventHandler event="onclick" submit="true"
                                              refreshMode="norefresh" save="true">
                                      </xp:eventHandler>
                              </xp:inputHidden>

                              <xp:inputText id="oraVisibile"
                                      style="width:60px; text-align:center;"
                                      value="#{compositeData.docNotes.VALORE}"
                                      disabled="true">
                              </xp:inputText>
                      </xp:td>
              </xp:tr>
      </xp:table>
</xp:view>

Ho utilizzato un campo Hidden e un campo visibile disabilitato perché i campi di tipo input Text disabilitati o readOnly, non possono “scrivere” attraverso il Data Source nel campo domino a cui sono legati.
Quindi, il campo Hidden consente il collegamento RW, attraverso il Data Source, con il documento domino; il campo input Text disabilitato consente di visualizzare il valore che verrà selezionato sullo SpinWheelTimePicker senza che l’utente possa modificarlo.
Questo campo ha avuto principalmente la sua utilità in fase di test e debug e può essere eliminato se non si desidera visualizzare ciò che l’utente ha selezionato sul widget.

Adesso, in fondo al codice del nostro custom control, possiamo inserire la dichiarazione e l’avvio del widget e il codice necessario alla gestione dei campi (i commenti spiegano le varie operazioni):

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
      xmlns:xe="http://www.ibm.com/xsp/coreex">
              <xp:table>
              <xp:tr>
                      <xp:td>
                              <xp:div id="spinDiv1"></xp:div>
                      </xp:td>
                      <xp:td>
                      </xp:td>
              </xp:tr>
      </xp:table>
     
      <xp:scriptBlock>
              <xp:this.value>

              require([
                  "dojo/_base/window",
                  "dojo/ready",
                  "dojo/registry",
                  "dojox/mobile/SpinWheelTimePicker",
                  "dojox/mobile/parser",
                  ],
function(win, ready, registry, SpinWheelTimePicker){
ready(function(){

//individuo i placeholders per il widget
var spinDiv = dojo.query("[id*='spinDiv1']");

      //individuo quello corrente sulla base dell'indice del repeater
var spinDiv = spinDiv[#{compositeData.indice}];

//individuo i campi dove leggo il valore salvato in Domino
var campoOra = dojo.query("[id*='oraSelezionata']");

//individuo quello corrente sulla base dell'indice del repeater
var campoOra = campoOra[#{compositeData.indice}];

if (campoOra.value==null || campoOra.value==""){
//se il campo è vuoto o è null
              //uso l'ora corrente
                      var ora = new Date();
var oraArray = new Array();
                      oraArray[0] = ora.getHours();
                      oraArray[1] = ora.getMinutes();
      }else{
//altrimenti uso il valore salvato
              var oraArray = campoOra.value.split(":");
}

      //creo il widget e lo assegno al placeholder
      var widget = new SpinWheelTimePicker({values:oraArray}, spinDiv);                        
      //eseguo il widget
      widget.startup();
                             
                  });
              });
              </xp:this.value>
      </xp:scriptBlock>
</xp:view>

Questo consente di caricare la pagina con gli N widget attivati e precaricati con il valore salvato nel documento domino o, in mancanza di quello, con l’orario corrente.
A questo punto il problema era quello di scrivere il valore selezionato all’interno del documento domino dato che, per la struttura della mia applicazione, non potevo avere un bottone “submit” per ogni widget e non potevo avere un bottone o un evento che mi salvasse tutti i valori all’uscita della pagina.
Una verifica della documentazione Dojo (https://dojotoolkit.com/api/1.8/dojox/mobile/SpinWheelTimePicker) mostra che il widget SpinWheelTimePicker (e anche SpinWheelDatePicker e SpinWheelPicker) non hanno eventi predefiniti –eccetto onclick, che viene scatenato quando si clicca sul widget, e onblur/onfocus che non sono adatti per quanto si vuole realizzare– per intercettare le azioni dell’utente.
Una ricerca in rete ha portato a questo link (trovato da un mio collega)
http://dojo-toolkit.33424.n3.nabble.com/dojox-mobile-SpinWheel-value-changed-td3282952.html
dove un programmatore mostra come usare la funzione “connect” di Dojo.
La documentazione della dojo.connect (http://dojotoolkit.org/reference-guide/1.7/quickstart/events.html) ci fa capire che potremmo connetterci con una nostra funzione ad un evento o una funzione del widget.
Poiché il widget non ha eventi per noi utili ho cercato quali funzioni avesse disponibili; aiutandomi con quanto suggerito nello scarno esempio sopra citato ho recuperato analizzato la documentazione del widget SpinWheelSlot (http://dojotoolkit.org/api/dojox/mobile/SpinWheelSlot) –che rappresenta ogni singola “rotellina”– e ho trovato la funzione stopAnimation() che viene scatenata quando l’animazione dello slot si ferma.
Ho quindi modificato l’esempio per collegarmi con una mia funzione, che scrive il valore selezionato nel campo che voglio io, alla funzione stopAnimation().
Il codice risultante, da inserire come parametro della funzione widget.startup() è il seguente:

dojo.forEach(widget.getChildren(), function(w){
      //ciclo sui “figli” del widget
if(w instanceof dojox.mobile.SpinWheelSlot){
      //se sono slot, mi collego alla stopAnimation per eseguire
      //la aggiornaValore
widget.connect(w, "stopAnimation", function(){aggiornaValore(#{compositeData.indice});});
}
})

Quindi la widget.startup() diventa:
widget.startup(
dojo.forEach(widget.getChildren(), function(w){
if(w instanceof dojox.mobile.SpinWheelSlot){
widget.connect(w, "stopAnimation", function(){aggiornaValore(#{compositeData.indice});});
}
})        );

Come ultimo passo ho scritto il codice della funzione aggiornaValore.
Essa prende come parametro l’indice del ciclo eseguito dal repeater della pagina in cui è collocato questo custom control, così da operare sulla coppia widget/campo corretta.

Il codice è il seguente:
function aggiornaValore(indice){
//scrive nel campo visibile ma non editabile e nel campo collegato al documento Domino il valore selzionato

//individuo il widget su cui sto selezionando un valore
var oggettoSpin = dojo.query("[id*='spinDiv1']");
var oggettoSpin = oggettoSpin[indice];

//ne recupero la rappresentazione JS
var w = dijit.byId(oggettoSpin.id);

//individuo il campo ad esso accoppiato
var campoOra = dojo.query("[id*='oraSelezionata']");

campoOra = campoOra[indice];

//imposto il valore del campo (è il campo di tipo HIDDEN)
campoOra.setAttribute("value", w.slots[0].value+ ":" + w.slots[1].value);

//trovo il corrispondente campo visibile e ne imposto il valore
var campoOraVis = dojo.query("[id*='oraVisibile']");
campoOraVis = campoOraVis[indice];
campoOraVis.setAttribute("value", w.slots[0].value+ ":" + w.slots[1].value);

//provoco il submit scatenando l'evento click del campo
//contenente il valore da salvare
campoOra.click();
}

L’ultima istruzione serve per far sì che il valore del campo venga salvato nel documento domino ad esso collegato dal data source (bisogna ricordare che non potevo avere pulsanti di submit).
La funzione deve essere posta all’inizio del codice del custom control.

Riporto qui il codice del campo

<xp:inputHidden id="oraSelezionata"
      value="#{compositeData.docNotes.VALORE}">
      <xp:eventHandler event="onclick" submit="true"
              refreshMode="norefresh" save="true">
      </xp:eventHandler>
</xp:inputHidden>

per evidenziare che ha un eventHandler sull’evento onclick che effettua il submit senza aggiornare la pagina.
Per finire, riporto di seguito il codice nella sua interezza.

La stessa metodologia, variando la funzione di aggiornamento del campo in modo da gestire gli slot diversi, può essere usata per gestire il widget SpinWheelDatePicker.

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
      xmlns:xe="http://www.ibm.com/xsp/coreex">
     
              <xp:scriptBlock>
                      <xp:this.value>
                              function aggiornaValore(indice){
                                      //scrive nel campo visibile ma non editabile e nel campo collegato al documento Domino il valore selezionato
                                      var oggettoSpin = dojo.query("[id*='spinDiv1']");
                                      var oggettoSpin = oggettoSpin[indice];
                                      var w dijit.byId(oggettoSpin.id);
                                     
                                      var campoOra = dojo.query("[id*='oraSelezionata']");
                                      campoOra = campoOra[indice];
                                      campoOra.setAttribute("value", w.slots[0].value+ ":" + w.slots[1].value);
                                     
                                      var campoOraVis = dojo.query("[id*='oraVisibile']");
                                      campoOraVis = campoOraVis[indice];
                                      campoOraVis.setAttribute("value", w.slots[0].value+ ":" + w.slots[1].value);

                                      //scateno il submit scatenando l'evento click del campo
                                      //contenente il valore da salvare
                                      campoOra.click();
                                      }
                      </xp:this.value>
              </xp:scriptBlock>
             
              <xp:table>
              <xp:tr>
                      <xp:td>
                              <xp:div id="spinDiv1"></xp:div>
                      </xp:td>
                      <xp:td>
                              <xp:inputHidden id="oraSelezionata"
                                      value="#{compositeData.docNotes.VALORE}">
                                      <xp:eventHandler event="onclick" submit="true"
                                              refreshMode="norefresh" save="true">
                                      </xp:eventHandler>
                              </xp:inputHidden>

                              <xp:inputText id="oraVisibile"
                                      style="width:60px; text-align:center;"
                                      value="#{compositeData.docNotes.VALORE}"
                                      disabled="true">
                              </xp:inputText>
                      </xp:td>
              </xp:tr>
      </xp:table>
     
      <xp:scriptBlock>
              <xp:this.value>

              require([
                  "dojo/_base/window",
                  "dojo/ready",
                  "dojo/registry",
                  "dojox/mobile/SpinWheelTimePicker",
                  "dojox/mobile/parser",
                  ], function(win, ready, registry, SpinWheelTimePicker){
                  ready(function(){
                 
                  var spinDiv = dojo.query("[id*='spinDiv1']"); //individuo i placeholders per il widget
                     
                      var spinDiv = spinDiv[#{compositeData.indice}]; //individuo quello corrente sulla base dell'indice del repeater
 
                  var campoOra = dojo.query("[id*='oraSelezionata']"); //individuo i campi dove leggo il valore salvato in Domino
                  var campoOra = campoOra[#{compositeData.indice}]; //individuo quello corrente sulla base dell'indice del repeater
                  if (campoOra.value==null || campoOra.value==""){ //se il campo è vuoto o è null
                          var ora = new Date();                                                        //uso l'ora corrente
                          var oraArray = new Array();
                          oraArray[0] = ora.getHours();
                          oraArray[1] = ora.getMinutes();
                  }else{
                          var oraArray = campoOra.value.split(":");   //altrimenti uso il valore salvato
                  }
                 
                      var widget = new SpinWheelTimePicker({values:oraArray}, spinDiv); //creo il widget e lo assegno al placeholder
                     
                      //eseguo il widget connettendomi alla funzione stopAnimation per intercettare la selezione di un valore
                      //e lanciare la funzione che scrive nel campo opportuno il valore selezionato
                      widget.startup(
                       dojo.forEach(widget.getChildren(), function(w){
                 if(w instanceof dojox.mobile.SpinWheelSlot){
                     widget.connect(w, "stopAnimation", function(){aggiornaValore(#{compositeData.indice});});
                 }
             })        );
                             
                  });
              });
              </xp:this.value>
      </xp:scriptBlock>

</xp:view>


---------------------------------------------------
Alessandro Bignami
Analista e sviluppatore presso ZEL S.r.L.
alessandro.bignami@zel.it

0 Commenti:

    Nessun Commento Trovato
Commenta articolo
 

Questo spazio web è stato creato da per un uso pubblico e gratuito. Qualsiasi tipo di collaborazione sarà ben accetta.
Per maggiori informazioni, scrivete a info@dominopoint.it

About Dominopoint
Social
Dominopoint social presence: