Mustache, Templating library, jQuery, JSON – Utiliser Mustache comme système de template javascript pour une API sortant des flux JSON


Dans un précèdent billet, on avait vu comment utiliser le plugin de jQuery Template pour se faciliter le travail de génération de pages/écrans/IHM depuis des flux JSON. En effet, pour faire valider des écrans par le métier, mieux vaut présenter un aperçu intuitif dans le même esprit qu’une maquette Photoshop ou d’un wireframe fait avec des outils comme Axure, Balsamiq ou Omnigraffle. A la différence, que l’exercice peut se révéler périlleux car cette fois-ci les pages ne sont pas statiques comme sur une maquette HTML mais vont être alimentés par de véritable contenu émanant d’un API sous la forme des flux JSON. Il faut entendre par le métier : un client, un responsable marketing digital, un responsable éditorial, un DA…

C’est là où le recours à un framework javascript de génération de template peut-être d’une grande aide. C’est en ce sens que nous allons utiliser Mustache - Templating library

JSON vs IHM

On ne va pas faire valider des flux JSON quand bien même ces flux peuvent être mis en forme par une extension comme JSONview et sont le « coeur » de la génération des écrans. C’est tout le fossé presque culturelle qui existe entre le monde du développement et d’autres départements (marketing, éditorial, design) qui travaillent aussi à la création d’une application ou d’un site. Mieux vaut une représentation UX (des écrans) des règles fonctionnelles, compréhensibles par tous.

Pour se rendre compte du fossé entre les flux JSON et de véritables IHM, deux captures suffisent, l’une du flux JSON brut, l’autre de l’écran, rapidement maquetté à l’aide de Mustache de ce même flux.

Chargement non-UX du flux JSON à l’aide de JSONview
Mustache, Templating library, jQuery, JSON - Utiliser Mustache comme système de template javascript pour une API sortant des flux JSON

Chargement UX du même flux JSON à l’aide de Mustache
Mustache, Templating library, jQuery, JSON - Utiliser Mustache comme système de template javascript pour une API sortant des flux JSON

Dans notre article, on ne sert de Mustache que comme d’un outil véloce de maquettage pour des flux JSON mais cela va sans dire Mustache peut-être aussi la solution choisie, en production, pour générer véritablement les écrans.

Comme dans l’article précèdent, une précision s’impose, vous pourriez rencontrer un problème lorsque vous essayez de lire le document JSON qui alimente vos écrans si vous ne mettez pas ce code sur un serveur. En effet, pour des raisons de sécurité un navigateur comme Google Chrome ne permet pas de charger des fichiers externes sur les dossiers locaux. Si vous devez charger un fichier .json en local, le mieux est donc d’utiliser firefox, safari ou tout simplement charger le fichier appelant la source de données au format .json sur un serveur en local type WAMP, MAMP ou EASYPHP.

Utiliser mustache pour rendre un flux JSON

Dans notre template, on se montre volontairement prolixe pour ce qui est des boucles pour être sûr de savoir ce qui est effectivement appeller depuis le JSON

OUTPUT – Le rendu du flux JSON à l’aide de Mustache
Mustache, Templating library, jQuery, JSON - Utiliser Mustache comme système de template javascript pour une API sortant des flux JSON

INPUT – La source du flux JSON, relativement complexe.
Mustache, Templating library, jQuery, JSON - Utiliser Mustache comme système de template javascript pour une API sortant des flux JSON

Détails du template de mustache

 
  <script id="content-template" type="text/template">
 
  <ol>
  	{{#result}}
  		{{#people}}
  			{{#person}}
        <li>
  			{{#image}}
  								{{#code}}
  								IMG ({{code}}) => <img src="{{url}}" /><br>
  								{{/code}}
  								{{^code}}
  								no img
  								{{/code}}
  			{{/image}}
 
 
  				TITLE => <a href="{{url}}/" title="{{title}}">{{title}} </a>
  				<br><br>
 
 
  					BODY escaped HTML => {{{body}}}
  					<br><br>
 
  					BODY unescaped HTML => {{body}}
  					<br><br>
 
            TAXONOMY => 
  						{{#taxonomy}}
  							{{#tags}}
  								<span class="tag"><a class="no-decoration" href="{{tid}}">{{name}} ({{tid}})</a></span>
  							{{/tags}}
  						{{/taxonomy}}
  			    <br>
  			    GEOLOC => 
        		{{#geoloc}}
        		  LAT, LONG => ({{latitude}}, {{longitude}})
        		  <br>
        		      {{#address}}
                                 GEOLOC => CITY, COUNTRY => <span class="tag">{{locality}} -[{{country}}]</a></span><br>
              		{{/address}}
        		{{/geoloc}}
        </li>
  			{{/person}}
  		{{/people}}
  	{{/result}}
  </ol>	
 
  	</script>

Pour comprendre la fonctionnement du template, beaucoup d’information sont disponibles sur le site de mustache de github.

Source : http://mustache.github.io/mustache.5.html

Par exemple BODY => {{{body}}} fait que mustache va interpréter les balises html de la variable du JSON body.

Tous les éléments pour faire fonctionner l’ensemble

Il y un fichier HTML, CSS, un fichier JS et des fichiers JSON qui sont la source de données. On a placé le tout dans différents répertoires : assets, css, js, json. On a intentionnellement mis deux fichiers JSON de source de donnés assez compliqués pour véritablement simuler le fonctionnement d’une API qui remonte des donnés complexes et pas seulement 3 données qui se battent en duel !

Tous les fichiers de cet article sont dans le fichier .zip : _mustache_example_4.zip

Le style avec good_all_style.css

  /* #General
  ================================================== */
 
  body {
  	padding: 10px;
  	margin: 20px;
  	font-size: 14px;
  }
  body * {
  	-webkit-font-smoothing: antialiased !important;
  	text-rendering: optimizelegibility;
  }
  a {
  	transition: all 0.2s ease-in-out;
  	-moz-transition: all 0.2s ease-in-out;
  	-webkit-transition: all 0.2s ease-in-out;
  	-o-transition: all 0.2s ease-in-out;
  }
  body, h1, h2, h3, h4, h5, h6 {
  	font-family: "Myriad Pro", Arial, Helvetica, Tahoma, sans-serif;
  	font-weight: 300;
  }
  h1 {
  	font-size: 30px;
  	line-height: 42px;
  }
  h2 {
  	font-size: 24px;
  	line-height: 32px;
  }
  h3 {
  	font-size: 18px;
  	line-height: 24px;
  	font-weight: normal;
  	margin-bottom: 15px;
  }
  h4 {
  	font-size: 16px;
  	font-weight: normal;
  	line-height: 20px;
  	margin-bottom: 15px;
  }
  h5 {
  	font-size: 14px;
  	font-weight: bold;
  	line-height: 18px;
  	margin-bottom: 15px;
  }
  h6 {
  	font-size: 12px;
  	font-weight: bold;
  	line-height: 16px;
  	margin-bottom: 10px;
  	text-transform: uppercase;
  }
  p {
  	font-size: 14px;
  	line-height: 170%;
  }
  p:empty {
  	/*display: none;
  	margin-bottom: 0;*/
  }
  a.list {
  	/*color: #F6177B;*/
  	color: #000;
  	text-decoration: none;
  	outline: none;
  }
  span.tag a {
  	/*color: #F6177B;*/
  	color: #999999;
  	text-decoration: none;
  	outline: none;
  }

Le code JS qui permet de charger les différents fichiers json main_mustache_complex.js

  function sample_1 () {
  	  		var product_name ='json/G_data_sample_utf8_complex_1.json';
    				$.getJSON(''+product_name+'', function(data) {	
  		    	var template = $('#content-template').html();
  		    	var info = Mustache.to_html(template, data);
  		    $('#PersonNewsFeed').html(info);
  			});
  		console.log("product_name =&gt; "+product_name+"\n");
  	}
 
  	function sample_2 () {
  		  		var product_name ='json/G_data_sample_utf8_complex_2.json';
  	  				$.getJSON(''+product_name+'', function(data) {	
  			    		var template = $('#content-template').html();
  			    		var info = Mustache.to_html(template, data);
  			    	$('#PersonNewsFeed').html(info);
  				});
  			console.log("product_name =&gt; "+product_name+"\n");
  	}

Le premier fichier de source de données G_data_sample_utf8_complex_1.json chargé par la fonction sample_1

  { 
  "encoding" : "UTF-8",
    "result": {
      "people": {
                "person": [
 
                      { "firstname": "Alikaou",
                    		"lastname": "Camara",
                    		"title": "The Soninke diaspora workers",
                    		"image": [
                          {
                            "code": "256x256",
                            "url": "assets/userphoto/alikaou_soninke.jpg"  
                          }],
                    		"body": "<p>Nulla facilisi. Etiam enim tellus, adipiscing et rutrum vitae, tempus nec orci. Aenean in consequat nunc. Pellentesque sagittis dolor non leo rutrum at suscipit quam adipiscing. Nulla faucibus faucibus neque id ultrices. Suspendisse potenti. Nulla sit amet justo vitae leo convallis facilisis. Maecenas condimentum urna quis tellus imperdiet sagittis. Nam quis metus sed erat feugiat sollicitudin pulvinar nec justo. Sed est quam, porta non elementum non, ullamcorper eu arcu. <b>Duis semper placerat enim non luctus. Vivamus tincidunt tempor erat, ac fringilla nulla aliquam quis. Suspendisse pretium enim risus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi id risus vel nibh dignissim dictum.</b></p>",
                    		"taxonomy": [
                          {
                            "vid": "1_D890_3",
                            "tags": [
                              {
                                "tid": "1_D75_9",
                                "name": "Africa"
                              }
                            ]
                          },
                          {
                            "vid": "1_D890_1",
                            "tags": [
                              {
                                "tid": "1_D75_639",
                                "name": "Mali"
                              },
                              {
                                "tid": "1_D75_3949",
                                "name": "Barack Obama"
                              }
                            ]
                          }
                        ],
 
                        "geoloc": {
                          "latitude": "12.650000",
                          "longitude": "-8.000000",
                          "bounds": {
                            "northeast": {
                              "latitude": "12.732974",
                              "longitude": "-7.875945"
                            },
                            "southwest": {
                              "latitude": "12.490909",
                              "longitude": "-8.115081"
                            }
                          },
                          "address": {
                            "thoroughfare": "",
                            "postalcode": "",
                            "locality": "Bamako",
                            "subadminarea": "Bamako",
                            "adminarea": "Bamako",
                            "country": "ML"
                          }
                        }
 
 
 
                    	},
 
                    	{ "firstname": "Robert Herbert",
                    		"lastname": "Appel",
                    		"title": "A Concise History of Modern Painting",
                    		"image": [
                          {
                            "code": "256x256",
                            "url": "assets/userphoto/Modern-Painting-Herbert.jpg"  
                          }],
                    		"body": "<p>Nulla facilisi. Etiam enim tellus, adipiscing et rutrum vitae, tempus nec orci. Aenean in consequat nunc. Pellentesque sagittis dolor non leo rutrum at suscipit quam adipiscing. Nulla faucibus faucibus neque id ultrices. Suspendisse potenti. Nulla sit amet justo vitae leo convallis facilisis. Maecenas condimentum urna quis tellus imperdiet sagittis. Nam quis metus sed erat feugiat sollicitudin pulvinar nec justo. Sed est quam, porta non elementum non, ullamcorper eu arcu. <b>Duis semper placerat enim non luctus. Vivamus tincidunt tempor erat, ac fringilla nulla aliquam quis. Suspendisse pretium enim risus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi id risus vel nibh dignissim dictum.</b></p>",
                    			"taxonomy": [
                            {
                              "vid": "1_D430_3",
                              "tags": [
                                {
                                  "tid": "1_D89_2",
                                  "name": "South America"
                                }
                              ]
                            },
                            {
                              "vid": "1_D430_1",
                              "tags": [
                                {
                                  "tid": "1_D89_210",
                                  "name": "Brasil"
                                },
                                {
                                  "tid": "1_D89_2902",
                                  "name": "Dilma Rousseff"
                                },
                                {
                                  "tid": "1_D89_3794",
                                  "name": "Pope François"
                                },
                                {
                                  "tid": "1_D89_790",
                                  "name": "Da Vinci"
                                },
                                {
                                  "tid": "1_D89_1601",
                                  "name": "Religion"
                                },
                                {
                                  "tid": "1_D89_731",
                                  "name": "Vatican"
                                }
                              ]
                            }
                          ],
                          "geoloc": {
                            "latitude": "-22.903539",
                            "longitude": "-43.209587",
                            "bounds": {
                              "northeast": {
                                "latitude": "-22.749045",
                                "longitude": "-43.099382"
                              },
                              "southwest": {
                                "latitude": "-23.08302",
                                "longitude": "-43.795449"
                              }
                            },
                            "address": {
                              "thoroughfare": "",
                              "postalcode": "",
                              "locality": "Rio de Janeiro",
                              "subadminarea": "",
                              "adminarea": "Rio de Janeiro",
                              "country": "BR"
                            }
                          }
 
                    	},
 
                    	{ "firstname": "Youssef",
                    		"lastname": "Touieb",
                    		"title": "History of the Muslim Brotherhood in Egypt",
                    		"image": [
                          {
                            "code": "256x256",
                            "url": "assets/userphoto/Youssef-Touieb-Selbstbildnis.jpg"  
                          }],
                    		"body": "<p>Nulla facilisi. Etiam enim tellus, adipiscing et rutrum vitae, tempus nec orci. Aenean in consequat nunc. Pellentesque sagittis dolor non leo rutrum at suscipit quam adipiscing. Nulla faucibus faucibus neque id ultrices. Suspendisse potenti. Nulla sit amet justo vitae leo convallis facilisis. Maecenas condimentum urna quis tellus imperdiet sagittis. Nam quis metus sed erat feugiat sollicitudin pulvinar nec justo. Sed est quam, porta non elementum non, ullamcorper eu arcu. <b>Duis semper placerat enim non luctus. Vivamus tincidunt tempor erat, ac fringilla nulla aliquam quis. Suspendisse pretium enim risus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi id risus vel nibh dignissim dictum.</b></p>",
                    			"taxonomy": [
                            {
                              "vid": "1_D670_3",
                              "tags": [
                                {
                                  "tid": "1_D64_3",
                                  "name": "Middle East"
                                }
                              ]
                            },
                            {
                              "vid": "1_D670_1",
                              "tags": [
                                {
                                  "tid": "1_D94_579",
                                  "name": "Egypt"
                                },
                                {
                                  "tid": "1_D94_3942",
                                  "name": "Moubarak"
                                },
                                {
                                  "tid": "1_D94_3599",
                                  "name": "Mohamed Morsi"
                                }
                              ]
                            }
                          ],
                          "geoloc": {
                            "latitude": "30.044420",
                            "longitude": "31.235712",
                            "bounds": {
                              "northeast": {
                                "latitude": "30.110602",
                                "longitude": "31.301973"
                              },
                              "southwest": {
                                "latitude": "30.009228",
                                "longitude": "31.222067"
                              }
                            },
                            "address": {
                              "thoroughfare": "",
                              "postalcode": "",
                              "locality": "Cairo",
                              "subadminarea": "",
                              "adminarea": "Cairo Governorate",
                              "country": "EG"
                            }
                          }
 
                    	}
 
 
 
                          ]
                }
          }
    }

Le deuxième fichier de source de données G_data_sample_utf8_complex_1.json chargé par le fonction sample_2

  { 
  "encoding" : "UTF-8",
    "result": {
      "people": {
                "person": [
 
                      { "firstname": "Youssef",
                    		"lastname": "Wara",
                    		"title": "Ougao vs Disney",
                    		"image": [
                          {
                            "code": "256x256",
                            "url": "assets/userphoto/youssef_wara.jpg"  
                          }],
                    		"body": "<p>Nulla facilisi. Etiam enim tellus, adipiscing et rutrum vitae, tempus nec orci. Aenean in consequat nunc. Pellentesque sagittis dolor non leo rutrum at suscipit quam adipiscing. Nulla faucibus faucibus neque id ultrices. Suspendisse potenti. Nulla sit amet justo vitae leo convallis facilisis. Maecenas condimentum urna quis tellus imperdiet sagittis. Nam quis metus sed erat feugiat sollicitudin pulvinar nec justo. Sed est quam, porta non elementum non, ullamcorper eu arcu. <b>Duis semper placerat enim non luctus. Vivamus tincidunt tempor erat, ac fringilla nulla aliquam quis. Suspendisse pretium enim risus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi id risus vel nibh dignissim dictum.</b></p>",
 
 
                    		"taxonomy": [
                          {
                            "vid": "1_D890_3",
                            "tags": [
                              {
                                "tid": "1_D75_9",
                                "name": "Africa"
                              }
                            ]
                          },
                          {
                            "vid": "1_D890_1",
                            "tags": [
                              {
                                "tid": "1_D75_639",
                                "name": "Kenya"
                              },
                              {
                                "tid": "1_D75_3949",
                                "name": "Tanzanie"
                              }
                            ]
                          }
                        ],
 
                        "geoloc": {
                          "latitude": "12.650000",
                          "longitude": "-8.000000",
                          "bounds": {
                            "northeast": {
                              "latitude": "12.732974",
                              "longitude": "-7.875945"
                            },
                            "southwest": {
                              "latitude": "12.490909",
                              "longitude": "-8.115081"
                            }
                          },
                          "address": {
                            "thoroughfare": "",
                            "postalcode": "",
                            "locality": "Bamako",
                            "subadminarea": "Bamako",
                            "adminarea": "Bamako",
                            "country": "ML"
                          }
                        }
 
 
 
                    	},
 
                    	{ "firstname": "Moshe",
                    		"lastname": "Dunlop",
                    		"title": "Chess theory",
                    		"image": [
                          {
                            "code": "256x256",
                            "url": "assets/userphoto/Moshe-Dunlop.jpg"  
                          }],
                    		"body": "<p>Nulla facilisi. Etiam enim tellus, adipiscing et rutrum vitae, tempus nec orci. Aenean in consequat nunc. Pellentesque sagittis dolor non leo rutrum at suscipit quam adipiscing. Nulla faucibus faucibus neque id ultrices. Suspendisse potenti. Nulla sit amet justo vitae leo convallis facilisis. Maecenas condimentum urna quis tellus imperdiet sagittis. Nam quis metus sed erat feugiat sollicitudin pulvinar nec justo. Sed est quam, porta non elementum non, ullamcorper eu arcu. <b>Duis semper placerat enim non luctus. Vivamus tincidunt tempor erat, ac fringilla nulla aliquam quis. Suspendisse pretium enim risus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi id risus vel nibh dignissim dictum.</b></p>",
 
                    			"taxonomy": [
                            {
                              "vid": "1_D430_3",
                              "tags": [
                                {
                                  "tid": "1_D89_2",
                                  "name": "South America"
                                }
                              ]
                            },
                            {
                              "vid": "1_D430_1",
                              "tags": [
                                {
                                  "tid": "1_D89_210",
                                  "name": "Brasil"
                                },
                                {
                                  "tid": "1_D89_2902",
                                  "name": "Dilma Rousseff"
                                },
                                {
                                  "tid": "1_D89_3794",
                                  "name": "Pope François"
                                },
                                {
                                  "tid": "1_D89_790",
                                  "name": "Da Vinci"
                                },
                                {
                                  "tid": "1_D89_1601",
                                  "name": "Religion"
                                },
                                {
                                  "tid": "1_D89_731",
                                  "name": "Vatican"
                                }
                              ]
                            }
                          ],
                          "geoloc": {
                            "latitude": "-22.903539",
                            "longitude": "-43.209587",
                            "bounds": {
                              "northeast": {
                                "latitude": "-22.749045",
                                "longitude": "-43.099382"
                              },
                              "southwest": {
                                "latitude": "-23.08302",
                                "longitude": "-43.795449"
                              }
                            },
                            "address": {
                              "thoroughfare": "",
                              "postalcode": "",
                              "locality": "Rio de Janeiro",
                              "subadminarea": "",
                              "adminarea": "Rio de Janeiro",
                              "country": "BR"
                            }
                          }
 
                    	},
 
                    	{ "firstname": "Abdul",
                    		"lastname": "Fromager",
                    		"title": "Milk Recipes",
                    			"image": [
                            {
                              "code": "256x256",
                              "url": "assets/userphoto/Abdul-Fromager-Selbstbildnis.jpg"  
                            }],
                      		"body": "<p>Nulla facilisi. Etiam enim tellus, adipiscing et rutrum vitae, tempus nec orci. Aenean in consequat nunc. Pellentesque sagittis dolor non leo rutrum at suscipit quam adipiscing. Nulla faucibus faucibus neque id ultrices. Suspendisse potenti. Nulla sit amet justo vitae leo convallis facilisis. Maecenas condimentum urna quis tellus imperdiet sagittis. Nam quis metus sed erat feugiat sollicitudin pulvinar nec justo. Sed est quam, porta non elementum non, ullamcorper eu arcu. <b>Duis semper placerat enim non luctus. Vivamus tincidunt tempor erat, ac fringilla nulla aliquam quis. Suspendisse pretium enim risus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi id risus vel nibh dignissim dictum.</b></p>",
 
                    			"taxonomy": [
                            {
                              "vid": "1_D670_3",
                              "tags": [
                                {
                                  "tid": "1_D64_3",
                                  "name": "Middle East"
                                }
                              ]
                            },
                            {
                              "vid": "1_D670_1",
                              "tags": [
                                {
                                  "tid": "1_D94_579",
                                  "name": "Egypt"
                                },
                                {
                                  "tid": "1_D94_3942",
                                  "name": "Moubarak"
                                },
                                {
                                  "tid": "1_D94_3599",
                                  "name": "Mohamed Morsi"
                                }
                              ]
                            }
                          ],
                          "geoloc": {
                            "latitude": "30.044420",
                            "longitude": "31.235712",
                            "bounds": {
                              "northeast": {
                                "latitude": "30.110602",
                                "longitude": "31.301973"
                              },
                              "southwest": {
                                "latitude": "30.009228",
                                "longitude": "31.222067"
                              }
                            },
                            "address": {
                              "thoroughfare": "",
                              "postalcode": "",
                              "locality": "Cairo",
                              "subadminarea": "",
                              "adminarea": "Cairo Governorate",
                              "country": "EG"
                            }
                          }
 
                    	}
 
 
 
                          ]
                }
          }
    }

L’ensemble du fichier qui charge le tout….

  &lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
     "http://www.w3.org/TR/html4/loose.dtd"&gt;
 
  &lt;html lang="en"&gt;
  &lt;head&gt;
  	&lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"&gt;
  &lt;body&gt;
  &lt;head&gt;
  	&lt;meta charset="utf-8" /&gt;
  	&lt;title&gt;Mustache complex&lt;/title&gt;
  	&lt;link rel="STYLESHEET" type="text/css" href="css/good_all_style.css"&gt;
  &lt;/head&gt;
  &lt;body&gt;
 
 
 
    &lt;h2&gt;Check Out the list&lt;/h2&gt;
 
    &lt;ul&gt;
    	&lt;li&gt;&lt;a href="javascript:sample_1()" class="list"&gt;Load sample_1&lt;/a&gt;&lt;/li&gt;
    	&lt;li&gt;&lt;a href="javascript:sample_2()" class="list"&gt;Load sample_2&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
 
    &lt;h2&gt;People&lt;/h2&gt;
    &lt;ul class="list" id="PersonNewsFeed"&gt;
    &lt;/ul&gt;  
 
  &lt;script src="js/jquery-1.7.1.min.js"&gt;&lt;/script&gt;
  &lt;script src="js/mustache.js"&gt;&lt;/script&gt;
  &lt;script src="js/main_mustache_complex.js"&gt;&lt;/script&gt;   
  &lt;script id="content-template" type="text/template"&gt;
 
  &lt;ol&gt;
  	{{#result}}
  		{{#people}}
  			{{#person}}
        &lt;li&gt;
  			{{#image}}
  								{{#code}}
  								IMG ({{code}}) =&gt; &lt;img src="{{url}}" /&gt;&lt;br&gt;
  								{{/code}}
  								{{^code}}
  								no img
  								{{/code}}
  			{{/image}}
 
 
  				TITLE =&gt; &lt;a href="{{url}}/" title="{{title}}"&gt;{{title}} &lt;/a&gt;
  				&lt;br&gt;&lt;br&gt;
 
 
  					BODY escaped HTML =&gt; {{{body}}}
  					&lt;br&gt;&lt;br&gt;
 
  					BODY unescaped HTML =&gt; {{body}}
  					&lt;br&gt;&lt;br&gt;
 
            TAXONOMY =&gt; 
  						{{#taxonomy}}
  							{{#tags}}
  								&lt;span class="tag"&gt;&lt;a class="no-decoration" href="{{tid}}"&gt;{{name}} ({{tid}})&lt;/a&gt;&lt;/span&gt;
  							{{/tags}}
  						{{/taxonomy}}
  			    &lt;br&gt;
  			    GEOLOC =&gt; 
        		{{#geoloc}}
        		  LAT, LONG =&gt; ({{latitude}}, {{longitude}})
        		  &lt;br&gt;
        		      {{#address}}
                                 GEOLOC =&gt; CITY, COUNTRY =&gt; &lt;span class="tag"&gt;{{locality}} -[{{country}}]&lt;/a&gt;&lt;/span&gt;&lt;br&gt;
              		{{/address}}
        		{{/geoloc}}
        &lt;/li&gt;
  			{{/person}}
  		{{/people}}
  	{{/result}}
  &lt;/ol&gt;	
 
  	&lt;/script&gt;
 
 
 
 
 
 
  &lt;/body&gt;
  &lt;/html&gt;

Conclusion : Cela peut sembler assez bousinesque et décevant de se donner tant de mal pour un si piètre résultat en terme UX. Disons-le les templates sont assez moches. Toutefois, l’objectif était de valider rapidement des règles fonctionnelles, à l’aide de Mustache, en rendant des écrans plus « parlants » qu’un aride flux JSON, de ce point de vue la valeur ajoutée en terme fonctionnel est indéniable.

En savoir plus