I'm using experimenting with .extend() to set up my views and initialise them with. I've found it's convenient to assign config variables to view objects nested deep within a hierarchy.
My problem is that my Views lose their this context. This becomes the ctor object which I asume is the constructor. How can I fix this?
My coffeescript is below. The first class would be nested deep within a tree, the second is at the top level where the application boots up:
# This is a child somewhere deep within a tree of views.
class View extends Backbone.View
initialize: ->
console.log # # returns object ctor
MyView = View.extend({
initialize: ->
# do config stuff then init prototype
App.Views.MyView.prototype.initialize()
})
view = new MyView
Two things:
First, and not as importantly, you can use
class MyView extends View
instead of View.extend. CoffeeScript classes and Backbone classes are interoperable.
Second—and this is the important part—instead of
App.Views.MyView.prototype.initialize()
you should simply use the CoffeeScript keyword
super
That effectively does the same thing, but also ensures that the function is called in the correct context. Bonus: It also passes in all of your function arguments for you.
If you're curious, super here compiles into
initialize.__super__.constructor.apply(this, arguments)
(where __super__ is a reference to the superclass that's set by both CoffeeScript's extends). Read about apply at MDN.
My coffe script isnt so hot but can you call the function sending the view you want to be the value for this in as the first parameter
I suppose in standardish js
var view = null;
MyView = View.extend({
initialize: function() {
// do config stuff then init prototype
App.Views.MyView.prototype.initialize.call(view)
}
})
view = new MyView;
Im not sure what you have access to at that point or the generated js either.
The point being if you have access to what should be the value for this when you call the function you should be able to pass it in.
I would check myself but i don't comprehend coffeescript :)
Related
I'm trying to retrieve the model, because I need to access certain attributes, but I cannot. I'm inside a view, which extends another one. This is how the view looks like:
var ImageGridControlView = GridControlView.extend({
//.... stuff ....
alert(this.model.get('property')) //This gives me an error in console and nothing is alerted.
});
In the class that the above code extends, the model can be retrieved, like this:
var GridControlView = ControlView.extend({
//.... stuff ....
alert(this.model.get('property')) //This shows the property value correctly
});
Why can't the model be retrieved from ImageGridControlView? What is the persistence of models across different classes, children, etc? I'm new to Backbone, and the official documentation only covers models superficially.
Your snippets are too small to actually find the problem, so we have to just guess. Most likely candidates are:
you are accessing this.model inside a function triggered by an event binding that is not properly bound, so this isn't actually your view instance. It could be a the source DOM element of the window object instead.
You aren't passing a model property in the options object provided to the view's constructor
Many of the views in my application need to be "collapsible". To the user this means that you can click an arrow to collapse or expand the view's contents.
When creating a view I need to be able to easily say, "This view should be collapsible," and then run the appropriate setup code (which essentially means adding the .collapsible class to the view's wrapper and inserting a dom element that looks like this: <div class="toggle"></div>
Suggestions on ways to pull this off seamlessly? I'm currently using Backbone, Backbone.Marionette, and Underscore.
I do this with another application that doesn't use Backbone. In that application every action results in a page refresh, so I just use jQuery to look for all elements with the .collapsible class and do my setup that way.
EDIT:
I'm using Backbone.Marionette.CompositeView for these particular views, if that helps.
I've done similar thing in my project by extracting such functionality into mixins. There're different approaches to implementing mixins in Backbone. Take a look here or here
You can create parent view that extends from Marionettes compositeView and add your common functionallity there, and have your project views extend from this parent view.
var CollapsibleView = Backbone.Marionette.CompositeView.extends({
variable1: 1,
var2: true,
initialize : function() {
// your code here
},
helperfunction : function () {
// other helpful function
}
});
var MySpecificView = CollapsibleView.extends({
mySpecificFunction : function () {
// some specificView functionality
}
});
var myProjectView= new MySpecifcView();
myProjectView.helperfunction(); /// function from the parent
myProjectView.mySpecificFunction(); /// function from the specificView
/// you also have the functionality added on the initialization of the collpasibleView
For context I use coffeescript. If I create a base model that extends Backbone.Model and I create another class (i.e. App.Models.Project extends App.Models.Base).. everything works as expected.. what would be the difference to an instance of Project if in this base class I wrote:
initialize: ->
super
console.log 'hi'
and just plain
initialize: ->
console.log 'hi'
Without spending too much time, it seems in my console an instantiated object acts as expected in both cases.. I hear you should 'always call super' here but I don't know what I'm getting..
Backbone.Model.initialize does nothing.
From the annotated source code, you can see the empty function defined in Backbone.Model
initialize: function(){}
It's upto your model to override. Usually, model variables are set here. Whenever you create a model object, initialize is called internally.
The same principle holds good when creating Views and Collections too.
I need to execute some code both on initialize and render methods, but as I understand, I canot just modfy them directly when using Chaplin - when I define my own initialize method, my routes stop working.
I also tried afterInitialize() but it seems its not meant to be overrided: https://github.com/chaplinjs/chaplin/issues/168#issuecomment-8015915
[...] but as I understand, I canot just modfy them directly when using
Chaplin
You can modify them directly as long you appropriately delegate to the extended prototype.
As you haven't tagged your question javascript or coffeescript, the following are two solutions for each one. First up is javascript. Notice how we must explicitly invoke the extended function.
var View = Chaplin.View.extend({
initialize: function(options) {
// Add code here ..
// The below invokes the initialize function of the
// base Chaplin.View class in the context of
// this class
Chaplin.View.prototype.initialize.call(this, arguments);
// .. or here
}
});
Next is coffeescript, which makes this kind of thing significantly easier.
class View extends Chaplin.View
initialize: ->
// Add code here ..
// The below auto-magically invokes the
// initialize method of the base class
super
// .. or here
I'm building a single page app with Backbone and Requirejs.
For now I've been returning a new instance from my module files, ending the files with:
return new moduleName;
This worked ok until I had to pass arguments to the initialize method of a collection. Since the initialize is called when the instance is created, I took the "new" away from the return statement.
return mymoduleName;
And instantiated the collection with parameters in my router:
myCollection = new library({paramname: "value"});
This indeed creates an instance of "library" - a collection introduced in my router's define block, but how do I pass it to the view responsible of rendering it?
My view class has the same dependency in its define block and in its initialize I bind it to the collection's reset:
this.collection = library;
_.bindAll(this, 'render');
this.collection.bind('reset', this.render);
This worked before taking away the "new" from the collection class (which on retrospective makes no sense at all!) but now the view never gets rendered because it was instantiated in the router - how do I pass that same instance to the view?
Don't set the collection in its initialize method, but wherever you instantiate your view (in your case, the router).
var myCollection = new library({paramname: "value"});
var myView = new myCustomView({
collection: myCollection
});
If myCollection is a variable you need to access from other places as well, you can set it on the router (myRouter.myCollection = ...), you could create a model that holds global objects (MyAppGlobals) or you could simply have a global variable (not recommended).