Comment utiliser JSON pour alimenter l'application interactive Backbone.js

Je travaille à convertir une série d'applications éducatives interactives de Flash en Javascript et mon équipe envisage d'utiliser Backbone.js comme cadre. Chacune de ces applications est essentiellement une collection de scènes qui présentent des informations à l'utilisateur et / ou suscitent une certaine interaction, que ce soit sous forme de questions ou de widgets interactifs. La structure de base que nous envisageons pour l'application est la suivante:

  • Un ensemble de fichiers JSON qui contiennent les informations particulières pour chaque application, telles que le nombre de "scènes" ou d'objets auxquels l'application possède, les différents messages et / ou widgets affichés aux utilisateurs, etc.
  • Un ensemble de modèles Backbone (probablement Undercore) qui régissent la navigation, les messages, etc.
  • Une collection de vues / routeurs / modèles Backbone pour faciliter la navigation entre les scènes d'une application et la gestion de l'interaction entre les utilisateurs
  • Certains widgets interactifs construits en version native Javascript

Le problème est, bien sûr, que je suis un novice quand il s'agit de Backbone. J'ai parcouru quelques-uns des didacticiels de base mais j'ai du mal à intégrer Backbone avec des fichiers JSON statiques.

Disons que j'ai le fichier JSON très basique suivant qui définit trois scènes à afficher:

var scenes = [ { "name": "Introduction", "label": "Introduction", "message": "Welcome to this app" }, { "name": "Exercise", "label": "Exercise", "message": "If this were a real app, there'd be some sort of exercise here" }, { "name": "Conclusion", "label": "Conclusion", "order": "Thank you for completing this app" } ] 

Ce dont j'ai besoin et ce que j'essaie de faire, c'est que Backbone génère un widget de navigation qui permet aux utilisateurs de naviguer entre ces scènes et d'afficher le message pour chaque scène. (C'est évidemment une version incroyablement simplifiée de l'application du monde réel.)

Voici ce que j'ai essayé:

 // simplified object containing stage information var stages = [ { "name": "Introduction", "label": "Introduction", "message": "Welcome to this app" }, { "name": "Exercise", "label": "Exercise", "message": "If this were a real app, there'd be some sort of exercise here" }, { "name": "Conclusion", "label": "Conclusion", "order": "Thank you for completing this app" } ]; $(function(){ // create model for each stage StageModel = Backbone.Model.extend({}); // create collection for StageModel StageModelList = Backbone.Collection.extend({ model: StageModel }); var stageModelList = new StageModelList(); // create view for list of stages StageListView = Backbone.View.extend({ el: $("#stageNav"), initialize: function() { // if stages are added later stagemodellist.bind('add',this.createStageList, this); }, events: { 'click .stageListItem' : 'selectStage' }, createStageList: function(model) { $("#stageList").append("<li class='stageListItem'>"+model.label+"</li>"); }, selectStage: function() { this.router.navigate("stage/"+this.stage.name,true); } }); // create view for each stages StageView = Backbone.View.extend({ el: $("#stage"), initialize: function(options) { // get stage variable from options this.stage = this.options.stage; // display stage createOnEnter(this.stage); }, createOnEnter: function(stage) { $("#stageLabel").html(stage.label); $("#stageMsg").html(stage.message); } }); // create router AppRouter = Backbone.Router.extend({ initialize: function() { Backbone.history.start(); // create collection new StageModelList(); // create view when router is initialized new StageListView(); // loop through stages and add each to StageModelList for (var s in stages) { StageModelList.add(stages[s]); } }, routes: { "stage/:stage" : "renderStage" }, renderStage: function(stage) { // display StageView for this stage new StageView({stage:stage}); } }); var App = new AppRouter(); }); 

Et le html:

 <!DOCTYPE html> <html> <head> <script class="jsbin" src="http://code.jquery.com/jquery-1.7.1.min.js"></script> <script class="jsbin" src="http://documentcloud.github.com/underscore/underscore-min.js"></script> <script class="jsbin" src="http://documentcloud.github.com/backbone/backbone.js"></script> <script src="js/ilo4.js"></script> <meta charset=utf-8 /> <title>JS Bin</title> </head> <body> <p>My pathetic attempt at a Backbone.js app</p> <div id="stageNav"> <ul id="stageList"> </ul> </div> <div id="stage"> <div id="stageLabel"> </div> <div id="stageMsg"> </div> </div> </body> </html> 

(Vous pouvez également voir une version jsbin ici: http://jsbin.com/iwerek/edit#javascript,html,live ).

En ce moment, cela ne fait rien, malheureusement.

Je sais que je fais tellement mal de choses ici, et certaines questions que je lance:

  • Dois-je même besoin d'un routeur?
  • Dois-je initialiser la collection en tant que variable?
  • Existe-t-il un meilleur moyen de lier le modèle à la liste des étapes?

UNE

Vous étiez en fait pas trop loin.

J'ai cloné votre jsbin et l'ai réparé afin qu'il fonctionne: lien

Je le soutiens comme réponse à votre question. Je l'ai commenté à fond pour expliquer ce qui se passe.

Jetez un coup d'oeil, j'espère que cela vous aide.

EDIT: qu'est-ce que je veux dire, je vais mettre le code ici aussi:

 // simplified object containing stage information window.stages = [ { "name": "Introduction", "label": "Introduction", "message": "Welcome to this app" }, { "name": "Exercise", "label": "Exercise", "message": "If this were a real app, there'd be some sort of exercise here" }, { "name": "Conclusion", "label": "Conclusion", "message": "Thank you for completing this app" } ]; $(function(){ // StageModel: no need to extend if you're not adding anything. StageModel = Backbone.Model; // StageCollection StageCollection = Backbone.Collection.extend({ model: StageModel }); // create view for list of stages StageCollectionView = Backbone.View.extend({ el: $("#stageNav"), initialize: function() { // if stages are added later this.collection.bind('add', this.createStageListItem, this); }, events: { 'click .stageListItem' : 'selectStage' }, // I'm adding the model's cid (generated by backbone) as the // id of the 'li' here. Very non-ideal, as one of the points // of backbone et al. is to keep from embedding and retrieving // data from the DOM like this. // // Perhaps better would be to create a StageListItemView and // render one for each model in the collection, perhaps like: // // createStageListItem: function(model) { // this.$('#stageList').append(new StageListItemView({model: model}); // } // // where you have a StageListItemView that knows how to render // itself and can handle click events and communicate with the // collectionview via events. // // At any rate, this string-munging will suffice for now. createStageListItem: function(model) { this.$("#stageList").append("<li id=\"" + model.cid + "\" class='stageListItem'>" + model.get('label') + "</li>"); }, // Use backbone's event system, it's pretty awesome. Not to mention // that it helps to decouple the parts of your app. // // And note that you can pass arguments when you trigger an event. // So any event handler for the 'new-stage' event would receive // this model as its first argument. selectStage: function(event) { var cid = $(event.target).attr('id'); this.trigger('new-stage', this.collection.getByCid(cid)); }, // This was a missing puzzle piece. Your StageCollectionView wasn't // being rendered at all. // // Backbone convention is to call this function render, but you could // call it whatever you want, as long as you, well, end up _calling_ it. render: function() { var self = this; this.collection.each(function(model) { self.createStageListItem(model); }); return this; } }); // StageView, StageView = Backbone.View.extend({ el: $("#stage"), // We're going to assume here that we get passed a // newStageEventSource property in the options and // that it will fire a 'new-stage' event when we need // to load a new stage. initialize: function(options) { this.eventSource = options.newStageEventSource; this.eventSource.bind('new-stage', this.loadAndRenderStage, this); }, // A load function to set the StageView's model. load: function(model) { this.model = model; return this; }, render: function() { $("#stageLabel").html(this.model.get('label')); $("#stageMsg").html(this.model.get('message')); }, loadAndRenderStage: function(stage) { this.load(stage); this.render(); } }); // Instatiate a StageCollection from the JSON list of stages. // See Backbone docs for more, but you can pass in a list of // hashes, and the Collection will use its model attribute to // make the models for you var stageCollection = new StageCollection(stages); // View constructors take an options argument. Certain properties // will automatically get attached to the view instance directly, // like 'el', 'id', 'tagName', 'className', 'model', 'collection'. var stageCollectionView = new StageCollectionView({ collection: stageCollection }); // Instantiate the StageView, passing it the stageCollectionView in // the options for it to listen to. var stageView = new StageView({ newStageEventSource: stageCollectionView }); // Last step, we need to call 'render' on the stageCollectionView // to tell it to show itself. stageCollectionView.render(); });