4

backbone newbie here. I'd like to start using backbone on an web app (backend is Ruby on Rails), which until now had very little client functionality (some jquery for doing slideToggles, and a couple ajax calls).

One of the problems I'm facing is that Backbone seems to be built so that you load your javascript, then make it request data (usually JSON) to the server, and then it renders the view. This is not acceptable in my case. I'd like to take the html originated on the server, present it to the user, and then populate my models with that html (after that, I'm fine with the models requesting JSON stuff from the server).

I'm guessing that what I need is some sort of "Inverse View". Something that given this html:

<ul class="people">
  <li><span class="name">Peter</span></li>
  <li><span class="name">John</span></li>
</ul>

And a People Collection and a Person model with a name attribute, can parse Peter and John out of that (maybe using the View).

Is this something that exists? Am I approaching the whole thing the wrong way?

kikito
  • 51,734
  • 32
  • 149
  • 189
  • I was also faced with this problem. My solution is described in http://stackoverflow.com/questions/17687972/connecting-predefined-html-to-models-and-views-in-backbone/17719267#17719267. – gitaarik Jul 29 '13 at 17:59

4 Answers4

5

It is possible to attach Backbone.View to a HTML page pre-rendered on the server. I've described this in more detail in this SO answer. This is a quite common scenario when the page needs to be crawlable by search engines.

However, I would recommend against a design where you initialize your model state from by parsing the server-rendered HTML. I suggest you instead bootstrap the initial model data to the served page as JSON. Something like:

<body>
      <!-- your server-side template code here -->
      <script>
        window.bootstrap = {
          people: <%= @people.to_json %>
        };
      </script>
</body>

When you initialize your collections, you can simply initialize them from the bootstrapped data and discard the bootstrapped collections:

var bootstrap = window.bootstrap || {};
var peopleCollection = new PeopleCollection(bootstrap.people);
delete window.bootstrap;

Just make sure that your server-side rendering engine consumes the same data as is bootstrapped, so you can guarantee that the rendered page and the initial model data is in sync.

Alternatively, reconsider whether a framework like Backbone is the right fit for your needs. You say your application has had very little (javascript) functionality. Do you need Backbone at all? It's a great framework, but what you want is The Right Tool For The Job™.

Community
  • 1
  • 1
jevakallio
  • 35,324
  • 3
  • 105
  • 112
3

I ended up just adding the json of every model I needed on the element of each view.

In other words:

<ul class="people" data-collection="people">
  <li data-model="person" data-attributes='{"name":"Peter"}'>Peter</li>
  <li data-model="person" data-attributes='{"name":"John"}'>John</li>
</ul>

The JSON pieces on each attribute are trivial to generate on the server in my case.

The reason I prefer including json embedded inside the html instead of returning it in a big array at the beginning is that this way I can attach the models to their views quite easily.

The rest streams from there.

I use jquery to detect the data-collections and create the corresponding views, associated to the DOM element. Then I parse the models, and finally I start listening to the events on the collection view.

$(function(){
  $('[data-collection="people"]').each(function() {
    var view = new PeopleView({el: this});
    view.parse();
    view.listen();
  });
});

The parse and listen methods looks like this:

MyApp.PeopleView = Backbone.View.extend({
  collection: MyApp.Collections.People,
  ...
  parse: function() {
    var people = this.$("[data-model='person']").map(function(i,el){
      return new MyApp.Models.Person($(el).data('attributes'));
    };
    this.collection.reset(comments, {silent: true});
  },
  listen: function() {
    this.listenTo(this.collection, 'add',   this.showNewPerson, this);
    this.listenTo(this.collection, 'reset', this.renderEveryone, this);
    this.delegateEvents();
  }
  ...
});

On this particular case I didn't need to create specific Backbone views for each person in the list. But I could have done so in the parse method if I needed to do so.

kikito
  • 51,734
  • 32
  • 149
  • 189
1

You can pre-render your HTML using server or client-side code. Although I'm not really sure why the HTML has to be loaded via AJAX in your case. By any means, Backbone Model or Collection doesn't really know about its definition until you do a fetch on it.

The bottom line is, you can preload a predefined View and alter it with Backbone. In your case, if you want Backbone to operate on your $('.people'), you can simply just append new rows or empty the element before loading new records.

As far as parsing your actual Models from a rendered View, not really sure why you should but you can traverse it using jQuery and select the html() or text().

If you can perform a AJAX request, I don't see why sending the actual Model would be an issue.

var json = { people: [{name: 'Peter' },{name: 'John' }]};

Backbone doesn't place any limitations as far as usage and mainly structural base. Hope this clears up your issue.

Dennis Rongo
  • 4,611
  • 1
  • 25
  • 25
  • I don't think you got me. Once the page is working the usual way I am ok by requesting json to the server (or not. Whatever is easier to do, I don't really care at this point). On the *initial page load*, however, I would like to create the models from the rendered html that the server already provides. – kikito Jan 30 '13 at 14:24
  • You can pre-render your server-side JSON object and have `Backbone` operate on that object. Basically output and treat it as JavaScript objects. { "name" : "<%= @people.name %>" } – Dennis Rongo Jan 30 '13 at 17:01
  • I must be communicating my needs really badly. Please forget about json or ajax. I want to parse html. That is what my question is about. – kikito Jan 30 '13 at 17:05
1

I'm using the Backbone LayoutManager to do this. You can define a fetch() function, where views may be fetched remotely (ie. your server). They are then cached in a Javascript Template Object, for example.

Some code taken from the Backbone BoilerPlate:

var app = { root: "/" };
var JST = window.JST = window.JST || {};

// Configure LayoutManager
Backbone.LayoutManager.configure({

  ....


  fetch: function(path) {
    var done;
    path = path + ".html";

    if (!JST[path]) {
      done = this.async();
      $.ajax({ url: app.root + path }).then(function(contents) {
        JST[path] = _.template(contents);
        done(JST[path]);
      });
    }
    return JST[path];

  }
});
wes
  • 734
  • 6
  • 14
  • Sorry, I don't see how this answers my question. To clarify: I am not trying to obtain model data from the server via ajax. I am trying to parse the html that is already there, and properly initialize the relevant collections with that html. That's all. *Later*, I might want to do ajax and get things from the server, but that's not what I'm asking on this question. I'm interested in the parsing of existing html only here. Thanks. – kikito Jan 30 '13 at 16:10
  • @kikito - yes, I understand. The fetch() function is a property of the LayoutManager's _View_ -- you can fetch _html templates_ from your server this way. Then, cache them and re-uses them using JST – wes Jan 30 '13 at 19:43
  • re-reading other comments -- so, you'd like to send HTML as "if it were JSON"? That is to say, treat it as if it were data and then somehow parse it as such? – wes Jan 30 '13 at 19:48
  • Correct. I guess I could do it in Jquery, but I was wondering if there was a better way of doing that, or if people did that kind of thing at all. – kikito Jan 30 '13 at 20:25
  • Ah, I see. There is a [parse](http://backbonejs.org/#Model-parse) method you can use to deal with the data coming from the server... but you'll have to write your own method --jQuery or whatever to deal with it. But, as your models are requesting JSON after that, it may be better to instead run your method on your "html data" only once, on bootstrap. That said, Im pretty sure there's nothing in Backbone that can do this kind of parsing for you. – wes Jan 30 '13 at 21:44