Enhanced Component Properties

Tempo di lettura 10 minuti

Affermare che l’adozione di una Power Apps Canvas riduce notevolmente i tempi di sviluppo non fà più notizia, è una fatto ormai di pubblico dominio.

Nonostante sono noti i benefici di soluzione disegnata con questa tecnologia si può sempre cascare in problematiche relative alle performance accentuate per applicazioni di dimensioni modeste .

Prima di buttare tutto all’aria dovresti chiederti sempre se stai ottimizzando tutte le chiamate al data source, se stai evitando di duplicare informazione, se il numero di controlli può essere ridotto, e soprattutto se stai utilizzando tutti gli strumenti che ti vengono messi a disposizione.

Nelle righe che seguiranno parleremo di una funzionalità che certamente ti aiuterà nel migliorare le tue applicazioni.

Sto parlando della Enhanced component properties!

Prima di addentrarci nell’utilizzo di questa funzionalità proviamo a definire cosa sono i componenti per una Power Apps Canvas. 

I componenti consentono di creare controlli personalizzati da utilizzare all’interno di una o più app.

I componenti consentono di definire proprietà personalizzate.

Risultano particolarmente adatti per applicazioni con diversi screen e con modelli di controllo simili.

Puoi creare un componente dall’interno di un’app oppure creando un nuovo componente all’interno di una libreria di componenti.

Le librerie dei componenti (component library ) sono il modo consigliato per archiviare diversi componenti in un’unica posizione. 

Qualsiasi componente in una libreria di componenti può essere utilizzato in altre app mentre un componente memorizzato all’interno di un’app non può. 

Oltre a falicitare la condivisione e il riutilizzo non bisogna dimenticare che l’archiviazione di componenti in una libreria agevolerà la vita di altri sviluppatori, fatto non trascurabile

Al netto di questa piccola digressione torniamo a prendere in cosiderazione la funzionalità Enhanced component properties.

Per farlo prova ad immaginare il seguente scenario:

Lo scenario e la demo che ti presenterò tiene in considerazione l’utilizzo di un custom connector perchè è quello più sensibile in quanto soggetto a modifiche più frequenti, tuttavia i ragionamenti e il funzionamento sono replicabili in tutti gli altri scenari che prevedono dei data source diversi dal custom connector.

Immagina di essere il developer che ha in carico di gestire la creazione di un componente cui compito è quello di fare rendering di una lista di giochi. 

Pronti via potresti pensare: ” Ok, nella proprietà OnReset del componente mi basterà inserire la chiamata GET AllGames per recuperare i dati“.

Successivamente il requisito viene modificato ed ora il componente deve pure prevedere una chiamata verso l’API di modifica per aggiornare il prezzo del singolo gioco.

A questo punto potresti dire: “invoco la PUT UpdateGameById direttamente nell’OnSelect del button all’interno del componente“. 

Ecco, questo approccio di gestione delle chiamate potrebbe essere l’inizio della fine. 

Infatti è possibile immaginare che saresti portato a ripeterlo pure per altri componenti, duplicando così le chiamate e soprattutto facendo chiamate inutili perchè magari quel tipo di dato già è noto all’interno dell’app principale . 

Immagia per un attimo di avere 30 component library e di aver utilizzato in tutti una determinata chiamata.

Arriva il giorno X e hai modificato la chiamata nel custom connector.

Ora dovrai andare in tutti componenti che utilizzano quella chiamata e riaggiornare il custom connector e la chiamata.

Di conseguenza dovrai rifare la review per tutti i componenti che stai utilizzando dilatando i tempi di sviluppo e test.

Inoltre c’è da tenere in considerazione che le performance della power apps sono influenzate pure dal numero di connessioni create con il data source e anche dal numero di chiamate che vengono effettuate. 

Quindi bisogna trovare un modo per centralizzare il dato,avere un solo punto di modifica, limitare il numero di connesisoni create e il numero di chiamate lanciate con il custom connector.

Ecco che quindi in questo contesto interviene la funzione di Behavior delle component library, il power ranger pronto ad aiutarci nello sviluppo della Power Apps Canvas

Ottimo! 

Finalmente possiamo entrare nel dettaglio della funzionalità Enhanced component properties .

Questa funzionalità consente di creare proprietà nel componente di tipo behavior.

Una proprietà behavior consente di creare eventi custom che verrà invocato dal component verso l’app principale.

L’app principale prenderà in carico la richiesta ed esuirà una funzione per il componente. 

L’immagine qui accanto rappresentare in maniera semplificata la funzionalità appena descritta.

Questo giro vale anche in scenari più semplici ovverò che l’app principale esegue come funzione un Notify al posto del componente.

A breve vedremo nel dettaglio come abilitare questa proprieta e come creare eventi custom.

Quello che ti presenterò è uno scenario semplificato che cerca di rappresentare una situazione reale.

Prima di passare alla pratica ti introduco ad alto livello la demo che ho preparato per te. 

Immaginando un caso reale dove un team di backend sviluppa un set di API custom, ho preparato un set limitato di API . 

Queste API sono state rese disponibili sfruttato Azure.

Chiaramente puoi utilizzare anche altri servizi di Hosting non per forza Azure.

Una volte deployate le API ho creato un custom connector per poter consumarle tramite una Power Apps Canvas.

Per non uscire dallo scopo di questo articolo non andremo ad analizzare il codice scritto. Non vedremo nemmeno la configurazione di un custom connector.  Se vuoi approfondire puoi trovare il progetto completo nel mio profilo GitHub . 

I concetti che stiamo per affrontare rimangono validi anche se non stai utilizzando un custom connector come data source. 

Siamo finalmente arrivati al momento decisivo, possiamo iniziare a mettere le mani in pasta.

Qui sotto ti riporto l’applicazione che andremo a sviluppare.

Il riquadro rosso indica il componente che ha il compito di fare il rendering della lista di giochi recuperata tramite il custom connector. Dalla Tree View è riportato con il nome ComponentListGame_1.

Come già ti ho anticipato nelle righe precedenti all’interno dell’applicazione viene utilizzato il custom connector come riportato nell’immagine seguente.

Entriamo subito nel vivo del componente per capire com’è strutturato.

Nell’immagine che ti riporto di seguito puoi notare il componente sviluppato all’interno della component library GameCatalog-library. 

Espandendo la Tree view del componente è possibile osservare che prevede al suo interno semplicemente una gallery chiamata GalleryGames.

Questa gallery ha il compito di fare il rendering della lista di giochi che viene passata tramite una proprietà di input.

Nella sezione Custom Properties è presente la prima proprietà è ListGames di tipo table che rispetta il modello della risposta del custom connector. 

Questa proprietà è una classica proprietà di input e verrà riportata nella  proprietà Items della galleria GalleryGames.

Oltre a questa proprietà ne ho definito un’altra.

La seconda proprietà chiamata CallbackOnClickDiscount è la proprietà di tipo behavior.

Prima divedere come utilizzarla bisogna però abilitare questa funzionalità altrimenti non sarà possibile andare a creare una nuova proprietà di questo tipo. 

Per abilitarla devi andare in Settings > Upcoming features e cercare behavior . A questo punto ti basterà abilitare la funzionalità Enhanced component properties come nell’immagine che ti riporto di seguito.

Bisonga precisare che si tratta di una funzionalità in preview ciò significa che potrebbe essere soggetta a qualche cambiamento prima che passi in General Availabity.
Questo però non significa che non puoi utilizzarla, ma dovrai prestare attenzione nel suo utilizzo specialmente per futuri aggiornamenti. 

Ora che hai abilitato la funzionalità potrai creare una nuova proprietà di tipo behavior. 

Per farlo ti basterà cliccare su New custom property e nel campo Propery type troverai Behavior.

Prendendo in considerazione la CallbackOnClickDiscount che ho creato puoi notare la possibilità di definire un tipo di ritorno.

Puoi definire un tipo di ritorno a tuo piacimente, ad esempio potrebbe essere un booleano per identificare se l’esito dell’operazione oppure potrebbe essere una table per rimandare direttamente indietro la lista aggiornata.  

La cosa però veramene interessante risulta essere l’opzione Parameters

Hai la possibilità di definire dei paramentri che saranno poi passati all’app principale tramite il behavior.

In questo caso ho definito il parametro GameObjSelected (non chiaro cosa viene passato e cosa succede) di tipo record che rispetto il modello dell’oggetto della gallery.

Se libero di scegliere il data type che più si adatta alla tua applicazione. 

Prova ad immaginare questo parametro come se fosse il parametro di una funzione. 

Puoi lasciarlo opzionale oppure impostarlo obbligatori cliccando su Required.

Io in questo caso ho scelto un tipo Record perchè utilizzerò un oggetto per comunicare all’applicazione principale quale gioco è stato selezionato dalla gallery GalleryGames

Grazie all’immagine che segue puoi notare come ho definito il modello che dovrà rispettare questo Record.

Il modello del Record non per forza deve essere identico all’oggetto definito nel modello della lista GalleryGames.

Questo perchè potresti avere la necessità di passare solo alcune proprietà.

Ricapitoliamo.

Ti ho appena mostrato come creare una nuova proprietà behavior e come definire dei parametri opzionali oppure mandatori per trasferire il dato all’app pricipale. 

Ora che finalmente abbiamo visto come definire la proprietà behavior dobbiamo concentrarci su come lanciare l’evento.

Per farlo abbimo bisogno di un controllo che ha la proprietà OnChange oppure OnSelect

In questo caso sfruttiamo la proprietà OnSelect del pulsante btnDiscount che ho inserito all’interno della gallery GalleryGames, qui sotto ti ripropongo la tree view del component che stiamo sviluppando.

Il compito di questo pulsante è quello di applicare uno sconto qualora il prezzo del gioco sia superiore a 30 € , condizione impostabile scrivendo un semplice if nella proprietà DisplayMode del pulsante. 

Noi però non vogliamo che la chiamata al custom connector avvenga direttamente all’interno del componente.

Quello che vogliamo disaccoppiare questo legame.

Quindi dobbiamo sfruttare la proprietà behavior CallbackOnClickDiscount  che abbiamo precedentemente creato.

Come farlo ? 

Nella proprietà OnSelect del pulsante btnDiscount ho inserito la seguente funzione

ComponentListGames.CallbackOnClickDiscount(ThisItem)

Che cos’è ThisItem ?

È esattamente l’oggetto che verrà cliccato e utilizzato come parametro per la CallbackOnClickDiscount.

In questo caso passerò tutto l’oggetto selezionato ma se ad esempio avevo la necessità di passare soltanto il prezzo del gioco avrei potuto scrivere la seguente funzione 

ComponentListGames.CallbackOnClickDiscount(ThisItem.price)

Chiaramente in questo caso non avresti dovuto definire un tipo record per la proprietà ma la proprietà sarebbe stata di tipo Number.

Fantastico ! Il componente è finalmente pronto per lanciare l’evento una volta cliccato il pulsante! 

A questo punto bisogna tornare nell’app principale e aggiungere il componente.

Una volta aggiunto troverai la proprietà CallbackOnClickDiscount nella sezione Custom Properties.

Sarà proprio in questo punto che dovrai inserire la funzione che deve essere eseguita quando questo evento viene invocato dal componente. 

Pertanto cliccando sulla custom property CallbackOnClickDiscount ho inserito la seguente funzione :

IfError(
    'GameCatalog-API'.PUTGAME(
        GameObjSelected.upc,
        {
            upc_1: GameObjSelected.upc,
            name: GameObjSelected.name,
            shortDescription: GameObjSelected.shortDescription,
            price: GameObjSelected.price - (GameObjSelected.price*0.2),
            releasedDate: GameObjSelected.releasedDate
        }
    ),
    Notify(
        "Error during price update",
        NotificationType.Error
    ),
    Set(
        GamesListStore,
        'GameCatalog-API'.GETGAMEs()
    );
    Notify(
        "Game price succesfully updated",
        NotificationType.Success
    )
);

Osserva come sono la funzione è stata inserita solo a livello dell’app e non all’interno del componente.

La funzione che ho inserito prevede un IfError per gestire un potenziale errore runtime della chiamata PUTGAME.

Puoi notare come il body della chiamata PUTGAME viene costruito utilizzando la proprieta esposta dal behavior ovvero GameObjSelected. 

Nel caso in cui la chiamata va in errore verrà eseguito un Notify con il messaggio: ” Error during price update“.

Viceversa se la chiamata va a buon fine viene effettuata la chiamata GETGAMEs per recuperare la lista aggiornata e settarla nella variabile globale GamesListStore. Successivamente viene effettuato anche un Notify con il messaggio “Game price succesfully updated“. 

La variabile globale GamesListStore deve essere interpretata come store globale che contiene la lista dei giochi sempre aggiornata.

Questo per consentire di avere un unico punto dove andare a recuperare l’informazione per trasferire il dato al componente oppure per recuparalo in altri screen in maniera più rapida e performante senza la necessità di duplicare chiamate in giro nell’app. 

Sfruttando il monitor per analizzare il funzionamento dell’ app puoi osservare che dopo l’operazione di Select del pulsante viene lanciate le chiamate PUTGAME.

Spostandoti nella sezione Details della chiamata puoi notare che il controlName è esattamente ComponentListGames_1 che invoca la proprietà CallbackOnClickDiscount. 

La cosa più interessante è il body della chiamata.

Esaminando la Request puoi notare che il body ha le proprietà tutte popolate in base al gioco che è stato selezionato, questo è esattamente il ThisItem che è stato definito all’interno del componente.

Grandioso! 

Si perchè a mio parere questa nuova funzione seppure ancora in preview è incredibilmente utile .

Se volevi farlo prima del rilascio di questa funzionalità dovevi utilizzare un Toogle insieme ad una prorpietà di output di tipo boolean. 

Questa pratica però, specialmente per progetti di modeste dimensioni, va ad introdurre complessità e ostacola la manutenzione dell’app. Un’altra alternativa era quella di applicare la logica al componente nella proprietà OnReset, tuttavia pure in questo caso si porebbe intoppare in situazioni non attese.

Con l’utilizzo della proprietà behavior puoi creare potenti componenti migiliorando notevolmente la sinergia con l’app canvas. 

Questo ti dà la libertà di definire ad esempio trasformazioni complesse in un unico luogo e soprattutto avendo un solo entry point.

Sfruttando questo approccio eviterai di duplicare chiamate, ottimizzerai le performance, ridurrai il numero di connessioni e lascierai al componente il solo compito per cui è stato pensato.