Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

backbone Inverse view

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?

like image 649
kikito Avatar asked Dec 27 '22 10:12

kikito


2 Answers

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™.

like image 137
jevakallio Avatar answered Dec 29 '22 02:12

jevakallio


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.

like image 38
kikito Avatar answered Dec 29 '22 00:12

kikito