Per realizzare una applicazione web, ci sono fondamentalmente 3 modi per gestire l’HTML che poi verrà mostrato nel browser:
- HTML statico, scritto a mano (il DOM non cambia)
- struttura HTML scritta a mano con elementi DOM creati da codice JavaScript
- caricare pezzi di codice HTML per poi inserirli in un punto preciso del DOM
In questo articolo andremo a realizzare un form per il login dove l’HTML è statico e scritto a mano. In questo caso gli elementi del DOM sono già presenti nel codice HTML e, quindi, per poter interagire con loro, bisognerà effettuare un bind tra questi ed un puntatore che poi useremo nel codice pascal.
Per rendere il tutto graficamente accattivante, seguirò il consiglio che ho trovato su un articolo di Blaise Pascal Magazine al quale mi sono ispirato per questo tutorial: verrà utilizzato il framework Bulma (http://bulma.io/). Si tratta di un framework veloce e pulito che, per renderizzare tutti elementi, fa uso esclusivo di CSS.
Prerequisiti
Per poter utilizzare il transpiler Pas2Js è necessario che questo venga installato e configurato.
Per effettuare questa procedura, potete fare riferimento all’articolo:
Iniziamo: nuovo progetto Web Browser Application!
Per iniziare, apriamo Lazarus e, da menù: File -> Nuovo … , scegliamo Web Browser Application.
Ci apparirà un wizard e lo andremo a completare come sotto.
Spiegazione delle voci selezionate:
- Create initial HTML page: il wizard ci proporrà in modo automatico una pagina HTML precompilata (che potremo modificare e/o sostituire in qualsiasi momento);
- Maintain HTML page: si tratta di un’opzione ancora sperimentale, per ora si dovrebbe limitare a cambiare il nome del file HTML del progetto al cambiare del nome del progetto; pare che ancora non funzioni molto e quindi sarà nostra cura tenere allineati i nomi dei file;
- Run rtl when all page resources are fully loaded: fondamentalmente serve a lanciare rtl.run immediatamente oppure solo quando tutte le risorse sono state caricate (consigliato);
spuntando questa opzione, invece di avere nell’HTML:rtl.run();
avremowindow.onload=function(){
rtl.run();
} - Let rtl show uncaugth exception: deve essere una nuova impostazione con la versione 2.2.0 di Pas2Js, ma non ho ancora avuto modo di sperimentarla…
- Use Browser Application Object: si tratta della stessa opzione che abbiamo a disposizione quando ad esempio creiamo un nuovo progetto console; se non si mette la spunta, il sorgente del progetto è veramente minimale, con la spunta si avrà un oggetto TApplication che incapsulerà le nostre funzionalità in una classe;
- Use Browser Console Unit to display Writeln() output: non ha bisogno di commenti 😉
File di progetto HTML : elementi indispensabili di una pagina per lavorare con Pas2Js
Usando il wizard di Lazarus per creare una nuova WebApplication, ia pagina HTML che viene generata automaticamente è la seguente.
<!doctype html> <html lang="en"> <head> <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Project1</title> <script src="project1.js"></script> </head> <body> <script> window.addEventListener("load", rtl.run); </script> </body> </html>
In realtà l’HTML può essere cambiato totalmente, l’importante sono le 2 righe che vedete evidenziate in grassetto:
- <script src=”project1.js”></script>
indica il nome del file con la logica dell’applicazione; se il ns progetto si chiama project1.pas, il transpiler genererà project1.js e questo è il file che deve essere indicato nel tag <script> - <script>window.addEventListener(“load”, rtl.run);</script>
questo è invece lo script che contienre la rtl tradotta in JavaScript, il sorgente che permetterà il funzionamento della nostra applicazione
Per rendere la cosa un pò più divertente, come già accennato, è stato utilizzato il framework Bulma e quindi il file HTML è stato cambiato come segue.
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Login demo</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Questrial&display=swap"> <link rel="stylesheet" href="https://unpkg.com/bulma@0.9.3/css/bulma.min.css"> <script src="project_login.js"></script> </head> <body> <section class="hero is-success is-fullheight"> <div class="hero-body"> <div class="container has-text-centered"> <div class="column is-4 is-offset-4"> <h3 class="title has-text-black">Login</h3> <div class="box"> <form action="javascript:void(0)"> <div class="field"> <div class="control"> <input id="edtEmail" class="input is-large" type="email" placeholder="Your Email" autofocus=""> </div></div> <div class="field"> <div class="control"> <input id="edtPassword" class="input is-large" type="password" placeholder="Your Password"> </div></div> <button id="btnLogin" class="button is-block is-info is-large is-fullwidth"> Login <i class="fa fa-sign-in" aria-hidden="true"></i> </button> </form> </div> </div> </div> </div> </section> <script> window.addEventListener("load", rtl.run); </script> </body> </html>
Notare che l’HTML è cambiato sensibilmente, soprattutto per adattarsi all’uso del framework in questione, ma i 2 script evidenziati in grassetto sono rimasti identici.
Guardando il resto dell’HTML, gli elementi chiave di questo form sono i seguenti:
- elemento input con id “edtEmail”
- elemento input con id “edtPassword”
- elemento bottone con id “btnLogin”
Per ora è importante sapere che è fondamentale che questi elementi siano dotati di id in quanto è esattamente tramite questo identificativo che andremo a fare il binding.
File di progetto .PAS
Il wizard avrà generato anche il file di progetto in pascal.
Segue il codice del progetto.
NB:
notate nel codice il commento che indica di eliminare o commentare lo statement Application.Free.
Questa riga infatti non deve essere eseguita in quanto finirebbe per togliere I valori alle vostre variabili inizializzate dopo la creazione.
Non è detto che la troverete in quanto dovrebbero già averla eliminata dalla generazione automatica del wizard, ma se c’è… eliminatela.
Per riferimento vi invito a leggere questo breve thread.
https://forum.lazarus.freepascal.org/index.php/topic,59432.0.html
program project_login; {$mode objfpc} uses browserapp, JS, Classes, SysUtils, Web; type TMyApplication = class(TBrowserApplication) procedure doRun; override; end; procedure TMyApplication.doRun; begin // Your code here Terminate; end; var Application : TMyApplication; begin Application:=TMyApplication.Create(nil); Application.Initialize; Application.Run; // Se presente, commentare o eliminare Application..Free // Application.Free; end.
Analizzando brevente il seguente codice, notiamo che:
- il nome del projetto è “project_login” e verrà tradotto in project_login.js, che è lo stesso script presente nel file di progetto HTML visto sopra
- l’applicazione eredita da TbrowserApplication
Primo step: adattamento della classe per il binding
Visto che dobbiamo “agganciare” degli elementi del DOM definiti nell’HTML, all’interno del ns sorgente pascal, facciamo qualche modifica alla classe dell’applicazione in modo da:
- definire i puntatori agli elementi del DOM che abbiamo bisogno di gestire
- definiamo una procedura che si occuperà di fare il bind
{ TMyApplication } TMyApplication = class(TBrowserApplication) edtEmail: TJSHTMLInputElement; edtPassword: TJSHTMLInputElement; btnLogin : TJSHTMLButtonElement; procedure doRun; override; private procedure BindElements; end;
In particolare, sia edtEmail che edtPassword sono di tipo TJSHTMLInputElement. Quest’ultima classe è definita nella unit Web (in uses al progetto).
Il puntatore btnLogin è di tipo TJSHTMLButtonElement.
Sia TJSHTMLInputElement (destinatario di una istanza di un elemento di tipo <input>) che TJSHTMLButtonElement (destinatario di una istanza di un elemento di tipo <button>), ereditano entrambi dalla classe TJSHTMLElement.
Nella sezione private della classe TmyApplication, troviamo il metodo BindElements, che si occuperà di valorizzare i puntatori sopra definiti con le istanze effettive del DOM.
Ecco come effettuare il bind.
procedure TMyApplication.BindElements; begin Writeln('BindElements'); edtEmail:=TJSHTMLInputElement(GetHTMLElement('edtEmail')); edtPassword:=TJSHTMLInputElement(GetHTMLElement('edtPassword')); btnLogin:=TJSHTMLButtonElement(GetHTMLElement('btnLogin')); end;
NB:
notare la Writeln: scriverà nella console del browser!
Secondo step: risposta all’evento OnClick
Visto che stiamo preparando un form per fare il login, ad un certo punto dovremo rispondere all’evento OnClick.
Verosimilmente, all’interno di questa risposta verrà effettuata una richiesta di autenticazione ad un’altra procedura.
Adattiamo ancora la classe.
{ TMyApplication } TMyApplication = class(TBrowserApplication) edtEmail: TJSHTMLInputElement; edtPassword: TJSHTMLInputElement; btnLogin : TJSHTMLButtonElement; procedure doRun; override; private procedure BindElements; procedure doLoginClick(aEvent: TJSEvent); procedure doServerLogin(const aUser, aPassword: String); end;
A questo punto modifichiamo il metodo doRun per fare in modo che alla partenza dell’applicazione, venga effettuato il bind e vengano anche agganciati i metodi in risposta agli eventi che il browser rileverà.
procedure TMyApplication.doRun; begin // Your code here BindElements; btnLogin.AddEventListener('click',@DoLoginCLick); Terminate; end;
Quindi segue il codice pascal per richiedere l’autenticazione.
procedure TMyApplication.doLoginClick(aEvent: TJSEvent); begin Writeln('doLoginClick'); DoServerLogin(edtEmail.Value,edtPassword.Value); end;
In questo caso l’autenticazione consiste nel far apparire un alert.
procedure TMyApplication.doServerLogin(const aUser, aPassword: String); begin window.alert('Richiesta login per: '+aUser + ' (pass: ' + aPassword + ')'); end;
Testiamo il tutto
Non ci resta che puntare il nostro browser preferito sul file html e vedremo la form per il login.
Cliccando sul bottone login…
Conclusioni
Un tutorial un po’ lungo, ma visto che si tratta della prima esperienza ho voluto analizzare bene il funzionamento.
Per qualsiasi domanda su questo articolo potete come al solito usufruire del ns forum https://www.lazaruspascal.it/
Buon lavoro
Riferimenti
Questo articolo ed il relativo codice pascal è tratto da un articolo di Mychael Van Canneyt su Blaise Pascal Magazine nr.99/100.