English
Instant Developer Foundation Documentazione
  • Approfondimenti
  • Integrazione componenti javascript

Introduzione ai componenti javascript FLUID

copia link

Introduzione

Instant Developer, fin dalla sua origine, permette di personalizzare le applicazioni che vengono sviluppate. È possibile personalizzare l'IDE ma anche personalizzare i template utilizzati dall'IDE per generare le applicazioni web. La personalizzazione della parte server, con l'aggiunta di classi C#/Java/Javascript è sempre stata più semplice di quella dell'interfaccia utente: aggiungere un nuovo componente grafico che non fosse già parte del template è complicato.

Nella versione 20.0 di Instant Developer abbiamo cercato di semplificare l'aggiunta di nuovi componenti client di terze parti. In particolare abbiamo cercato di semplificare l'integrazione con il framework già esistente permettendo di inserire oggetti grafici e integrarli con il framework di Instant Developer.

Vediamo di seguito come si integra un componente javascript.

Definizione dell'interfaccia nell'IDE

Per prima cosa occorre definire nel progetto una libreria che descriva come è fatto l'oggetto client. Questa nuova libreria è di tipo "libreria client" e si aggiunge tramite il relativo comando del menù contestuale del progetto.


Una volta creata la libreria client occorre definirne proprietà, procedure ed eventi. Le procedure permetteranno di eseguire i corrispondenti metodi sull'oggetto client, le proprietà permetteranno di scambiare dati tra client e server e viceversa mentre gli eventi permetteranno all'oggetto client di inviare informazioni al server.

Le proprietà e gli eventi possono essere di qualunque tipo semplice (int, string, date, etc) oppure di tipo IDMap o IDArray ed il loro nome deve essere uguale a quello che poi andremo a definire nell'interfaccia Javascript (vedi dopo).
Se il parametro è di tipo IDMap la controparte client sarà un Object. Se, invece, il parametro è di tipo IDArray sarà mappato con un array client.

Nota 1: Non è necessario indicare l'espressione in C# o Java: viene automaticamente calcolata da Instant Developer ogni volta che cambia il nome della funzione.
Nota 2: Non è possibile specificare altri tipi di oggetti perché devono essere serializzabili in JSON.
Nota 3: Non è possibile definire funzioni perché avvenendo in modo asincrono la comunicazione con il javascript non c'è modo di ottenere un valore in modo sincrono.

Utilizzo nei frame

Una volta definita la libreria client, che descrive come è fatto l'oggetto javascript corrispondente, è possibile indicare ad Instant Developer dove si usa.
Per farlo è sufficiente tirare la libreria all'interno dei riquadri delle videate o all'interno di campi statici (come un qualunque altro riquadro: pannelli, alberi, book, grafici, etc.).


All'interno della videata verrà creato un nuovo oggetto (un'istanza di quella libreria) con l'icona di una sotto-videata. Tramite l'oggetto appena creato potremo controllare quella particolare istanza di oggetto client. Nel designer non verrà mostrato nulla se non il nome della libreria client che è collegata al riquadro. Potremo chiamare i metodi che abbiamo definito nella libreria, valorizzare le proprietà e, facendo tasto destro, implementare gli eventi che abbiamo definito nella libreria client.

Utilizzo nei campi in lista

Dalla versione 21.0 è possibile utilizzare componenti personalizzati anche nei campi di pannello in lista.
Per farlo basta tirare la libreria sul campo di pannello (quello con icona gialla) nell'albero oppure direttamente dall'anteprima della videata.


In questo caso all'interno della videata non verrà creato nessun nuovo oggetto. L'oggetto client sarà direttamente controllabile referenziando il campo di pannello.
Nel designer non verrà mostrato nulla se non il nome della libreria client che è collegata al campo di pannello.
Per interagire con gli oggetti in lista occorre valorizzare le proprietà ed invocare i metodi nell'evento OnDynamicProperties, mentre per i campi fuori lista o in dettaglio si può farlo in qualunque altro punto del codice.

Classe wrapper lato client

Per facilitare l'inserimento di qualunque oggetto grafico occorre una classe di interfaccia scritta in Javascript. Questa classe è quella che si inserisce tra il framework di Instant Developer (FLUID) e l'oggetto client di terze parti.

In fondo all'articolo è scaricabile un template di classe wrapper con tutto ciò che serve per partire.

Questa classe deve avere la stessa forma di quella della libreria client che abbiamo definito nel progetto. Deve avere le stesse proprietà e possedere gli stessi metodi che abbiamo definito nella libreria all'interno del progetto.

Tale file andrà inserito nella directory custom dell'applicazione in cui utilizziamo la libreria client, nel percorso: custom > fluid > objects.
Nella directory custom dovranno essere inseriti anche tutti gli eventuali file utilizzati dall'oggetto client di terze parti: file javascript, font, css, etc.


Il file javascript wrapper verrà automaticamente caricato dall'applicazione a run-time (Instant Developer lo inserirà automaticamente nel file DesktopFluid.htm e DesktopFluid_sm.htm).

Attenzione: se sono stati già personalizzati i file DesktopFluid.htm e/o DesktopFluid_sm.htm occorre riallinearli al template inserendo le nuove macro che sono state inserite e che vengono utilizzate da Instant Developer per inserire il file Javascript wrapper.

Vediamo ora come deve essere definita la classe wrapper scritta in javascript. Innanzitutto il nome della classe deve concidere con il nome del file. Se la classe si chiama MyClientLibrary il file si deve chiamare MyClientLibrary.js. Il nome del file, infatti, viene utilizzato da Instant Developer per creare, quando necessario, l'istanza della classe Javascript lato client.
La classe wrapper deve estendere la classe del framework Element come mostrato nell'esempio seguente:

Client.MyClientLibrary = function (element, parent, view)
{
  // Call base constructor
  Client.Element.call(this, element, parent, view);
  //
  // Initialization of properties
  //...
  //
  // Copy property from design time values
  this.updateElement(element);
  //
  // Attach server events
  this.attachEvents(element.events);
  //
  // Put the DOM object in the document
  parent.appendChildObject(this, this.domObj);
}
//
// Define extension of class
Client.MyClientLibrary.prototype = new Client.Element();


Vediamo come è composto il costruttore della classe. Innanzitutto possiamo notare che il costruttore viene assegnato ad una proprietà chiamata Client.MyClientLibrary, che rappresenta la classe di questi elementi. È necessario che la proprietà di Client che contiene il costruttore abbia lo stesso nome della classe dell’elemento definita nella libreria del progetto.

A questo punto occorre chiamare la classe base degli elementi nella webview, e questo viene fatto dalla prima riga di codice del metodo:

Client.MyClientLibrary.call(this, element, parent, view);

Il compito successivo della parte di inizializzazione è quello di creare un oggetto DOM che conterrà tutti gli altri oggetti del componente. In questo caso viene creato un DIV, che poi viene memorizzato nella proprietà this.domObj. Questo passo è essenziale perché questa proprietà viene poi utilizzata da diversi componenti del framework client. Il tipo di oggetto da creare come contenitore per gli oggetti DOM creati dal componente può variare in funzione delle specifiche del componente stesso.

A questo punto è possibile utilizzare il costruttore per inizializzare proprietà specifiche del componente, fino ad arrivare all’ultima parte in cui viene effettuata l’inizializzazione delle proprietà e degli eventi. Vengono richiamati i seguenti metodi:

  1. this.updateElement(element);
  2. this.attachEvents(element.events);
  3. parent.appendChildObject(this, this.domObj);

this.updateElement è il metodo che viene chiamato anche dal framework durante i cambiamenti di valore delle proprietà. L’oggetto passato contiene le proprietà ed i relativi valori da impostare.
Il metodo può esser chiamato prima che this.domObj sia stato attaccato al DOM della pagina, oppure può esser preceduto dal punto 3).

this.attachEvents serve per attaccare gli eventi di interesse al componente in modo da poterli notificare al front-end.

parent.appendChildObject è il metodo che attacca il DOM del componente a quello dell’elemento padre. Si noti che non sempre l’elemento padre è già attaccato al DOM della pagina. Anche se normalmente è così, in alcuni casi esso viene attaccato dopo che tutti i figli sono già stati inizializzati; questo può richiedere per alcuni componenti l’utilizzo di una setTimeout per ritardare alcune inizializzazioni.

Per completare la fase di inizializzazione dell’elemento occorre estendere la classe base con la seguente riga di codice, esterna al costruttore. In questo caso occorre estendere la classe base impostata nella classe di interfaccia, come ad esempio Element, Container, Input e così via.

Client.MyClientLibrary.prototype = new Client.Element();

Aggiornamento delle proprietà

Vediamo adesso come viene eseguito l’aggiornamento delle proprietà, sia al momento dell’inizializzazione che al momento della variazione delle stesse. Il metodo che si occupa di questo è updateElement, come vediamo nel codice seguente:

Client.MyClientLibrary.prototype.updateElement = function (props)
{
  // ...
  Client.Element.prototype.updateElement.call(this, props);
}


Il parametro passato è un oggetto che contiene le proprietà da impostare sul componente. La classe di interfaccia può intercettare le proprietà che desidera, rimandando le altre al trattamento di default che avviene nella classe base Element.

Al termine del metodo viene richiamata la classe base con la riga di codice:

Client.Element.prototype.updateElement.call(this, el);

Anche in questo caso si consiglia di richiamare la classe base specifica dell’elemento.

Caricamento dipendenze

Per caricare i file dell'oggetto grafico (es: file javascript, file css) è possibile procedere in 2 modi:

  1. inserire i file nei file DesktopFluid.htm e DesktopFluid_sm.htm
  2. caricarli dinamicamente, quando la videata, contenente l'oggetto grafico, viene aperta la prima volta
Se si desidera caricare dinamicamente i file dell'oggetto grafico è possibile utilizzare il metodo statico getRequirements:

Client.MyClientLibrary.getRequirements = function ()
{
  let req = {};
  req["fluid/objects/MyClientLibrary/main.min.css"] = {type: "cs", name: "main.min.css"};
  req["fluid/objects/MyClientLibrary/main.min.js"] = {type: "jc", name: "main.min.js"};
  
  //...
  
  return req;
}

Inizializzazione degli eventi

Il metodo che inizializza gli eventi si chiama attachEvents:

Client.MyClientLibrary.prototype.attachEvents = function (events)
{
  // ....
  // Call base class
  Client.Element.prototype.attachEvents.call(this, events);
}


Il parametro events è un array di stringhe che contiene gli eventi a cui il front-end è interessato, cioè per i quali è stato definito uno script che lo gestisce. Per ogni evento disponibile occorre quindi vedere se esso è presente nella lista passata come parametro, e, in caso affermativo, occorre toglierlo dalla lista e attaccare al componente il relativo event handler.

È poi importante rimuovere dall’array events quelli già gestiti, in modo che le classi base non lo facciano di nuovo. Al termine del metodo, infatti occorre richiamare la classe base.

Notifica di eventi

Per notificare gli eventi al front-end è possibile passare al metodo Client.mainFrame.sendEvents(param) un array composto dai seguenti oggetti:

  • obj: è l’id dell’elemento che notifica l’evento.
  • id: è il nome dell’evento come definito nell’interfaccia dell’elemento.
  • content: sono i parametri da passare all’evento.


Client.MyClientLibrary = function (element, parent, view)
{
  // Call base constructor
  Client.Element.call(this, element, parent, view);
  //
  let options = Object.assign({

    // ...

    evOne: function (ev) {
      Client.mainFrame.sendEvents([{obj: this.id, id: "OnEvOne", content: [Client.MyClientLibrary.adjustInfo(ev)]}]);
    }.bind(this),
    evTwo: function (ev) {
      Client.mainFrame.sendEvents([{obj: this.id, id: "OnEveTwo", content: [ev.event.id]}]);
    }.bind(this),
    // ...
  }, element.options);

  // ...

};

Integrazione in campi di pannello

Quando il componente è utilizzato in un campo di pannello, il framework utilizza la proprietà della classe con nome value per assegnare il valore al campo.

Se la classe non ha una proprietà con questo nome occorre indicare qual è la proprietà che il framework deve usare per assegnare il valore al campo.
Per esempio, se si vuole usare la proprietà intProperty occorre scrivere la seguente riga di codice:

Client.MyClientLibrary.defaultBindingProperty = "intProperty";

Metodi sovrascrivibili nella classe di interfaccia

Oltre ai metodi già descritti, le classi di interfaccia possono sovrascrivere diversi altri metodi della classe base di gestione degli elementi visuali nella webview. Vediamo adesso un elenco di questi metodi:

  • Client.<elemento>.prototype.appendChildObject = function (child, domObj):
    Questo metodo dovrebbe essere sovrascritto solo se un elemento contenitore gestisce l’inserimento a video di un elemento figlio in un modo specifico. Il metodo standard consiste nell'aggiungere l’oggetto radice dell'elemento figlio al domObj di questo elemento.

    Il parametro child è l’elemento che deve essere inserito. Il parametro domObj è il nodo HTML dell’elemento child che deve essere aggiunto a quello attuale.
  • Client.<elemento>.prototype.close = function (firstLevel, triggerAnimation):
    Questo metodo viene chiamato quando l’elemento viene rimosso dal DOM. Un elemento dovrebbe sovrascrivere questo metodo solo se ha bisogno di aggiornare il DOM in un modo specifico diverso dalla rimozione dell'oggetto radice.
  • Client.<elemento>.prototype.onResize = function (ev):
    Questo metodo viene chiamato quando il browser viene ridimensionato. Può essere utile per aggiornare la visualizzazione del componente se essa non lo fa automaticamente.
  • Client.<elemento>.prototype.getRootObject = function ():
    Questo metodo viene chiamato per sapere qual è il nodo HTML radice dell’elemento. Normalmente il metodo restituisce this.domObj.
  • Client.<elemento>.prototype.onRemoveChildObject = function (child):
    Questo metodo viene chiamato su un elemento padre quando un elemento figlio viene rimosso. Va sovrascritto solo se si deve aggiornare l'elemento padre dopo la cancellazione del figlio.
  • Client.<elemento>.prototype.onPositionChildObject = function (position):
    Questo metodo viene chiamato su un elemento padre quando un elemento figlio viene inserito e poi posizionato in mezzo agli altri. Va sovrascritto solo se si deve aggiornare l'elemento padre dopo l’inserimento posizionato del figlio.

Ultima modifica: 28/11/2022 / Validità: da 22.5.8700

Vedi anche

  • ID Array
  • ID Map
  • On Dynamic Properties (evento ricorrente)
    Scarica il template per la classe wrapper MyClientLibraryFluid.zip

Hai trovato utile l'articolo?

Grazie per il feedback!

Ti serve altro aiuto?

Consulta il Forum