I have joined Anti-IF Campaign

Comment mettre en oeuvre une architecture orientée Model Driven pour Angularjs ?

Je développe actuellement une application avec Angularjs.
Pour cette application, je tente de relever deux défis techniques :

  • implémenter le principe du lazy loading pour une partie des données du modèle (je ne charge que ce dont j'ai vraiment besoin)
  • centraliser les interdépendances des données au sein du modèle (changer une donnée à des conséquences sur d'autres données du modèle)


Etant à la base un Java-iste, j'ai tout de suite pensé aux beans et aux getter setter.

Mais comment mettre en oeuvre ce genre de pratique en JavaScript ?

Après quelques recherches, je suis tombé sur une syntaxe que je ne connaissais pas :
Ce qu'il faut retenir c'est que cela permet de définir pour une propriété un getter et un setter (si celle-ci est modifiable).
Dès lors, les interactions avec cette propriété utiliseront automatiquement le getter ou le setter.
Cette syntaxe est compatible avec les navigateurs modernes (chrome, firefox, ie10, safari), je n'ai pas testé les navigateurs headless.
Vous trouverez dans les liens des articles expliquant cette syntaxe, dont un de l'excellant John Resig (Jquery) qui date de 2007.

Il nous faut maintenant intégrer cela avec Angularjs.

Adepte de la séparation en couche, je décide de crée :

  • une vue
  • un contrôleur en charge de l'interception des actions de l'utilisateur
  • un service qui représentera le modèle, l'accès aux données

La vue est très simple, utilise pleinement les possibilités d'Angularjs (expressions et directives) :
Le contrôleur (et la création du module) est aussi expéditif :
Le contrôleur possède deux dépendances : $scope et myService qui correspond au service que nous créerons plus loin.
Son fonctionnement, est relativement simple, il branche les données sur le scope courant afin de permettre à la vue de les présenter.
Il offre aussi un wrapper vers la fonction showData du service que nous verrons plus loin.

Reste le plus difficile : le service.
Rappelons nos cas d'utilisations :

  • je ne charge que ce dont j'ai vraiment besoin :
    je dois donc récupérer les données au moment ou on les demandes (en asynchrone tout de même)
    ces chargements doivent pris en compte par Angularjs et donc être visibles dans la vue
  • changer une donnée à des conséquences sur d'autres données du modèle
    ces modifications internes doivent prise en compte par Angularjs et donc être visibles dans la vue

Voici le code de notre service :
Dans ce service nous avons trois grosses parties :

  • les données internes réelles -> inData
  • une fonction interne réalisant un appel ajax -> loadData
  • l'interface publique du service permettant d'accéder à ces données -> outData et showData

"inData" est une simple structure JavaScript possédant trois propriétés.
Ce sont là les données que nous afficherons.
Notez que pour l'instant extData est "undefined", nous devrons l'initialiser quand la vue en aura besoin.
"cpt" devra quant à lui comptabiliser le nombre de modification effectuée sur "mytext".

"loadData" s'appuie sur $http d'Angularjs pour réaliser des appels ajax.
L'appel en question utilise un service bouchon "echo" proposé par jsfiddle.
Comme son nom l'indique, il renverra que qu'on lui à posté ("hello": 'world').

"outData" est l'interface publique d'accès aux données.
C'est cette structure qui met en place le principe des getter/settet dont nous parlions au début.
On proposera donc :

  • un getter et un setter pour "mytext"
  • un getter seulement pour "cpt"
  • un getter seulement pour "extData"

Le setter de "mytext" permet d'incrémenter "cpt" à chaque changement du texte.
Le getter de "extData" lui permet d'initialiser la valeur du champ si celle-ci n'est pas encore définie.
Pour être certain de ne pas provoquer de boucle à ce niveau en cas d'erreur ou de succès, nous utilisons la propriété elle-même : inData.extData = "loading ...".
Le chargement étant asynchrone, nous informons Angularjs de la mise à jour en utilisant : "$rootScope.$apply()".
Notons enfin que pour éviter de rentrer en conflit avec un cycle de rafraichissement d'Angulajs, on contrôle son état avant de lancer apply : $rootScope.$$phase.
Les nombreux logs vous permettront de suivre l’enchaînement de tous les appels.

Voici le code complet :

Conclusion :
Je comprendrais ceux qui tiqueront sur l'utilisation de apply depuis le modèle, l'utilisation d'événements pourrait permettre d'améliorer l'architecture.
C'est malgré tout une première implémentation d'une architecture centrée autour du modèle de données, j'espère que vous pourrez vous en inspirer.
La partie service peut de plus tout à fait être reprise hors Angularjs, à condition de remplacer $http et la fonction apply.

En tout cas, JavaScript devient vraiment de plus en plus attractif.

Merci au forum FRAngular et notamment à Thierry Chatel pour leurs précieux conseils.

Liens utiles :
http://angularjs.org/
Communauté Angularjs FR
jsfiddle
article sur les getter setter par John Resig
autre article de Mozilla

Aucun commentaire:

Enregistrer un commentaire