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?
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?
Share Improve this question edited Jun 24, 2015 at 15:32 realph asked Jun 24, 2015 at 9:47 realphrealph 4,68114 gold badges52 silver badges109 bronze badges 5- Not sure what you mean by "injecting" do you mean you are requiring in the model and instantiating it in the view? This is ok practice and you would be able to access whatever you want on it. You haven't showed the important part of the code so I cannot see what you did wrong – nanobar Commented Jun 24, 2015 at 15:07
- @DominicTobias Sorry, I meant requiring and instantiating like you said. I’ve updated the code to provide a bit more context. – realph Commented Jun 24, 2015 at 15:32
-
I think you need to change the order of your dependencies in the example, should be:
($, _, Backbone, JST, ProjectModel, CountriesModel)
. – sergdenisov Commented Jun 27, 2015 at 16:35 - what does your models/projects look like? as @SergeyDenisov points out, your require statements are reversed.Whats more important here though is how you instantiated the objects, adding dependencies in a require statement doesnt mean it will be used. show the constructor code and the objet instantiation code with the arguments passed in – michael truong Commented Jul 2, 2015 at 3:30
- remend taking a look at Marionette. extends Backbone and makes reusing models a breeze. Not too sure why you're injecting instead of including the model with the View constructor; use a Controller one level higher to hold the mon model and create the different views – softwarenewbie7331 Commented Jul 3, 2015 at 8:12
3 Answers
Reset to default 4 +25I 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 ponent 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 ponents to perform eg. server calls or specific calculations that might be mon for different views. Handling this mon 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.
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
}
});
TBH I don't really get why you are using your model as a deps. A good view should abstract from the model and the model should be attached to the view from the route.
// this goes in your view file
var CountryView = Backbone.view.extend({
// here goes all the view logic to display the model data
// you can refer to your model using this.model
})
...
// in the router file
var myModel = new CountriesModel();
var router = Backbone.router.extend({
routes: {
"": home,
"home": home
},
home: function (){
var view = new CountryView({ model: myModel});
view.render(); //if you didn't rendered the view in the initialize method
}
})
You can access another model in the view attaching it at runtime in the router
...
// in the router file
var myModel = new CountriesModel();
var anotherModel = new ProjectModel();
var router = Backbone.router.extend({
routes: {
"": home,
"home": home
},
home: function (){
var view = new CountryView({
model: myModel
anotherModel: anotherModel
});
view.render(); //if you didn't rendered the view in the initialize method
}
})
and in CountryView attach it in the initialize function
initialize: function (options){
this = _.extend(options, this)
// or
// this.anotherModel = options.anotherModel
// this.model = options.model
}
But you shoul not mix models in a view, just have another view for the projectModel and use them both when needed
// something like
var countryView = new CountryView({model: myModel});
var projectView = new ProjectView({model: anotherModel});
and render them together from the router