PageRenderTime 57ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/03-1ERS-CONTACTS-AVEC-BACKBONE.md

https://github.com/fabiansky/backbone.en.douceur
Markdown | 495 lines | 346 code | 149 blank | 0 comment | 0 complexity | 299f43c623e88c445177b6189c22dd4c MD5 | raw file
  1. #1er contact avec Backbone
  2. >*Sommaire*
  3. >>- *Premier modèle*
  4. >>- *Première collection*
  5. >>- *Première vue & premier template*
  6. >*Nous allons faire un premier exemple Backbone pas à pas, même sans connaître le framework. Cela va permettre de « désacraliser » la bête et de mettre un peu de liant avec tout ce que nous avons vu précédemment. Puis nous passerons dans le détail tous les composants de Backbone dans les chapitres qui suivront.*
  7. Voilà, il est temps de s'y mettre. L'application que nous allons réaliser avec Backbone tout au long de cet ouvrage va être un Blog, auquel nous ajouterons au fur et à mesure des fonctionnalités pour finalement le transformer en CMS (Content Management System). Je vous l'accorde ce n'est pas très original, mais cela répond à des problématiques classiques (récurrentes ?) dans notre vie "d'informaticien" et cela a le mérite d'avoir un aspect pratique et utile. Notre point de départ va être un blog que nous agrémenterons de fonctionnalités au fil des chapitres.
  8. ##1ère application Backbone
  9. Nous allons faire ici un exemple très rapide, sans forcément entrer dans le détail ni mettre en œuvre les bonnes pratiques d'organisation de code. Cet exercice est là pour démontrer la simplicité d'utilisation, et le code devrait être suffisamment simple pour se passer d'explications. Donc, "pas de panique !", laissez-vous guider, dans **15 minutes** vous aurez une 1ère ébauche.
  10. ###Préparons notre page
  11. Nous allons utiliser notre même page `index.html`, mais faisons un peu de ménage à l'intérieur avant de commencer :
  12. ```html
  13. <!DOCTYPE html>
  14. <html>
  15. <head>
  16. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  17. <title>Backbone</title>
  18. <link href="libs/vendors/bootstrap/css/bootstrap.css" rel="stylesheet">
  19. <style>
  20. body {
  21. padding-top: 60px;
  22. padding-bottom: 40px;
  23. }
  24. </style>
  25. <link href="libs/vendors/bootstrap/css/bootstrap-responsive.css" rel="stylesheet">
  26. </head>
  27. <body>
  28. <div class="navbar navbar-fixed-top">
  29. <div class="navbar-inner">
  30. <div class="container">
  31. <a class="brand">Mon Blog</a>
  32. </div>
  33. </div>
  34. </div>
  35. <div class="container">
  36. <div class="hero-unit">
  37. <h1>Backbone rocks !!!</h1>
  38. </div>
  39. </div>
  40. </body>
  41. <!-- === Références aux Frameworks === -->
  42. <script src="libs/vendors/jquery-1.7.2.js"></script>
  43. <script src="libs/vendors/underscore.js"></script>
  44. <script src="libs/vendors/backbone.js"></script>
  45. <script>
  46. $(function (){
  47. });
  48. </script>
  49. </html>
  50. ```
  51. L'essentiel de notre travail va se passer dans la balise `<script></script>` en bas de page. De quoi avons-nous besoin dans un blog ?
  52. - Des articles : un ensemble d'articles (ou "posts"), généralement écrits par une seule personne (le blog est personnel, c'est en lui donnant des fonctionnalités multi-utilisateurs que nous nous dirigerons doucement vers un CMS).
  53. - Des commentaires : Il est de bon ton de permettre aux lecteurs du blog de pouvoir commenter les articles.
  54. Pour le moment nous allons nous concentrer uniquement sur les articles, notre objectif sera le suivant : "Afficher une liste d'articles sur la page principale".
  55. ##Le Modèle Article
  56. Dans la balise `<script></script>` saisissez le code suivant :
  57. *Définition dun modèle Article :*
  58. ```html
  59. < script >
  60. $(function() {
  61. //permettra d'accéder à nos variables en mode console
  62. window.blog = {};
  63. /*--- Modèle article ---*/
  64. // une "sorte" de classe Article
  65. blog.Article = Backbone.Model.extend({
  66. //les valeurs par défaut d'un article
  67. defaults: {
  68. title: "titre de l'article",
  69. content: "contenu de l'article",
  70. publicationDate: new Date()
  71. },
  72. // s'exécute à la création d'un article
  73. initialize: function() {
  74. console.log("Création d'un nouvel article")
  75. }
  76. });
  77. });
  78. < /script>
  79. ```
  80. Sauvegardez, relancez dans le navigateur, et allez dans la console :
  81. - Pour créer un nouvel article : tapez la commande `myFirstArticle = new blog.Article()`
  82. - Pour "voir" le titre de l'article : tapez la commande `myFirstArticle.get("title")`
  83. - Pour "voir" le contenu de l'article : tapez la commande `myFirstArticle.get("content")`
  84. - Pour changer le titre de l'article : tapez la commande `myFirstArticle.set("title","MON TITRE")` ou `myFirstArticle.set({title : "MON TITRE"})`
  85. - Pour changer simultanément le titre et le contenu : tapez la commande `myFirstArticle.set({title : "MON TITRE ...", content : "blablabla"})`
  86. - Pour créer un article directement avec un titre et du contenu : tapez la commande `mySecondArticle = new blog.Article({title : "MON AUTRE ARTICLE", content : "lore ipsum ..."})`
  87. ![BB](RSRC/03_01_BB.png)\
  88. Vous venez donc de voir que nous avons défini le modèle article un peu comme une classe qui hériterait (`extend`) de la classe `Backbone.Model`, que nous lui avons défini des valeurs par défauts (`defaults`), et affecté une méthode dinitialisation (`initialize`). Et quil existe un système de getter et de setter un peu particulier (`model.get(property_name)`, `model.set(property_name, value)`), mais nous verrons ultérieurement dans le détail comment fonctionnent les modèles.
  89. >>**Remarque** *: le modèle de programmation de Javascript est bien orienté objet, mais nest pas orienté classe comme peut lêtre par exemple Java. Cela peut déstabiliser au départ, mais je vous engage à lire [REF VERS ARTICLE] à ce propos.*
  90. ##La Collection dArticles
  91. Nous allons maintenant définir une collection qui nous aidera à gérer nos articles. Donc, à la suite du modèle Article saisissez le code suivant :
  92. *Définition dune collection darticles :*
  93. ```javascript
  94. /*--- Collection d'articles ---*/
  95. blog.ArticlesCollection = Backbone.Collection.extend({
  96. model: blog.Article,
  97. initialize: function() {
  98. console.log("Création d'une collection d'articles")
  99. }
  100. });
  101. ```
  102. >>**Notez** *qu'il faut bien préciser le type de modèle adressé par la collection (on pourrait dire que la collection est typée).*
  103. Sauvegarder, relancer dans le navigateur, et retournez à nouveau dans la console et saisissez les commandes suivantes :
  104. - Création de la collection :
  105. listeArticles = new blog.ArticlesCollection()
  106. - Ajout darticles à la collection :
  107. listeArticles.add(new blog.Article({ title : "titre1", content : "contenu1" }))
  108. listeArticles.add(new blog.Article({ title : "titre2", content : "contenu2" }))
  109. listeArticles.add(new blog.Article({ title : "titre3", content : "contenu3" }))
  110. Nous venons donc d'ajouter 3 articles à notre collection,
  111. - Si vous tapez la commande `listeArticles.models` vous obtiendrez un tableau de modèles
  112. - Si vous souhaitez obtenir le titre du 2ème article de la collection, tapez :
  113. `listeArticles.models[1].get("title")`
  114. - vous souhaitez parcourir les articles de la collection et afficher leur titre :
  115. `listeArticles.each(function(article){ console.log (article.get("title")); });`
  116. >>Cela vous rappelle quelque chose ? Le `each` de Backbone est implémenté grâce à Underscore.
  117. ![BB](RSRC/03_02_BB.png)\
  118. **Maintenant que nous avons de quoi gérer nos données, il est temps de les afficher dans notre page HTML.**
  119. ##Vue et Template
  120. Avant toute chose, allons ajouter dans notre code javascript (en bas de la page HTML) le bout de code qui va créer les articles et la collection d'articles pour nous éviter de tout re-saisir à chaque fois. Donc après le code de la collection, ajoutez ceci :
  121. ```javascript
  122. /*--- bootstrap ---*/
  123. blog.listeArticles = new blog.ArticlesCollection();
  124. blog.listeArticles.add(new blog.Article({ title : "titre1", content : "contenu1" }));
  125. blog.listeArticles.add(new blog.Article({ title : "titre2", content : "contenu2" }));
  126. blog.listeArticles.add(new blog.Article({ title : "titre3", content : "contenu3" }));
  127. blog.listeArticles.add(new blog.Article({ title : "titre4", content : "contenu4" }));
  128. blog.listeArticles.add(new blog.Article({ title : "titre5", content : "contenu5" }));
  129. ```
  130. Ensuite dans le code html, ajoutons le template de notre vue et le div dans lequel les données seront affichées :
  131. ```html
  132. <% _.each(articles, function(article) { %>
  133. <h1><%= article.title %></h1>
  134. <h6><%= article.publicationDate %></h6>
  135. <p><%= article.content %></p>
  136. <% }); %>
  137. ```
  138. donc :
  139. ```html
  140. <body>
  141. <div class="navbar navbar-fixed-top">
  142. <div class="navbar-inner">
  143. <div class="container">
  144. <a class="brand">Mon Blog</a>
  145. </div>
  146. </div>
  147. </div>
  148. <div class="container">
  149. <div class="hero-unit">
  150. <h1>Backbone rocks !!!</h1>
  151. </div>
  152. <!-- ìci notre template -->
  153. <script type="text/template" id="articles-collection-template">
  154. <% _.each(articles, function(article) { %>
  155. <h1><%= article.title %></h1>
  156. <h6><%= article.publicationDate %></h6>
  157. <p><%= article.content %></p>
  158. <% }); %>
  159. </script>
  160. <!-- les données seront affichées ici -->
  161. <div id="articles-collection-container"></div>
  162. </div>
  163. </body>
  164. ```
  165. Puis dans le code javascript, à la suite du code de la collection et avant le code de chargement des données (bootstrap), ajoutez le code de la vue Backbone :
  166. ```javascript
  167. /*--- Vues ---*/
  168. blog.ArticlesView = Backbone.View.extend({
  169. el: $("#articles-collection-container"),
  170. initialize: function() {
  171. this.template = _.template($("#articles-collection-template").html());
  172. },
  173. render: function() {
  174. var renderedContent = this.template({
  175. articles: this.collection.toJSON()
  176. });
  177. $(this.el).html(renderedContent);
  178. return this;
  179. }
  180. });
  181. ```
  182. ###Quavons-nous fait ?
  183. Eh bien, nous avons défini une vue avec :
  184. - Une propriété `el` (pour élément) à laquelle on attache le `<div>` dont lid est :
  185. `“articles-collection-container”`. Cest dans ce `<div>` que seront affichés les articles
  186. - Une méthode `initialize`, qui affecte une méthode `template()` à linstance de la vue en lui précisant que nous utiliserons le modèle de code html définit dans le `<div>` dont lid est `“articles-collection-template”`
  187. - Une méthode `render`, qui va passer les données en paramètre à la méthode `template()` puis les afficher dans la page
  188. Sauvegarder, relancer dans le navigateur, et retournez encore dans la console pour saisir les commandes suivantes :
  189. - Pour instancier une vue : `articlesView = new blog.ArticlesView({ collection : blog.listeArticles })` à laquelle nous passons la collection darticles en paramètre
  190. - Pour afficher les données : `articlesView.render()`
  191. **Et la "magie" de Backbone s'opère, vos articles s'affichent instantanément dans votre page :** :)
  192. ![BB](RSRC/03_03_BB.png)\
  193. >>**Remarque** : Notez bien que la collection doit être transformée en chaîne JSON pour être interprétée dans le template ( `this.template({ articles : this.collection.toJSON() })` ) et que nous avons nommé le paramètre `articles` pour faire le lien avec le template ( `_.each(articles, function(article) {}` ).
  194. ##Un dernier tour de magie pour clôturer le chapitre dinitiation : binding
  195. A la fin de la méthode `initialize` de la vue, ajoutez le code suivant :
  196. ```javascript
  197. /*--- binding ---*/
  198. _.bindAll(this, 'render');
  199. this.collection.bind('change', this.render);
  200. this.collection.bind('add', this.render);
  201. this.collection.bind('remove', this.render);
  202. /*---------------*/
  203. ```
  204. ###Que venons-nous de faire ?
  205. Nous venons "d'expliquer" à Backbone, qu'à chaque changement dans la collection, la vue doit rafraîchir son contenu. `_.bindAll` est une méthode d'Underscore ([http://documentcloud.github.com/underscore/#bind](http://documentcloud.github.com/underscore/#bind)) qui permet de conserver le contexte initial, c'est à dire : quel que soit "l'endroit" d'où l'on appelle la méthode `render`, ce sera bien l'instance de la vue (attachée à `this`) qui sera utilisée.
  206. //TODO: à expliquer plus simplement
  207. Une dernière fois, sauvegarder, relancer le navigateur, et retournez encore dans la console pour saisir les commandes suivantes :
  208. - Création de la vue : `articlesView = new blog.ArticlesView({ collection : blog.listeArticles })`
  209. - Afficher les données : `articlesView.render()`
  210. - Ajouter un nouvel article à la collection : `blog.listeArticles.add(new blog.Article({title:"Hello", content:"Hello World"}))`
  211. **Et , magique ! : Laffichage s'est actualisé tout seul :**
  212. ###Oh la vilaine erreur !!!
  213. Si vous avez bien suivi, j'ai fait une grossière erreur (je l’ai laissé volontairement, car c'est une erreur que j'ai déjà faite, et il n'est donc pas impossible que d'autres la fassent), la date de publication ne change pas ! En effet, je l'affecte dans les valeurs par défaut qui ne sont "settées" qu'une seule et unique fois lors de la définition de la "pseudo" classe `Backbone.Model`. Il faut donc initialiser la date de publication lors de l'instanciation du modèle, et ce dans la méthode `initialize()`. Modifiez donc le code du modèle de la manière suivante :
  214. ```javascript
  215. /*--- Modèle article ---*/
  216. blog.Article = Backbone.Model.extend({ // une "sorte" de classe Article
  217. defaults: { //les valeurs par défaut d'un article
  218. title: "titre de l'article",
  219. content: "contenu de l'article",
  220. //publicationDate : null
  221. },
  222. initialize: function() { // s'exécute à la création d'un article
  223. console.log("Création d'un nouvel article");
  224. this.set("publicationDate", new Date());
  225. }
  226. });
  227. ```
  228. Refaites les manipulations précédentes, et (si vous avez laissez suffisamment de temps entre la création des articles), vous pourrez noter que la date est bien mise à jour :
  229. ![BB](RSRC/03_04_BB.png)\
  230. ![BB](RSRC/03_05_BB.png)\
  231. >>**Remarque** : la propriété date nexiste plus dans les valeurs par défaut, elle est créée à linstanciation du modèle lors de lappel de `this.set("publicationDate",new Date())` dans la méthode `initialize`. De la même manière, vous pouvez créer à la volée des propriétés à posteriori pour les instances des modèles.
  232. **Et voilà, linitiation est terminée. Nous allons pouvoir passer aux choses sérieuses et découvrir jusqu nous pouvons pousser Backbone.**
  233. ##Code final de l'exemple
  234. Le code final de votre page devrait ressembler à ceci :
  235. ```html
  236. <!DOCTYPE html>
  237. <html>
  238. <head>
  239. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  240. <title>Backbone</title>
  241. <link href="libs/vendors/bootstrap/css/bootstrap.css" rel="stylesheet">
  242. <style>
  243. body {
  244. padding-top: 60px;
  245. padding-bottom: 40px;
  246. }
  247. </style>
  248. <link href="libs/vendors/bootstrap/css/bootstrap-responsive.css" rel="stylesheet">
  249. </head>
  250. <body>
  251. <div class="navbar navbar-fixed-top">
  252. <div class="navbar-inner">
  253. <div class="container">
  254. <a class="brand">Mon Blog</a>
  255. </div>
  256. </div>
  257. </div>
  258. <div class="container">
  259. <div class="hero-unit">
  260. <h1>Backbone rocks !!!</h1>
  261. </div>
  262. <!-- Template d'affichage des articles -->
  263. <script type="text/template" id="articles-collection-template">
  264. <% _.each(articles, function(article) { %>
  265. <h1><%= article.title %></h1>
  266. <h6><%= article.publicationDate %></h6>
  267. <p><%= article.content %></p>
  268. <% }); %>
  269. </script>
  270. <!-- div où seront affichés les articles -->
  271. <div id="articles-collection-container"></div>
  272. </div>
  273. </body>
  274. <!-- === Frameworks === -->
  275. <script src="libs/vendors/jquery-1.7.2.js"></script>
  276. <!--<script src="libs/vendors/bootstrap/js/bootstrap.js"></script>-->
  277. <script src="libs/vendors/underscore.js"></script>
  278. <script src="libs/vendors/backbone.js"></script>
  279. <!-- === code applicatif === -->
  280. < script >
  281. $(function() {
  282. window.blog = {};
  283. /*--- Modèle article ---*/
  284. blog.Article = Backbone.Model.extend({
  285. defaults: {
  286. title: "titre de l'article",
  287. content: "contenu de l'article",
  288. },
  289. initialize: function() {
  290. console.log("Création d'un nouvel article");
  291. this.set("publicationDate", new Date());
  292. }
  293. });
  294. /*--- Collection d'articles ---*/
  295. blog.ArticlesCollection = Backbone.Collection.extend({
  296. model: blog.Article,
  297. initialize: function() {
  298. console.log("Création d'une collection d'articles")
  299. }
  300. });
  301. /*--- Vues ---*/
  302. blog.ArticlesView = Backbone.View.extend({
  303. el: $("#articles-collection-container"),
  304. initialize: function() {
  305. this.template = _.template($("#articles-collection-template").html());
  306. /*--- binding ---*/
  307. _.bindAll(this, 'render');
  308. this.collection.bind('change', this.render);
  309. this.collection.bind('add', this.render);
  310. this.collection.bind('remove', this.render);
  311. /*---------------*/
  312. },
  313. render: function() {
  314. var renderedContent = this.template({
  315. articles: this.collection.toJSON()
  316. });
  317. $(this.el).html(renderedContent);
  318. return this;
  319. }
  320. });
  321. /*--- bootstrap ---*/
  322. blog.listeArticles = new blog.ArticlesCollection();
  323. blog.listeArticles.add(new blog.Article({
  324. title: "titre1",
  325. content: "contenu1"
  326. }));
  327. blog.listeArticles.add(new blog.Article({
  328. title: "titre2",
  329. content: "contenu2"
  330. }));
  331. blog.listeArticles.add(new blog.Article({
  332. title: "titre3",
  333. content: "contenu3"
  334. }));
  335. blog.listeArticles.add(new blog.Article({
  336. title: "titre4",
  337. content: "contenu4"
  338. }));
  339. blog.listeArticles.add(new blog.Article({
  340. title: "titre5",
  341. content: "contenu5"
  342. }));
  343. });
  344. </script>
  345. </html>
  346. ```