seedstack.org : la solution open source de PSA

seedstack_image


Un besoin permanent de productivité et de cadrage

Bien avant l’apparition du langage Java, PSA construisait déjà des frameworks permettant d’accélérer le développement logiciel et de répondre au mieux à ses besoins métiers. Lorsque Java est arrivé avec son lot de frameworks et d’outils, les frameworks PSA ont évolués pour devenir des couches d’intégration autour de frameworks existants et ainsi fournir des outils communs à l’ensemble de ses développeurs. Cependant, ce travail d’intégration d’outils qui n’ont pas été prévus pour fonctionner ensemble et difficiles à configurer a fini par montrer ses limites. De nombreux projets restaient bloqués sur des frameworks obsolètes parce que « montée de version » voulait dire réécriture complète du projet. Le manque de modularité se faisait cruellement sentir. De plus, même si les outils étaient communs (Spring, Struts, etc.), ils leur manquaient de l’opinion et des bonnes pratiques. C’est à dire qu’à chaque fois qu’un développeur changeait de projet il avait tout à réapprendre: organisation, conventions, etc…


Un programme d’entreprise

Début 2013, PSA a donc décidé de rassembler une équipe pour faire face à ce problème. Composée de deux architectes et de deux à huit développeurs selon le plan de charge (moitié PSA, moitié collaborateurs Infotel en mission pour PSA), l’équipe s’est donc attelé à la tâche : réécrire une solution complète nommée SeedStack. Dès l’origine du projet, des orientations structurantes ont été faites.


Des choix novateurs

Premièrement, la stack n’intègrerait pas Spring, comme les précedants frameworks. Elle aurait un noyau plus léger et plus modulaire pour éliminer le couplage. Le micro framework Nuun.io a été choisi pour d’adresser ces éléments grace à son architecture kernel/plugin. Il confère une grande agilité à SeedStack qui a pu grandir et se réorganiser depuis deux ans sans rencontrer aucun obstacle. Chaque plugin expose ses API (Application Programming Interface) qui servent de contrats avec les développeurs et facilitent ainsi les montées de version. Certains exposent également des SPI (Service Provider Interface) permettant aux développeurs d’étendre les fonctionnalités du framework et donc de répondre à tous leurs besoins.


Mettre le métier au cœur du développement et le cadrer

Deuxièmement, il a été décidé de promouvoir des styles d’architectures de type REST (Representational State Transfer) et DDD (Domain Driven Design). Le REST via ses API hypermedia permet aux applications d’exposer des ressources Web simples à utiliser, qui garantissent une compatibilité à long terme tout en permettant une grande évolutivité. Son support a été développé en se basant sur les spécifications JAX-RS (via Jersey) et HAL. Le DDD quant à lui permet de se recentrer sur le domaine métier. Il remet la conception au coeur du travail de développeur, alors qu’elle est trop souvent délaissée au profit d’architecture de type CRUD : correspondance 1-1 entre de la base de données et l’IHM. Aucune solution satisfaisante n’étant disponible pour son implémentation, il a été décidé de développer un nouveau framework adressant la partie tactique du DDD.

Enfin, pour la partie Web, un framework a été développé pour combiner le meilleur d’AngularJs, RequireJs, et Bootstrap. L’utilisation du REST côté serveur et d’une SPA (Single Page Application) côté UI permet ainsi, le découplage du front et du back qui peuvent maintenant avoir des cycles de vie séparés. Par ailleurs, ce framework Web n’est pas en reste côté modularité car il permet via un système de fragments de composer son application à partir de modules indépendants.


Une suite Open Source

Suite à ces choix SeedStack a connu un départ rapide et les gains en productivité ne se sont pas laissés attendre. La première version a été finalisée après seulement 6 mois de développement. Dès lors, elle a été intégrée par des projets pilotes, qui ont permis à la solution de se développer et de s’affiner. Avec un rythme de 3 versions par an, SeedStack a connu des évolutions fréquentes. Début 2015, une quarantaine de projets (dont la moitié en production) utilisaient déjà cette stack. Conscient de l’intérêt de sa solution, PSA a alors décidé de publier SeedStack sous une license libre : la MPL 2.0 (Mozilla Public License 2.0). Après un audit juridique et technique ainsi qu’un peu de refactoring, la stack a été publié sur GitHub en version béta en mai 2015. Puis, après quelques mois de travail une première version open source a été publiée en Juillet. Loin d’achever cette « success story », la mise en open source lance de nouveaux défis : tout d’abord continuer d’améliorer la stack, mais aussi communiquer et trouver de nouveaux partenaires.

Des premières contributions extérieures ont été enregistrées et je vous encourage tous à venir participer. Il n’y a pas de petites contributions ! Que ce soit de la documentation, des rapports de bug ou du code, toutes les contributions sont les bienvenues.



Premier pas dans l’internet des objets avec les Beacons

Nous voulons partager avec vous notre projet Beacon tout juste sorti par notre équipe Innovation.

L’idée est de pouvoir accompagner nos clients dans leurs projets disruptifs et force est de constater que l’Internet des Objets (IoT) est au cœur des enjeux du moment. L’annonce de Google concernant le ‘Web Physique’ (https://github.com/google/physical-web/blob/master/documentation/introduction.md ) ne va pas démentir cette observation.


Toujours plus contextualisée et personnalisée, l’information va vivre au contact des balises Beacons, tags NFC ou autre. Notre projet est de démontrer le potentiel de ces technologies en associant une application mobile avec une solution Web qui officie à la fois comme pilote des fonctions contextualisées et réceptacle du flot de données généré (…Big Data, le mot est lâché).


Fonctionnement schématique de la solution

Fonctionnement schématique de la solution



Première étape, l’acquisition de la balise et sa configuration.

On trouve des sites qui offrent des packages prêts à l’emploi, il faut ensuite associer paramétrer les quelques informations qui seront émises par notre balise.

  • UUID : c’est l’identifiant unique de notre balise, modifiable et donc personnalisable
  • Major : un des paramètres modifiables; on pourra y associer par exemple un numéro de magasin
  • Minor : autre paramètre, qu’on peut penser comme une précision apportée à un ensemble de balises reliées par la valeur d’un ‘Major’, comme le rayon d’un magasin
Balises utilisées pour le projet d'Infotel

Deuxième étape, créer l’application qui va capter le signal et prendre la décision appropriée :

Prérequis, il faut que votre téléphone embarque la prise en charge du BlueTooth Low Energy, qui est aujourd’hui disponible pour les smartphone sous iOS, Android 4.3 et Windows 8.
Une fois maîtrisée la détection du signal, on crée différentes interactions, différenciées selon que l’application est en veille ou en premier plan.

Capture Alerte iBeacon Catpure notification présence iBeacon
Envoyer une alerte ou un message associé à la proximité d’une balise Lorsque l’application est en veille, les notifications sont possibles, de manière plus ou moins intrusive.
Pour une interaction plus riche, on propose l’affichage d’un contenu HTML qui est fourni par notre serveur, mais on pourrait également constituer un message si l’application mobile embarque la logique (catalogue de produit embarqué, envois de messages en mode chat).

Voici le périmètre de notre solution :

  • Créer une application qui capte les signaux des Beacons,
  • Dans une approche connectée, envoyer au serveur les informations techniques de la balise associée à son identifiant,
  • Recevoir en retour l’action à déclencher qui se peut prendre une des valeurs suivantes :
    Aucune action : nous voulons simplement enregistrer le passage de notre usager,
    Réveil de l’application,
    Affichage d’une notification,
    Affichage d’un contenu Web (dans une WebView).
  • Filtrer les événements pour ne pas prendre en compte les signaux qui ne sont pas directement liés à notre parc de balises,
  • Afficher un sémaphore de présence de balises.


Par ailleurs, nous avons ajouté quelques fonctionnalités pour l’administration du parc : historique des signaux captés, débogage avec visualisation des informations techniques, configuration, et comportement par défaut au travers de ‘bouchons’ pour simuler une connexion à notre serveur.


Troisième étape, piloter son parc.


Il faut ensuite associer du contenu et des comportements à chaque balise, ce que nous faisons dans notre application centrale. En voici quelques captures d’écran représentatives.

Paramétrage d'une balise

Paramétrage d'une balise et association d'actions

création d'une action

Création d'une action type

Création d'une action générique

Visualisation d'une événement lié à une balise



Quatrième étape, analyser et améliorer.


Pour faire vivre la donnée et comprendre les comportements relevés par nos balises, rien de mieux que d’utiliser la masse d’information correspondant aux événements en les déversant dans ElasticSearch (par exemple).

On crée ainsi des tableaux de bord dans lesquels on décline les différentes actions, avec une visualisation temporelle.

On peut ensuite se pencher sur une classification et une analyse plus poussée.



BackboneJS et développement mobile : trucs et astuces (…fin)

Voici la suite (et fin) tant attendue de notre série sur le développement avec BackboneJS, framework JavaScript que nous utilisons pour nos créations de sites web / mobile HTML5, quand nous n’optons pas pour AngularJS.

Certaines bonnes pratiques sont communes à tout projet HTML5, notamment les techniques de responsive design, gestion des dépendances JavaScript et outillage des tests, ce qui constitue notre menu du jour :

Organisation des librairies

Require

Il s’agit de l’implémentation de l’API AMD la plus répandue. Elle permet de gérer les dépendances et chargement dynamique des scripts.
Pour démarrer, voici un lien très utile :
http://www.ringabell.org/un-simple-guide-pour-debuter-avec-requirejs/

Points d’attention

Nous avons fait face un certain nombre de fois des comportements étranges dans notre application qui étaient en fait du à des erreurs bêtes liées à l’utilisation de Require. Un bon réflexe quand vous ne comprenez absolument pas pourquoi votre code ne fait pas du tout ce qui est attendu est de vérifier les points suivants :

  • ne pas oublier le return avec le nom du module à la fin de la déclaration du dit module
  • l’ordre de déclaration des dépendances vers les modules qui doit correspondre exactement aux paramètres de la fonction (attention aux décalages par inadvertance!)

Optimisation

Bien que nous ne l’ayons pas utilisé dans notre projet, il peut être intéressant de mentionner que Require JS propose également un outil pour optimiser le chargement des scripts en concaténant et minifiant tous les fichiers JS nécessaires au démarrage de l’application en un seul fichier : http://requirejs.org/docs/optimization.html
Et pour un tutoriel en français sur le sujet : http://www.ringabell.org/requirejs-une-utilisation-plus-poussee/

Optimisation des performances

Dans le cas où nous travaillons avec une seule page HTML, on peut généralement la découper en différentes portions qui correspondront chacun à des vues Backbone à l’intérieur d’une vue globale (la page).
Il paraît alors préférable, dans la vue globale, de garder des références vers les sous-vues et d’éviter, quand c’est possible d’instancier de nouvelles vues à chaque changement dans un modèle sous-jacent. Il est possible d’associer un nouveau modèle (ou un modèle modifié) à une vue avant de la rafraîchir (via #render).

Var MyView = Backbone.View.extend({
 
 
    initialize: function() {
	    ...	
	    this.subView1 = new SubView1 ({model : modelA}) ;
 	   ...
    }
 
       refresh : function() {
	...	
      	this.subview1.model = myNewModel ;
	this.subview1.render() ;
       }
});

A lire également, pour éviter quelques erreurs qui peuvent être à l’origine de fuite de mémoire si on ne fait pas attention. Y sont notamment abordés le binding d’événements dans une vue et le rendering d’une collections de vues :
http://ozkatz.github.io/avoiding-common-backbonejs-pitfalls.html

Responsive Design

Les techniques de Responsive Design permettent d’assurer que l’affichage d’un site s’adapte correctement aux dimensions et résolution du support, que ce soit un ordinateur, une tablette ou un téléphone mobile.

Utilisation des media queries

Les Media Queries CSS3 sont une spécification du W3C qui permettant de définir l’application de feuilles de styles en fonction des périphériques (média). Mais elles vont bien au-delà de ce qu’il était possible de faire avec CSS2 où on pouvait définir une feuille de style différente pour l’impression par exemple puisqu’on peut créer des règles très précises qui prennent en compte, en plus du type de média, la largeur ou la hauteur de l’écran, l’orientation (portrait ou paysage), le support de la couleur etc. (voir : http://reference.sitepoint.com/css/mediaqueries#mediaqueries__tbl_media-queries_media-features)

Comme l’écriture d’une Media Query correspond à une expression logique, évaluée à vrai ou fausse, on peut tout à fait combiner ces différents paramètres, ce qui s’avère très pratique pour le design de sites mobiles.
Par exemple, pour un style spécifique à l’iPad :

/* iPads (portrait and landscape) ----------- */
@media only screen
and (min-width : 768px)
and (max-width : 1024px) {
/* Styles */
}

On trouvera un squelette de CSS avec Media Queries pour débuter avec le Responsive Design pour mobiles à l’adresse suivante : http://www.paulund.co.uk/boilerplate-css-media-queries
Les Media Queries sont supportées par la plupart des navigateurs récents (voir http://caniuse.com/css-mediaqueries pour un tableau récapitulatif des compatibilités)

Meta-balise « viewport »

Le « viewport » désigne normalement la surface d’affichage de la fenêtre du navigateur.

Sur un navigateur de bureau, un pixel écran correspond à un pixel CSS.

Mais comme expliqué dans l’excellent article, http://www.alsacreations.com/article/lire/1490-comprendre-le-viewport-dans-le-web-mobile.html, la plupart des terminaux mobiles ont une surface « réelle » qui correspond aux nombre de pixels physiques composant la matrice de l’écran et une surface « virtuelle » en pixels CSS, plus connue sous le terme de device-width/device-height et sur lequel se base en fait le terminal pour afficher les pages.

Et comme on peut le voir sur le site http://screensiz.es/phone , surface réelle et virtuelles ne correspondent pas forcément !

Pour couronner le tout, sur les terminaux mobiles, la valeur par défaut du « viewport » peut ne correspondre ni à l’un ni à l’autre… et dépend en fait du navigateur.
C’est ce qui explique les différences de zoom initial à l’ouverture d’une même page.
Pour s’assurer du bon affichage de son site quel que soit le navigateur, on peut recourir à la meta balise « viewport ».
Son attribut content offre la possibilité de fixer la largeur de viewport à la valeur souhaitée

<meta name="viewport" content="width=320">

Ou de l’adapter automatiquement à la valeur de device-width du terminal.

<meta name="viewport" content="width=device-width">

A noter enfin que cette balise ne fait partie d’aucune spécification, car elle a été introduite par Apple. Mais elle est généralement prise en compte par les autres navigateurs.
Il existe en fait un équivalent « officiel » du W3C sous forme de règle CSS @viewport, mais qui n’est malheureusement pas encore supporté par tous.

Quelques liens complémentaires forts intéressants pour démystifier le concept de viewport :

Tests

Emulation mobile sous Chrome

Les outils de développement Chrome permettent de simuler les événements tactiles (touch events) propres aux terminaux mobiles et qui n’existent normalement pas sur un navigateur de bureau.
Plus d’informations sur la page : https://developers.google.com/chrome-developer-tools/docs/mobile-emulation?hl=fr
On voit qu’en plus du support des touch events, les Chrome Dev Tools permettent également de simuler des tailles de viewports, l’orientation, différentes vitesses de connexion réseau, qui sont des spécificités propres aux mobiles qu’il faut prendre en compte.

Débugger une page dans Chrome pour Android depuis l’ordinateur

Il est possible de débugger les pages affichées dans le navigateur Chrome d’un téléphone Android connecté via un câble USB directement depuis un ordinateur. Pour cela, il faut :

  • Chrome 28+ sur l’ordinateur (tout OS) avec l’extension ADB installée
  • Chrome 28+ pour Android
  • Activer le débogage USB sur le téléphone

La procédure d’installation détaillée officielle est expliquée sur cette page : https://developers.google.com/chrome-developer-tools/docs/remote-debugging#remote-debugging
A noter que sur Windows uniquement, il faut impérativement installer le driver USB correspond au modèle du téléphone.
Plus d’informations sur ce lien : http://developer.android.com/tools/extras/oem-usb.html

Quant aux drivers pour les terminaux Google (Nexus…), ils sont téléchargeables sur la page suivante : http://developer.android.com/sdk/win-usb.html
Même en suivant point par point ces instructions, il se peut qu’un téléphone connecté ne soit pas reconnu directement par le plugin de Chrome. Ce fut le cas pour nous (Chrome 30 sur Windows 7 et Android + téléphone Nexus 4), il est alors nécessaire d’installer quand même ADB sur l’ordinateur.

  • Télécharger le SDK Android : http://developer.android.com/sdk/index.html
  • Décompresser l’archive dans le répertoire de votre choix
  • Ouvrir une invite de commande dans /platform-tools : adb devices
  • et vérifier que le téléphone est bien listé parmi les terminaux connectés

A ce stade, le plugin ADB de Chrome de l’ordinateur devrait proposer le téléphone dans : chrome://inspect/
et il est alors possible d’inspecter toute page ouverte dans le navigateur du téléphone, avec les mêmes outils de développement que pour la version web.

Débugger une page dans Firefox Mobile pour Android depuis l’ordinateur

A l’instar de Chrome, il est possible de débuguer une page ouverte dans Firefox Mobile à partir de la version Desktop du navigateur, comme expliqué sur la page « Remote Debugging » de Mozilla :
https://developer.mozilla.org/en-US/docs/Tools/Remote_Debugging
Les prérequis sont :

  • Avoir activé le débogage USB sur le téléphone Android (et éventuellement le driver USB pour Windows, voir le paragraphe sur Chrome)
  • Avoir activé le débogage distant dans le navigateur Firefox Mobile du téléphone (Paramètres > Outils de développement > Débogage distant)
  • Pouvoir utiliser ADB sur son ordinateur (voir la procédure d’installation résumée plus haut pour Chrome)

Pour se connecter depuis l’ordinateur au téléphone mobile, il faut rentrer la commande ADB suivante :

adb forward tcp:6000 tcp:6000

puis autoriser la connexion entrante sur le téléphone Android.
Malheureusement, il n’est pas possible pour l’instant, via le débogage distant d’utiliser les outils habituels du panneau « Inspecteur » pour inspecter les éléments, éditer du code HTML et des styles CSS à la volée. D’après la documentation, cette fonctionnalité sera rendu accessible avec Firefox 26.



BackboneJS : trucs, astuces & bonnes pratiques (part 2)

Vues

Passer des arguments à la construction d’une vue

Nous pouvons avoir besoin de passer des arguments, en plus du modèle, lors de la construction/initialisation d’une vue Backbone. Par exemple, une référence sur la vue parente.
On peut alors être tenté de faire quelque chose comme

new ServiceView ({parentView: this.myParentView, model: this.myModel })

mais on se rend vite compte avec un this.myParentView dans le code de la vue ne nous donne rien. parentView est en fait attaché à l’objet options de la vue lors de la construction de cette dernière.
En effet, d’après la documentation, seuls certains paramètres comme par exemple model et collection sont accessibles directement.
There are several special options that, if passed, will be attached directly to the view: model, collection, el, id, className, tagName, attributes and events.

Pour tous les autres, il faudra donc écrire :

// Instanciation de la vue
 
new ServiceView({
    model: serviceModel,
    parentView: this.parentView
});
 
// A l'initialisation, dans ServiceView
initialize: function() {
    this.parentView = this.options.parentView;
    // par la suite, on pourra récupérer la référence avec this.mainView;
}

Collection de modèles et listes de vues

On trouvera à travers les exemples sur le net plusieurs façons de créer un ensemble de vues à partir d’une collection Backbone.
Nous avons adopté l’approche suivante, qui nous paraît propre et facile à comprendre :

// Modèle
 var ProduitModel = Backbone.Model.extend({ }) ;
 
// Collection 
var ProduitsCollection = Backbone.Collection.extend({
 
        model: ProduitModel,
 
        }
});
 
// vue Liste
var ListeProduitsView = Backbone.View.extend({
 
        el: '#listeProduits',
 
        initialize: function() {
 
            this.collection.bind('reset', this.render, this);
 
            this.collection.bind('add', this.addItem, this);
 
        },
 
        render: function() {
 
            this.$el.empty();
 
            this.collection.each(this.addItem, this);
 
            return this.$el;
 
        },
 
 
        addItem: function(item) {
 
            this.$el.append(new ProduitView({
 
                model: item,
 
            }).render());
 
        }
 
 });
 
 
// Vue produit
var ProduitView = Backbone.View.extend({
 
      initialize: function() {            
 
            this.template = _.template(tmpl);
 
            _.bindAll(this, 'render');
 
            this.model.bind('change', this.render);
 
            this.render();
 
      }, 
 
render : function() {
 
            this.$el.html(this.template(
 
                this.model.toJSON()
 
            ));
 
            return this.$el;
 
    	}
 
});
 
//Nous pouvons directement créer une collection de produits correspondant au JSON suivant
{
  "ListeProduits": {
    "Produit": [
      {
        "CodeProduit": "P1",
        "Prix": "345"
      },
      {
        "CodeProduit": "P2",
        "Prix": "637"
      },
      {
        "CodeProduit": "P3",
        "Prix": "890"
      }
    ]
  }
}
 
// On remarquera que pour que le mapping collection / modèles corresponde exactement au JSON, il faut se placer au niveau de l'élément « enfant » qui correspond au modèle (ici 'Produit' et non pas 'ListeProduits')
var produitsCollection = new ProduitsCollection(data.ListeProduits.Produit);
 
 
// création de la liste de vues à partir d'une collection 
var listeProduitsView = new ListeProduitsView({collection: produitsCollection});
 
listeProduitsView.render();

Ecoute des événements JQuery

Tout comme une vue Backbone peut réagir aux événements survenant sur le modèle associé, elle peut évidemment réagir aux interactions utilisateurs et aux événements JavaScript/JQuery comme un clic, un cochage de checkbox…

Les associations avec des fonctions de callback se font dans ‘events’

MyView = Backbone.View.extend({
    events: {
        'click .item': 'handleClick'   // name of the event : name of the callback function
    },
 
    handleClick: function(e) {
        this; // The view instance
        e.target; // The element that was clicked 
        e.currentTarget; // The element that was bound by the click event
	    this.model.set({someValue : jQuery(e.currentTarget).val())}); 
    }
});

Templates

Associer un template à une vue/ un modèle à l’aide du plugin text.js de Require

Require contient un plugin « text.js » qui nous permet d’associer des templates soit aux vues soit aux modèles (exemple : modèle des requêtes soap). Pour pouvoir les charger, il faut procéder aux étapes suivantes :
– Editer la configuration de requireJS dans le main.js avec la ligne suivante :

 require.config({
   paths : {,
  "text" : "vendor/require/plugins/text" 
 
   }
});

– Exemple d’utilisation de require et plus particulièrement de la définition du paramètre qui permettra de récupérer le template

define([
    "jquery",
    "underscore",
 	    "backbone",
    "text!templates/html/templatePack.tpl"
 //Attention :  il faut préciser l’extension du fichier avec le plugin !  
], function($, _, Backbone, tmpl) {});
-	Et enfin, association de la vue ou du modèle avec le template
this.template = _.template(tmpl);

Ces fichiers sont chargés via des appels asynchrones XMLHttpRequest (XHR). Internet Explorer 9 et versions inférieures ne supportent pas cette technologie. Ils implémentent la technologie XDomainRequest. Par conséquent, pour résoudre ce problème, il a fallu que nous téléchargions un plugin javascript jquery.xdomainrequest.min et qui est chargé via RequireJS.

Variables indéfinies dans le modèle

Si on cherche à afficher dans un template un attribut qui n’est pas défini dans le modèle cela provoque une erreur.
Il faut donc soit faire une vérification préalable de type :

if (typeof myVar !== "undefined") {
    <div><%= myVar %>"</div>
}

Soit initialiser les attributs du modèle qu’on souhaite afficher avec des valeurs par défaut pour être plus prudent :

var MyModel = Backbone.Model.extend({
   defaults : {
    myVar : ‘’
    anotherVar : ‘’   
  }, 
});

Itération dans un template

Il est tout à fait possible d’utiliser des fonctions utilitaires d’Underscore à l’intérieur des templates. Par exemple pour itérer sur un tableau à l’intérieur dans un JSON :

{
  "ListEntryVarValues": {
    "EntryVarValue": [
      "12",
      "24",
      "36",
      "48",
      "60",
      "72"
    ]
  }
}
 
<% _.each(ListEntryVarValues.EntryVarValue, function(variable){ %>
 
    <input type="radio"
<% if (variable === Value)  { %> checked="checked" <% } %> name="radioGroup-<%=ShortDescriptionVar%>" value="<%= variable%>"/><label for="<%=ShortDescriptionVar%><%= variable %>">
<%= variable %> </label>
 
<% }); %>

Utilisation de fonctions

Dans un template, « this » fait référence à la Vue Backbone associée.
Ainsi, on peut donc tout à fait définir des fonctions dans la Vue Backbone et les utiliser ensuite à l’intérieur du template.

var MyView = Backbone.View.extend({ 
    dummyConcatFunction :function(name1, name2 ){
 
        return name1 + '':'' + 'name2 ; 
    }
});

// Appel dans le Template correspondant :

<div><%= this.dummyConcatFunction (nom1, nom2) %> </div>

Attention ! A l’intérieur d’une boucle ‘each’, ‘this’ ne fait plus référence à la vue Backbone. Une solution qui peut être adoptée :

<% var concatFunction = this.dummyConcatFunction ;
        _.each( variable, function(aVar){ %>                               
        <tr>
            <td> concatFunction(aVar.Value) %> </td>           
        </tr>

A suivre …



BackboneJS : trucs, astuces & bonnes pratiques (part 1)

Nous entamons ici une petite série d’articles techniques en retour des développements de site Web / Mobile responsive.

Pour commence : Quelques bonnes raisons de choisir Backbone

Structuration du code et découplage

Backbone est un framework JavaScript MVC qui permet de mettre en place rapidement une structure d’application où modèles et vues sont bien découplés et ainsi d’éviter le piège du code spaghetti d’une page web avec AJAX rafraîchie à grand coups de callback Jquery dans tous les sens.
Avec cette approche, il devient plus facile de maintenir synchronisées les données et leurs représentations, en réponse aux interactions de l’utilisateur.

Une forte communauté

Le framework jouit également d’une forte popularité ces derniers mois, soutenue par une grande communauté de développeurs. A la moindre difficulté, il sera facile de trouver de l’aide sur Stackoverflow.com ou autres.

Un moteur de templates intégré

De plus, il intègre nativement un moteur de templating, Underscore (qui est en fait sa seule dépendance forte) et se veut facile à intégrer avec d’autres bibliothèques JavaScript (comme JQuery)

Une prise en main rapide

Il est relativement aisé de démarrer avec ce framework étant donné qu’il y a peu de concepts-clés / composants à connaître : Modèles et Collections, Vues et Routeur.

….mais aussi quelques inconvénients

Complétude de Documentation

Cependant, nous avons trouvé que la documentation officielle de l’API n’était pas forcément des plus claires et manquait parfois d’exemples concrets. On voit ainsi souvent le paramètres « options » dans l’appel de fonctions, sans savoir exactement à quoi peuvent bien correspondre ces fameuses options.

Bonnes pratiques ?

Backbone revendique haut et fort sa flexibilité et la liberté laissée par le framework pour les développements, « There’s more Than One Way to Do it »… mais justement, n’y a-t-il pas trop de façons de faire ? En parcourant les tutoriels sur internet, on se rend en effet vite compte que chacun fait un peu à sa sauce, par exemple dans l’association (binding) entre vue et modèles ou l’orchestration des vues et des événements.
On en vient donc à se demander s’il existe vraiment de « bonnes pratiques » de développement avec Backbone, si l’utilisation qu’on fait du framework permet vraiment d’en tirer parti ou si on passe complètement à côté des possibilités qu’il offre.
En se voulant minimaliste et non-contraignant, Backbone laisse en réalité beaucoup de réflexions sur des points d’architectures à la charge du développeur… ce qui peut être vu par certains comme une force alors que d’autres y verront une limitation par rapport à d’autres fraweworks JavaScript qui cadrent davantage le développement.
En ce sens, il faudra explorer des extensions à Backbone tels que Chaplin (http://chaplinjs.org/) ou encore Marionette (http://marionettejs.com/) qui permettent de répondre à des problématiques plus complexes ou récurrentes (par exemple, gestion de la mémoire, gestions des événements) et de façon bien plus structurée.

Trucs & astuces concernant les ‘Models’

Getters: A.get(‘attribute’) VS A.attribute

Une étourderie qui nous coûte souvent une erreur de type « variable is undefined » quand on travaille à la fois avec des modèles Backbone et de simples objets JavaScript est d’oublier que pour accéder aux propriétés d’un modèle Backbone, il faut utiliser model.get(‘attribut’) et non pas model.attribut.

Il en va de même pour les setters.

Les différences entre le modèle et un objet JSON est par ailleurs très bien résumée dans le schéma de la page : http://www.atinux.fr/2012/04/15/backbone-js-le-fonctionnement-des-modeles/

On remarquera toutefois qu’il est tout à fait possible qu’un attribut d’un modèle Backbone corresponde en fait lui-même à un objet JSON, pour lequel la notation avec un point peut s’appliquer.
Par exemple, pour le modèle qui correspond au JSON suivant

{
  "Site": {
    "Login": "TOTO",
    "Password": "superpasswordsecure"
  }
}

Si on veut récupérer la valeur de Password, il faudra écrire : model.get(‘Site’).Password

Itération sur une collection Backbone

Nous pouvons utiliser la fonction each d’Underscore pour parcourir une collection :

produitsCollection.each(function(produit) {
    console.log(produit.get('name')) ;
});

Modifier un attribut de modèle sans déclencher l’événement « change » sur ce dernier

Dans quelques (rares) cas, il peut être utile de modifier un des attributs du modèle sans pour autant propager l’information que le modèle a changé (« change » event). Par exemple si on ne souhaite pas mettre à jour immédiatement la vue associée.
Pour cela, il faut mettre l’option silent à true :

this.model.set({headerValue: "lalala"}, {silent: true})

Redéfinition de fetch et parse

Il peut être utile de redéfinir la méthode fetch d’un modèle Backbone, par exemple pour utiliser les toujours les mêmes options lors d’un appel serveur.
Les options possibles sont celles de jQuery Ajax – http://api.jquery.com/jQuery.ajax/

var ServiceModel = Backbone.Model.extend({
        fetch: function(options) {
            options = options || {};
            options.method = "POST"; 
            options.data = 
            options.headers = {  "Content-Type" : "text/xml"  };
            };
 
            return Backbone.Model.prototype.fetch.call(this, options);
 
        } 
});

Rappelons que fetch s’exécute par défaut de façon asynchrone. De ce fait, pour pouvoir créer ou mettre à jours des vues avec les données récupérées du serveur, il faut le faire dans la méthode de callback success (il existe par ailleurs son pendant error pour la gestion d’erreur)

var MaView = Backbone.View.extend({
var self = this;
serviceModel.fetch({
       success: function() {
self.subview.render();
       },
       error: function(xhr, textStatus, options) {
            self.showError();
      console.log("Erreur sur serviceModel :" +      JSON.stringify(options.xhr));
       }
});
 
});

On remarquera qu’à l’intérieur de la function success, « this » ne fait pas référence au modèle. Une technique communément admise est de conserver la référence sur dans une variable extérieure par exemple var self = this.

Parse est appelé systématiquement après chaque fetch et save. L’implémentation par défaut se contente de retourner la réponse brute telle que reçue du serveur, mais il peut être utile de redéfinir la fonction pour appliquer un traitement sur ces données avant de les utiliser dans le modèle. Par exemple, si on reçoit un flux xml, le convertir en JSON.

parse: function(data) {
// data est la réponse telle que renvoyée par le serveur           
      var x2js = new X2JS();
      var result;
      result = x2js.xml_str2json(data);
      return result;
},

La méthode doit retourner un objet JSON qui décrit les attributs à setter sur le modèle.



à suivre…