I want to create a simple list of user profiles. The profiles will be brought in via a JSON file and displayed as a list. Seems so simple!
I’ll start by creating the model:
var Profile = Backbone.Model.extend();
I’ve seen it done this way in many examples. Sometimes default values are added, but usually it’s just this simple declaration. According to the backbone.js documentation, it’s also possible to add an initialize function and to add validation. I’m going to keep things as simple as possible for now though.
Next, I know I want a collection of Profiles, so I will create that in its most simple form:
var ProfileList = Backbone.Collection.extend({
   model: Profile
});
That looks to me to be a collection of individual Profiles.
I know I want to generate the models based on the values in an external JSON file. According to backbone.js: “Set the url property (or function) on a collection to reference its location on the server. Models within the collection will use url to construct URLs of their own.” As a result, I have this:
var ProfileList = Backbone.Collection.extend({
   model: Profile,
   url: '/json/profiles.json'
});
Does the collection automatically go out and grab the JSON? I don’t think I should need to explicitly call fetch() because the documentation says: “Note that fetch should not be used to populate collections on page load — all models needed at load time should already be bootstrapped in to place.” And further: “fetch is intended for lazily-loading models for interfaces that are not needed immediately.” So if I create an instance of ProfileList the models should be created automatically, correct? I’ll try it:
var profiles = new ProfileList();
console.log(profiles);
This results in an empty object. Am I not giving it enough time to grab the JSON? If I could trigger the call to console.log on a “success” event or something similar, that would be a good way to know the collection has grabbed the data.
Changing my code to this produces different results:
var profiles = new ProfileList(); 
profiles.fetch();
console.log(profiles);
But I still think I need to trigger console.log with an event. When I look at the results using Firebug, it still shows me an empty object, but now when I click through to see the empty object in more detail I can see that the data is there. It had showed me more emptiness when I clicked through it before adding the explicit call to fetch().
Here is my attempt to bind the call to console to when the collection grabs the data (note that this at least partially derived from this StackOverflow example):
var profiles = new ProfileList(); 
profiles.fetch();
profiles.bind('reset', function () { console.log(profiles); });
And indeed this works. The backbone.js documentation says I shouldn’t call fetch explicitly on page load though. So how do I get the data without calling fetch()?
I found a StackOverflow thread that addresses this exact issue. The answers are not crystal clear to me though. I think the conclusion is that you can either send in a static reference to an initial set of data, then call fetch() as needed to grab more, or you call fetch explicitly like I am doing.
I suppose I could change my JSON file to create a variable holding all my data, then pass it directly to the collection. That avoids a server call, but it doesn’t seem right. For one thing it will be a little cumbersome to switch to a dynamic data source (e.g. a db) down the line. I guess it just seems less flexible in general.
I’ll stick with explicitly calling fetch() for now.
Now I need to display my data. I need to create a view to display the profiles. The view should use an underscore template I have created and the rendering should be triggered by the ‘reset’ event of the profiles collection. I have a div already on the page and I’d like the newly created DOM elements to be inserted into that div.
The ID of the element into which I want the new DOM elements inserted is “profiles,” so the first line of my view is :
el: "#profiles"
My understanding is that by setting the ‘el’ property I am telling backbone there is an existing DOM element I want to use. If instead I were to set values for “tagName”, “className”, “id” or “attributes” properties, backbone would create a new element with those attributes.
I know that to specify the template to be used by the view I do this:
template: _.template($('#profileTemplate').html()),
Where the template with the id ‘profileTemplate’ looks like this:
<script id="profileTemplate" type="text/template">
   <div class="profile">
      <div class="info">
        <div class="name"><%= name %></div>
        <div class="title"><%= title %></div>
        <div class="background"><%= background %></div>
      </div>
  </div>
</script>
I think my view can have a simple render function that looks like this:
render: function(eventName) {
   _.each(this.model.models, function(profile){
      var profileTemplate = this.template(profile.toJSON());
      $(this.el).append(profileTemplate);
   }, this);

   return this;
}
So the complete view looks like this:
var ProfileView = Backbone.View.extend({
   el: "#profiles",
   template: _.template($('#profileTemplate').html()),
   render: function(eventName) {
      _.each(this.model.models, function(profile){
         var profileTemplate = this.template(profile.toJSON());
         $(this.el).append(profileTemplate);
      }, this);
      return this;
   }
});
Indeed, this works as I had hoped.

0 comments :

Post a Comment

 
Top