DEV Community

akamittal
akamittal

Posted on • Originally published at fix.code-error.com

Backbone.js Model defaults and parse

I’ve got this Backbone.Model representing a Google Books API volume:

var Book = Backbone.Model.extend({

    defaults: {
        volumeInfo : {
            title: 'n.a.',
            authors: 'n.a.',
            publisher: 'n.a.',
            publishedDate: 'n.a.',
            imageLinks : {
                smallThumbnail: '/unavailable.jpg'
            }
        }
    },

    parse: function(resp) {
        if (resp.volumeInfo.authors) {
            resp.volumeInfo.authors = resp.volumeInfo.authors.join(',');
        }
        return resp;
    }
});
Enter fullscreen mode Exit fullscreen mode

Which is fed to this template:

<script type="text/template" id="bookCollectionRow">
    <tr>
        <td><img class="thumbnail" src="<%= volumeInfo.imageLinks.smallThumbnail %>" /></td>
        <td><a target="_blank" href="<%= volumeInfo.canonicalVolumeLink %>"><%= volumeInfo.title %></a></td>
        <td><%= volumeInfo.authors %></td>
        <td><%= volumeInfo.publisher %></td>
        <td><%= volumeInfo.publishedDate %></td>
    </tr>
</script>
Enter fullscreen mode Exit fullscreen mode

Upon parsing the template, when a volume JSON does not contain an imageLinks I receive this error:

Uncaught TypeError: Cannot read property 'smallThumbnail' of undefined.
Enter fullscreen mode Exit fullscreen mode

I know I could fix it by checking with an if in the Model or in the template but what’s the purpose of defaults model property then? Does that work only if not overriding parse?

Solution

A few things. First, you shouldn’t have nested objects as backbone model attributes in general – it can be OK if you can always treat the attribute atomically, but this is a perfect example of when you can’t. From a data-model perspective, imageLinks should be its own backbone model class, as should volumeInfo.

Second, if defaults is an object literal ({}) instead of a function, the same object is used as the default attrs for each model instance. I think you want this:

defaults: function(){
    return {
        volumeInfo : {} // should be new VolumeInfo({}) imo
    };
},
Enter fullscreen mode Exit fullscreen mode

But the data model is the bigger issue – .defaults doesn’t do the kind of nested-object-template thing you seem to be going for, and for good reason: it doesn’t work well, this will just be the first of many gotchas you’ll run into if you don’t keep your instance data pretty flat.

Top comments (0)