Molto spesso nei nostri progetti c’è la necessità di passare dati fra diversi controller; una delle soluzioni che ho visto adottare spesso è quella di utilizzare emit / broadcast dalla parte di chi il dato lo vuole condividere e registrare uno o più listener dalla parte dei controller che devono essere notificati di questo nuovo dato.

Il tipo di approccio sopra pò presentare qualche criticità, sia nel capire il codice per chi non l’ha scritto (e anche per chi l’ha scritto, se passa molto tempo) sia per la sua implementazione; se abbiamo il controller A che deve condividere il dato xx con il controller B ed entrambi possono modificare xx, ci si trova  a dover creare un emit sia sua A sia su B e, allo stesso modo, un listener sia su A sia su B. Infine, una parola anche sulla testabilità: con tale approccio si può testare, ma anche la parte di testing diventa più difficoltosa e poco lineare.

Dopo questa premessa, vi propongo un approccio diverso, sfruttando un service per scambiare i dati e mantenere lo stato; tutto questo è possibile perchè in Angular i service sono singleton, quindi una volta istanziati dal container DI la stessa istanza viene passata nel costruttore di ogni oggetto che ne faccia richiesta tramite dipendenza nel costruttore.

Nel codice allegato (che trovate alla fine) c’è un esempio funzionante di quanto vi sto dicendo; ve lo descrivo brevemente di seguito.

Abbiamo una semplice pagina con una input box, un pulsante ed una lista.

L’input box e il pulsante sono controllati dal controller First mentre la lista dal controller Second (si, lo so, poca fantasia… ); l’utente può inserire una stringa non vuota nell’input e, premendo il pulsante, aggiungerla alla lista che viene poi mostrata da Second.

Entrambi i controller First e Second hanno una dipendenza verso il service myService (sempre molta fantasia…), che mantiene una collezione delle stringhe.
In particolare i diversi attori:

FirstController

  1. Ha la proprietà nuovoDato, in bind con la input
  2. Espone il metodo addDato in bind con il pulsante; quando premuto, chiama il metodo addDato di myService, passando come argomento nuovoDato

SecondController

  1. Espone la funzione dati, in bind con l’elemento <li> tramite la direttiva ngRepeat. La funzione dati, al suo interno, richiama il metodo getList di myService, che ritorna la collezione di stringhe

MyService

  1. Ha la proprietà privata myData che mantiene al suo interno la lista delle stringhe (si tratta di un array, inizializzata con già un dato al suo interno “Dato già presente…”)
  2. Espone il modo addDato, che prende in ingresso una stringa e la aggiunge all’array myData
  3. Espone il metodo getList che ritorna la lista delle strighe (array myData)

Il tutto è molto semplice, come potete vedere, ma anche lineare; un unico punto in cui il dato viene mantenuto e aggiornato; i controller non fanno altro che chiamare i metodi del servizio senza doversi preoccupare di notifiche e gestione di eventi.

Tutto il codice client è scritto in TypeScript

Soluzione Visual Studio 2015

Share
 

Sono appena “caduto” su codice JS scritto in questo modo:

angular.element(document).ready(function () {
    // Fatto così è un casino da testare!!
    angular.module("ModuloPerCostanti", [])
        .constant("parametri",
        {
            costanteUno: $("#parameters").data("costante-uno"),
            costanteDur: $("#parameters").data("costante-due")
        });

    angular.module("ModuloPrincipale", ["ModuloPerCostanti", "ModuloPerServizi", "ModuloPerDirettive", "ngSanitize", "ngResource"])
        .filter("filtroUno", function () {
            return function (data) {
               //DoSomething
            }
        })
        .filter("formatPrezzo", function () {
            return function (data) {
                //DoSomething                
            };
        })
        .controller("ControllerPrincipale", ["$rootScope", "$scope", "$http", function ($rootScope, $scope, $http) {
                // DoSomething
        }])
        .controller("ControllerSecondario", ["$rootScope", "$scope", "$compile", "$http", "$window", "$timeout", "$filter", "$sce", function ($rootScope, $scope, $compile, $http, $window, $timeout, $filter, $sce) {
            //DoSomething
        });
});

Scritto in questo modo, non sarà testabile a meno di scatenare l’evento “documentReady” e far inizializzare i moduli, i controller, i filtri, le direttive etc… (cfr: http://bittersweetryan.github.io/jasmine-presentation/#slide-17)

Questo perché? Jasmine (framework JavaScript utilizzato per il testing) carica i moduli angular in questo modo:

angular(“ModuloPerCostanti”) // Cercherà ma non troverà il modulo ModuloPerCostanti

angular(“ModuloPrincipale”) // Cercherà ma non troverà il modulo ModuloPrincipale

Purtroppo Jasmine non troverà alcun modulo, perché quella parte di codice, che definisce i moduli, viene avviata solo durante l’evento document.ready.

Il codice è scritto in quel modo perché dipende dell’HTML della pagina, in particolare la parte di “constant” richiede che il documento sia completamente caricato (di fatto quelle non sono costanti… se fosse C# non ci permetterebbe mai di compilare come costante qualche cosa di non noto a compile time); tralasciando tutto questo, come renderlo testabile senza stravolgerlo? (tenete conto che quando bisogna “rendere testabile” un codice qualcosa non funziona; test a parte, significa che stiamo usando un approccio poco lineare difficile da capire, manutenere e non è detto funzioni sempre).

Basta un piccolissimo accorgimento,utilizzando la notazione dei NameSpace javascript, in modo da minimizzare possibli conflitti:

  1. Dichiariamo il nostro nameSpace
  2. Creiamo una funzione Init che contenga tutto il nostro codice, con la notazione dei NameSpace:
    var NostroNameSpace; // Dichiariamo ma non inizializziamo il namespace
    (function(tempNameSpace){ // Funzione anonima autochiamante
        tempNameSpace.Init = function(){ // al parametro tempNameSpace viene agganciata la funzione Init
        angular.module("ModuloPerCostanti", [])
            .constant("parametri",
            {
                costanteUno: $("#parameters").data("costante-uno"),
                costanteDur: $("#parameters").data("costante-due")
            });
    
        angular.module("ModuloPrincipale", ["ModuloPerCostanti", "ModuloPerServizi", "ModuloPerDirettive", "ngSanitize", "ngResource"])
            .filter("filtroUno", function () {
                return function (data) {
                   //DoSomething
                }
            })
            .filter("formatPrezzo", function () {
                return function (data) {
                    //DoSomething                
                };
            })
            .controller("ControllerPrincipale", ["$rootScope", "$scope", "$http", function ($rootScope, $scope, $http) {
                    // DoSomething
            }])
            .controller("ControllerSecondario", ["$rootScope", "$scope", "$compile", "$http", "$window", "$timeout", "$filter", "$sce", function ($rootScope, $scope, $compile, $http, $window, $timeout, $filter, $sce) {
                //DoSomething
            });
    };
    })(NostroNameSpace || (NostroNameSpace = {})); // Se NostroNameSpace non è già stato istanziato, viene qui inizializzato ad un oggetto vuoto, altrimenti sarebbe <em>undefined</em>
    
  1. Richiamiamo l’unica funzione NostroNameSpace.Init così: angular.element(document).ready(NostroNameSpace.Init); // Di nuovo usiamo il document ready

Cosa cambia?

In Jasmine, a questo punto, faremo così:

NostroNameSpace.Init();

angular(“ModuloPerCostanti”) // Cercherà e caricherà il modulo ModuloPerCostanti

angular(“ModuloPrincipale”) // Cercherà e caricherà il modulo ModuloPrincipale

Il codice è stato rifattorizzato in maniera molto semplice, ma in questo modo possiamo testare tutto (circa 5/10 minuti di refactor, più che altro per stare attenti a non introdurre errori).

Share
 
W

Pandilò

in Io, Pandilò , by LoreX75

Microimpresa Domestica in Ambito Alimentare

Da tanto non aggiorno questa pagina… negli ultimi mesi ho dedicato tutte le mie energie ad un progetto totalmente nuovo e “diverso”:

Pandilò

Pandilò è una Microimpresa Domestica in ambito Alimentare: dopo qualche anno a sperimentare ed entusiasmarmi al magnifico mondo dell’Arte Bianca, ho deciso di dare seguito alla passione e lanciarmi in questa impresa e così, lo scorso 16 Dicembre 2015, è ufficialmente nata.

Abbiamo iniziato in grande stile con la produzione del Grande Lievitato per eccellenza: il Panettone, esclusivamente a lievitazione naturale, declinato nella versione Tradizionale oppure con Fichi e Noci.

Il prossimo week-end daremo invece il via alle “Colombiadi 2016”: anche in questo caso, Colombe fatte lievitare con solo Lievito Madre. E’ un lavoro impegnativo, che richiede tempo e dedizione. Il Lievito Madre, quello vero (vivo e vitale), non si improvvisa ma va preparato a dovere: qualche giorno prima si inizia con almeno 2 rinfreschi quotidiani, poi arriva il grande momento in cui inizia il suo vero percorso per diventare Colomba: si parte con un “bagnetto” preparatorio con acqua e zucchero, cui seguono ben 3 rinfreschi ravvicinato per portarlo in forza ed essere pronto al primo impasto, che richiederà circa 12 ore di lievitazione. Il giorno successivo si procede con il secondo impasto; una volta terminato, verrà fatto un riposo, seguito da 2 pirlature e la formatura finale. E colombe lieviteranno ancora per circa 8 ore, poi verranno glassate e guarnite con granella di zucchero e mandorle (che abbiamo fatto arrivare apposta dalla Sicilia, per offrire il meglio senza compromessi!) e cotte in forno per poco meno di un’ora. Una volta cotte, le colombe verranno infilzate e capovolte per almeno 12 ore, al fine di avere un raffreddamento lento, senza fretta, per garantire il massimo del gusto. Dopo altre 3/4 ore di riposo, vengono infine confezionate e consegnate.

Oltre ai lievitati, non mancano le frolle, che non sono “solo frolle”, ma si trasformano in buonissimi biscotti adatti ad ogni momento, oppure crostate da gustare insieme alle persone con cui stiamo bene.

Tutti i nostri prodotti sono fatti in casa, a Correzzana, in Brianza, e garantiscono la massima sicurezza per ogni nostro cliente, perché Pandilò è a tutti gli effetti un’Azienda Alimentare e come tale rispetta tutte le norme di igiene e  sicurezza alimentare secondo la vigente normativa. Ogni volta che portate nella vostra casa uno dei nostri prodotti, portate anche un pezzo della nostra casa insieme a voi e, proprio per questo, abbiamo scelto il motto: “Fatti da noi, come a casa vostra”.

Pandilò è una microimpresa domestica associata a CucinaNostra

Logo Cucina Nostra

Share

If you got here, probably you have just tried to build OrchardCms and got this error:
The specified solution configuration “Debug|MCD” is invalid
Maybe your laptop is an HP? It happens that on HP Laptops the Environment variable Platform is set to MCD
The solution is very easy, indeed… just go to System Properties -> Advance System Configuration -> Environment Variables and then change the value to Any CPU

variabili ambiente

Done! You can now build OrchardCms with “Build precompiled” from Visual Studio command prompt
Solution found here

Share
 

I’ve just got this exception in App.xaml, just at the bootstrapper line:

<Application
 x:Class="MyNamespace.App"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
 xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
 xmlns:caliburnMicro="clr-namespace:MyNamespace.CaliburnExt">

 <!--Application Resources-->
 <Application.Resources>
     <local:LocalizedStrings xmlns:local="clr-namespace:MyNamespace" 
x:Key="LocalizedStrings"/> <caliburnMicro:Bootstrapper x:Key="bootstrapper" /> </Application.Resources> </Application>

 

After a few investigation, I found out the problem was in the botstrapper, and precisely here:

protected override void Configure()
{
     container = new PhoneContainer();

     if (!Execute.InDesignMode)
     {          
          container.RegisterPhoneServices(RootFrame);
     }     
Problem is here!!!!
     container.Instance<IMobileServiceClient>(new MobileServiceClient(
                   "https://myMobileervice.azure-mobile.net/",
                   "xxxxxxxxxxxxxxxx"));            
            AddCustomConventions();            
}

To fix the issue is sufficient to move the Mobile Service registration inside “if (!Execute.InDesignMode)”

The working code is the following:

protected override void Configure()
{
     container = new PhoneContainer();

     if (!Execute.InDesignMode)
     {          
        container.RegisterPhoneServices(RootFrame);
        container.Instance<IMobileServiceClient>(
new MobileServiceClient( "https://myMobileervice.azure-mobile.net/", "xxxxxxxxxxxxxxxx")); } AddCustomConventions(); }

 

Share
Tagged with:  

Ed eccola qui, finalmente! La mia prima App per Windows Phone è sul MarketPlace: si tratta della controparte smartphone per “La mia Dieta Dukan” (possibile che rilasci anche una versione per il vecchio OS Windows Phone 7.x… ci sto ancora pensando).
Cliccate qui per accedere al MarketPlace

Lore
SplashScreenImage

Share
Tagged with:  

Nuova App sul Windows Store… questa volta è dedicata alla Dieta Dukan, con una raccolta di ricette create da chi la dieta la segue (me compreso ;-))
Le ricette verranno man mano aggiunte… se volete pubblicare una vostra ricetta, contattatemi liberamente.
Ecco il link

Lore
Screenshot_143802_1000001

Share
Tagged with:  

Ho appena inviato alla certificazione la prossima release di Lavoriamo!.
Le novità sono molte, a partire dal restyling, più professionale e funzionale, fino alla possibilità di passare da un mese all’altro, di impostare l’ora di inizio per le attività e di cancellare le attività precedenti, inserire per errore.
Seguiranno dettagli appena l’App sarò approvata 😉

Share
Tagged with:  

Sto finendo il prim update per l’App Lavoriamo!
Sarà dedicato ad un fastidioso bug che era scomparso e poi è magicamente tornato quando ho pubblicato l’App nello store, senza che me accorgessi, ovviamente: entrando ed uscendo dalla schermata delle giornate a quella di dettaglio, le righe paiono duplicate. Fortunatamente è solo un bug “visuale”; dalle prove che ho fatto, infatti, i dati non vengono corrotti.
Dovrei riuscire a pubblicare questo update entro la corrente settimana, quindi dopo poco sarà disponibile sullo Store. Mi spiace per il disagio che avete provate, ma sarà corretto a brevissimo.
E’ un’App cui tengo molto, quindi arriveranno altri aggiornamenti che miglioreranno l’interfaccia, arricchendola di funzionalità e rendendola più gradevole, pur mantenendo semplice ed immediato l’inserimento delle attività.

29/11/2012 Update: Il bug fix è in certificazione presso Microsoft, dovrebbe essere rilasciato a breve…
30/11/2012 Nello Store: Il primo update è nello store

Share
Tagged with:  

Heggià, la 5 App è nello store, che vi aspetta.
Si tratta di un aggregatore di feed di giornali femminili, per essere sempre informate sulle novità ^_^
La potete trovare a cliccando qui

Share
Tagged with: