Seems like backbone.js (or Javascript?) has some strange behaviours I didn't expect. This is my (simplified) Backbone View (in CoffeeScript):
class Application.Views.Sidebar.SidebarView extends Backbone.View
el: "#backbone-sidebar"
template: JST["backbone/templates/sidebar"]
initialize: () ->
# Yes, I am currently skipping addOne and addAll functions,
# because it is not needed to reproduce the problem
Articles.bind 'all', #render
Articles.fetch()
render: =>
$(#el).html(#template())
#
The problem is in the render function: #el is undefined when I run this in the debugger. Also the $() function is re-defined by backbone.js to some getElementById wrapper. When I use window.$("#backbone-sidebar") I get the correct jQuery functionality, but that is just a workaround.
Isn't there a way to get jQuery's $()?
And why is #el undefined?
Thank you for the comments. I am now able to answer the questions myself. To fix the issues, I did prepended window.Sidebar = new Application.Views.Sidebar.SidebarView with $ ->, which makes it wait for the DOM tree to load. This is the CoffeeScript equivalent of wrapping it in $(function() {});
Further explanation:
1. this.el vanishes:
Unlike spine.js (which I used previously), backbone.js seems to look up the selector specified in el. If it cannot find the selector, this.el will be undefined.
2. $() is redefined
If you evaluate $ in the Chrome debugger, it will return some function which does not look like anything useful. I was not able to figure out, what was exactly going on, but $("some-random-css-selector") worked as expected.
Related
This question already has answers here:
Underscore rendering [object HTMLDivElement]
(3 answers)
Closed 8 years ago.
I use backbone inside Rails. I got a very strange behaviour and I try to understand why it happens.
My model has an optional attribute avatar_url. I use it to generate an img tag. I got the following html code:
<img src="[object HTMLInputElement]" class="avatar-bubble">
When I looked at my code, I realize that I also have this static code:
<input id="avatar_url" type="text" class="form-control" />
My conclusion is that from some reason backbone takes also existing DOm objects in account when calculating the value of a given variable.
I wanted to dig inside the template parser and debug it in order to have a better idea of the login it has but couldn't find how to.
My question is what is the logic that uses backbone in order to assign a value to the template variables.
EDIT:
In the view I have implemented a render method which does the following:
render: function() {
var viewData = this.model.toJSON();
this.$el.html( this.template( viewData ) );
return this;
}
There isn't any Backbone "magic" to handle and render templates. The render method of a view is a no-op by default, and having a template method or property is just a recommended convention - again, nothing implemented for you, no magic.
There are frameworks built on top of Backbone, like Marionette, which add that kind of automatic functionality. Without those, you'd have to dig into your Rails code to figure out what's going on. Because Backbone views don't actually add anything to the DOM, or parse templates, until you write the code for it.
(The only thing a Backbone view provides out of the box is a container element, the el, which is generated automatically on instantiation but not attached to the DOM).
I have some code that creates and registers some animations inside a controller. Unfortunately, whenever I register an animation inside a controller, it does not seem to initialise (The function never gets called.)
I have created a plunker to illustrate the issue. In this, I call"createAnim1" outside the controller and the animation functions correctly. I call "createAnim2" inside a controller, and the animation does not function.
createAnim1();
app.controller('main', function($scope) {
createAnim2(); //This animation does not work
});
The createAnim function are in the form:
app.animation('.anim1', function() {
return {
...
}
});
Am I missing something obvious? The $animate and $animateProvider documentation don't seem to contain anything relevant to this.
I assume this has to do with the fact that a compilation has already happened, and some step required to initialise the animations is being missed? Is there some way to get around this?
PS: I have only been using angular since last week, so I'm not well versed on the internals.
Edit:
After the comments from Alex, I think it is worth clarifying what I am trying to do:
I have some code which generates css class animation selectors and appends them to the document head. If the browser does not support transitions, the code generates a jquery animation equivalent. I have tried to create a factory to expose this code via DI, and it is when this code is run (From inside a controller) the animation is never able to trigger (The initialisation code is never run).
I tried to create a Marionette module. When I access the template using jQuery in the module, it returns undefined. It happens for with or without default 6 arguments.
Note:
in the main.js this variable returns properly only in the module it returns undefined.
//App.module("LineModule", function(LineModule, App, Backbone, Marionette, $){
App.module("LineModule", function(LineModule){
console.log('LINE DRAFT : ' + $('#line-grid-content-template').html()); // undefined
});
What could be the reason? how can I fix it?
Thank you.
This will normally happen if #line-grid-content-template has not yet been added to the DOM. A few places to first check are, make sure it has the right ID (it's not a class or a miss spelling) and also that who ever is responsible for rendering the div has rendered it before you try to access it via the jQuery selector.
I am seeing some strange behavior that I'm hoping someone can explain.
From the render method in a backbone view I have been attempting to do the following:
this.$(".msg").colorbox();
And
this.$el.find(".msg").colorbox();
However in both cases, although the msg elements are located, when trying to invoke the colorbox method on the returned elements I get an exception that the method is not defined.
However when I use:
$(this.el).find(".msg").colorbox();
All is well. Is anyone aware of why this might be?
It is a common issue. Surely colorbox is a jQuery plugin. The jQuery plugin is injected until the View's Element is added to the DOM of your Page.
I mean, your code is normal to fail with the natural behavior.
$('body').append( view.render().el );
But if you do this, it will works:
$('body').append( view.el );
view.render();
Third party Backbone.js Plugins has a method "named" onRender that is executed after render the View(and assuming that added to the DOM). But if you do not work with additional Backbone.js Plugins just be sure to call the colorbox until your View was added to the DOM.
Shooting in the dark...
this.$el and $(this.el) are differente instances even if both make reference to the same DOM element.
Maybe since the moment Backbone did precompile this.$el = $(this.el) to the moment you call this.$el.colorbox() something has happend so this function is not available and it has never been available in the instance this.$el.
I don't know what is colorbox() but if this is part of a third part jQuery plugin can be important the order in that your JS code is loaded?
I've been looking at some examples of backbone.js based application. I notice that in some (such as this example below) the underscore function _.bindAll() is used:
initialize: function (args) {
_.bindAll(this, 'changeTitle');
this.model.bind('change:title', this.changeTitle);
},
whereas in others (such as the todo app below) do not:
initialize: function() {
this.model.bind('change', this.render, this);
this.model.bind('destroy', this.remove, this);
},
What is the purpose of _.bindAll() in this context, and is it necessary?
_.bindAll() changes this in the named functions to always point to that object, so that you can use this.model.bind(). Notice that in your second example, a third argument is passed to bind(); that's why using _.bindAll() isn't necessary in that case. In general, it's a good idea to use for any methods on the model that will be used as callbacks to events so that you can refer to this more easily.
In Detail: _.bind(ctx, 'method')
Takes your method, creates a copy with the context bound to 'ctx' and adds the copy as property.
This is a workaround for jQuery.bind() not allowing you to pass in a context.
JQ will always call the callbacks with a undefined context. Backbone is built on jQuery.
See here: http://backbonejs.org/#FAQ-this