socratic_singh
socratic_singh

Reputation: 183

Backbone model attributes raising TypeError when being called

I am trying to access items of an array in a model and it's raising errors but seems to be finding the correct attribute and writing it to the console still

class GD.Views.Place extends Backbone.View
  template: JST['mobile/templates/place']
  initialize: ->
    @model.on('change', @render, this)
  render: ->
    $(@el).html(@template(place:@model, open:@openNow() ))
    console.log @model.get("coordinates")
    console.log @model.get("coordinates")[0]
    console.log @model.get("coordinates")[1]
    console.log("done")
    this
  openNow: ->
    ...

The view is not rendering on-screen and I am seeing this message in my console

Uncaught TypeError: Cannot read property '0' of undefined
  GD.Views.Place.Place.render
  GD.Routers.Main.Main.place
  _.extend.route
  (anonymous function)
  _.some._.any
  _.extend.loadUrl
  _.extend.start
  window.GD.init
  (anonymous function)
  jQuery.Callbacks.fire
  jQuery.Callbacks.self.fireWith
  jQuery.extend.ready
  DOMContentLoaded
[51.4528837, -0.9739059999999428]
51.4528837
-0.9739059999999428
done

The error message is referring to the 2nd and 3rd console.log lines above. It also seems to be raising errors when accessing further attributes in embedded hashes.

I don't understand why this is happening or how to work around it. Thanks for the help/patience in advance!

Upvotes: 1

Views: 1135

Answers (1)

mu is too short
mu is too short

Reputation: 434985

The usual pattern with a Backbone view is this:

v = new V
$(something).append(v.render().el)

If you combine that with a common pattern for initializing a model:

m = new M
m.fetch() # asynchronous!

then you can get this:

m = new M
m.fetch()
v = new V(model: m)
$(something).append(v.render().el)

That would allow the v.render() inside the append call to happen before the model has been fetched from the server. In your case, that would mean that @model.get('coordinates') would be undefined in the v.render() call above and you would get:

Uncaught TypeError: Cannot read property '0' of undefined
...

then, the m.fetch() gets its data from the server and triggers a "change" event, that event would then call render on your view and you would get this:

[51.4528837, -0.9739059999999428]
51.4528837
-0.9739059999999428
done

That sequence of messages is exactly what you're seeing in your console.

I'd guess that you'r doing something like this (as above):

m = new M
m.fetch()
v = new V(model: m)
$(something).append(v.render().el)

Try checking to see if @model is actually loaded inside render and throw up some sort of "loading..." message if it isn't; then let the "change" event trigger the "real" rendering.


PS: You can use @ instead of this in CoffeeScript:

initialize: ->
  @model.on('change', @render, @)
render: ->
  #...
  @

You could also use a fat arrow (=>) to define your render method and not worry about supply a context to @model.on:

initialize: ->
  @model.on('change', @render)
render: =>
  #...
  @

Furthermore, if you're using a recent version of Backbone, you have a $el in your view instances so you could:

render: =>
  @$el.html(...)
  #...
  @

Upvotes: 4

Related Questions