Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Backbone: Use Model's Data and Functions in View

I'm fairly new to Backbone and was wondering how to access a model's data and functions from a view that injects the model as a dependency.

My model looks like this:

countries.coffee

define [
  'underscore'
  'backbone'
  'parse'
], (_, Backbone, Parse) ->
  'use strict';

  class CountriesModel extends Parse.Object

    countries: ['GB','US','FR','JP','WL','ZM','NG']

    returnCode = (code) ->
      return code

And my view looks like this:

country.coffee

define [
  'jquery'
  'underscore'
  'backbone'
  'templates'
  'models/projects'
  'models/countries'
], ($, _, Backbone, JST, CountriesModel, ProjectModel) ->
  class CountryView extends Backbone.View

    ...

    console.log countries

    returnCode(4)

I'm injecting the CountriesModel as a dependency, but when I call the function or log the countries I get the following error:

Uncaught ReferenceError: returnCode is not defined

I can't work out what I'm doing wrong. Any help is appreciated. Thanks in advance!

UPDATE

I’ve updated the code above to provide a bit more context.

I’m trying to create a re-usable model (CountriesModel), so I can access that countries array and the returnCode function on different views across my app. But I can’t work out how to access them on my CountryView.

My CountryView is already requiring a model ProjectModel, and I’m able to call functions and arrays from ProjectModel like this:

this.model.exampleArray
this.model.exampleFunction()

I can’t work out how I call functions or arrays from my CountriesModel.

Anyone know what I’m doing wrong?

like image 455
realph Avatar asked Jun 24 '15 09:06

realph


2 Answers

I think that in this particular case, it would be useful for you to create a model "countryModel" and a backbone collection "countriesCollection". But that might not be the nature of your question (your update indicates that you struggle with reusability of the model) so I won't take that into account in my answer.
I don't know coffeescript, but I'll elaborate using Javascript.
The answer is indeed technically to pass models via the options parameter to the view during instantiation.

I think that it is overall a good idea to use presenter objects to manage specific views groups. This object would instantiate views that are related, and as you mentioned it, allow injecting an instance of the countriesModel into this presenter.
Imagine hypothetically that you have a webapp that renders a map and list with places that require the model you describe for some reason. You could have code that looks like this:

var countriesModel = new CountriesModel(); 

var headerPresenter = new HeaderPresenter(); 
var mapPresenter = new MapPresenter(countriesModel); 
var listPresenter = new ListPresenter(countriesModel); 

What happens is that you instantiate the model only once, and inject the instance into the presenters that require it.
In the presenter object you can immediately access the properties / methods set on the passed model.

This approach allows you to quickly identify which presenters require the re-usable model.
Should you require the component in new presenters as well, it is easy to pass it in.
Then, within the presenter you can still choose to which views specifically you want to send the model.

Eg. list presenter:

function listPresenter(countriesModel){ 
    this.listView = new ListView({ "model": countriesModel); 
    //More views can be added with the same model instance 
}; 

Either from within the views or the presenters, you are able to listen to events on the model, execute its methods and re-render the views.
Personally I manage this logic from the presenter, because this is the place where I use other injected services and components to perform eg. server calls or specific calculations that might be common for different views. Handling this common logic for different views is easily done by passing an event aggregator to each of the view instances. The views trigger custom events to do what is required and the presenter listens to custom events, executes the required logic and (re-)renders views.
I prefer to keep the view clean and focused on DOM interaction / DOM event binding.

Sidenote: Backbone Marionette offers an app level event aggregator, which saves you the pain of passing in event aggregators to every view individually.
It is also a very convenient lib to render views from the presenter by using syntax like:

var someView = new SomeView(); 
var region = new Marionette.Region({ el: "#some-region" }); 
region.show(someView); 

Re-showing views by using Marionette regions is memory-safe.

Hopefully this is of any help.

like image 95
html_programmer Avatar answered Oct 31 '22 03:10

html_programmer


In your view you can instantiate models other than the one which is referenced at this.model. E.g:

var SomeView = Backbone.View.extend({

    initialize: function() {
        this.countriesModel = new CountriesModel();

        // Anywhere in your view do stuff with `this.countriesModel`. E.g:
        // this.listenTo(this.countriesModel, ...)
        // this.countriesModel.fetch()
        // etc
    }

});
like image 3
Dominic Avatar answered Oct 31 '22 05:10

Dominic