Backbone 0.9.2 view initialize not being called - backbone.js

I am new to Backbone.js. I am using CoffeScript on a v0.9.2 app. The app works "fine" but the initialize() method of the views is not being called. Events are not being properly binded either. I am trying to figure out why this is not the case. I am using other (manual) ways to bind events to elements but that should not be the case.
The app is instantiated with this:
window.Site =
Models: {}
Collections: {}
Views: {}
Routers: {}
init: ->
new Site.Routers.MyRouter()
Backbone.history.start()
$(document).ready ->
Site.init()
The router:
class Site.Routers.MyRouter extends Backbone.Router
routes:
'': 'index'
initialize: ->
# some code here (this IS being called)
index: =>
# this is also being called since I am trying mysite.com/
view = new Site.Views.MyView()
$('#someId').html(view.render().el)
The view:
class Site.Views.MyView extends Backbone.View
template: JST['views/index']
events:
'click .someElement': 'someMethod'
inititalize: ->
console.log "hello" # NOT CALLED
_.bindAll #
#
render: =>
# draw stuff (this works)
#
The view gets drawn fine. Why is initialize not being called?
Thanks!

You have to spell initialize correctly =p
inititalize: -> # should be `initialize: ->`
console.log "hello" # NOT CALLED

For future readers, also check you don't have two initialize functions.
Backbone.View.extend({
initialize: function () {
// not called
},
// stuff
initialize: function () {
// overwrites previous
}
})

I don't write CoffeeScript, but the only place I see an instance of your view initailized is in the router:
index: =>
view = new Site.Views.MyView()
I suspect the router's index is not being called and as a result your view's initialize isn't being called. Extending a view doesn't create an instance of the view, rather it creates a customized definition of a view.
HTH.

Related

Uncaught NoElError: An 'el' must be specified for a region

I'm trying to use this code for view animation and calling it BaseView:
https://gist.github.com/brian-mann/3947145
then extending view like this:
define(['underscore',
'handlebars',
'views/BaseView',
'text!templates/components/login.tmpl'
], function (
_,
Handlebars,
BaseView,
loginTemplate
) {
'use strict';
var LoginView, errorMap;
LoginView = BaseView.extend({
compiledTemplate: Handlebars.compile(loginTemplate),
events: {
'submit #loginForm': 'login'
},
initialize : function(options){
this.proxyLoginSuccess = options.loginSuccess;
this.errorMap = options.errorMap;
}...
});
return LoginView;
});
It is giving me this error: Uncaught NoElError: An 'el' must be specified for a region.
I tried to remove this.ensureEl(); but doesn't make any difference. Appreciate any help.
You seem to be unclear about some Marionette concepts. The code you linked isn't a view, it's a Marionette Region, and is therefore used to show views, not to be extended from as in your code. This is how you would use it (e.g.):
myApp.addRegions({
fadeRegion: FadeTransitionRegion.extend({
el: "#some-selector"
})
});
Then, you instantiate a view instance and show it:
var myView = new LoginView({
el: "#another-selector"
});
myApp.fadeRegion.show(myView);
In any case, your view needs to have an el attribute defined, either in the view definition, or when it gets instantiated (as above).
If you're still confused about the attributes and specifying them in the view definition or at run time, I'd suggest you read the free preview to my Marionette book where it's explained in more detail.

How to render item views for a collection including an add new view?

I don't understand why the DOM is not updated in the collection view render:
class FastTodo.Views.TodoItemsIndex extends Backbone.View
template: JST['todo_items/index']
el: '#main'
render: ->
$(#el).html #form_view.render()
#collection.each #renderOne
renderOne: (item) ->
console.log(#)
console.log(#el)
$(#el).append "model data"
initialize: ->
#collection = new FastTodo.Collections.TodoItems()
#form_view = new FastTodo.Views.AddTodoItem collection: #collection
#collection.bind 'reset', =>
#render()
#collection.on 'add', (item) =>
#renderOne(item)
#collection.fetch()
The idea is that #main first get a view with add new form, and then the collection is appended to #main.
How would I do this?
The output of the view in the console looks like:
1) For #collection.each #renderOne to work correctly you need to bind your renderOne method to the view instance like this: renderOne: (item) => (notice the fat arrow), because otherwise it is invoked in the global context (that's why you see these Window objects in your console.
2) DOM element, not the view itself, should be inserted into DOM, so $(#el).html #form_view.render() should be written as #$el.html #form_view.render().el (the render method should return the view instance according to the backbone community convention).
Other looks fine and it should work this way.
You may wish to refer to some posting about context in js to deeper understand the subject (this one for example).
btw you can write less code for some things. i.e. this
#collection.bind 'reset', =>
#render()
#collection.on 'add', (item) =>
#renderOne(item)
can become this
#collection.on 'reset', #render
#collection.on 'add', #renderOne
but you should remember to bind your render method with the fat arrow in this case.

BackboneJS + RequireJS allow view to act on a collection using global event dispatcher

Update: Problems solved, case closed.
I'm still having problems getting one part of my code to work.
My view now listens to the collection for updates, and what should happen is:
ListView listens to Results Collection
Results are synced
ListView creates an ItemView for each Result
ListView (ul) appends each ItemView (li)
Everything seems to work fine, up until the final step.
The function in ListView that is supposed to add the results to a list does not have access to the ListView's element.
I can create an ItemView, and retrieve it's element "<li>", but the ListView's "<ul>" cannot be referred to within the function.
Sample code bits from ListView:
el: $('.result-list'),
initialize: function() {
this.listenTo(this.collection, 'add', this.addOne);
},
addOne: function(result) {
view = new ItemView({ model: result });
this.$el.append(view.render().el);
},
In the above code, the variable view exists, as does it's element, but "this" doesn't refer to the ListView anymore.
Problem below solved
What I'm trying to accomplish is having a View module (search) able to trigger an event in a Collection (results).
When the Search View is submitted, it should pass the input field to the Collection's fetch method to retrieve results from the server. Currently, I can trigger a function from the View, but the function does not have access to any of the Collection's methods.
Previously, I had the View/Collection refer to each other directly by their variable names.
Since I have separated the code into modules, the View/Collection cannot access each other directly anymore.
Here is some of the code: (written in Coffeescript)
app.coffee - global_dispatcher is applied to Backbone
define [
'jquery'
'underscore'
'backbone'
'cs!router'
], ($, _, Backbone, Router) ->
# global_dispatcher added to all Backbone Collection, Model, View and Router classes
dispatcher = _.extend {}, Backbone.Events, cid: 'dispatcher'
_.each [ Backbone.Collection::, Backbone.Model::, Backbone.View::, Backbone.Router:: ], (proto) ->
_.extend proto, global_dispatcher: dispatcher
new Router()
router.coffee - This is where I'm having trouble. The function for 'getResults' is triggered, but the collection 'results' is not accessible from here.
define [
'backbone'
'cs!views/resuls/list'
'cs!views/results/search'
'cs!collections/results'
], (Backbone, ListView, SearchView, Results) ->
Backbone.Router.extend
routes:
# URL routes
'': 'index'
index: ->
results = new Results
new ListView { model: results }
new SearchView
#global_dispatcher.bind 'getResults', (data) ->
console.log results
search.coffee - View which triggers the event, it will successfully trigger the event and pass the correct arguments.
define [
'jquery'
'backbone'
], ($, Backbone) ->
Backbone.View.extend
events:
'submit #search-form': 'submit'
submit: (evt) ->
evt.preventDefault()
phrase = #.$('input').val()
#.$('input').val('')
args = name: phrase
#global_dispatcher.trigger 'getResults', args
If I'm understanding your problem correctly it's not hard to solve. Here's some dummy code to illustrate:
var Results = Backbone.Collection.extend();
var Search = Backbone.View.extend({
someEventHandler: function() {
// The next line accesses the collection from the view
this.collection.fetch();
}
});
var results = new Results();
var search = new Search({collection: results});
If you want your view to do something after the results come back, just bind an event handler on the collection:
var Search = Backbone.View.extend({
fetchResposne: function() { /* do something*/},
someEventHandler: function() {
// The next line accesses the collection from the view
this.collection.on('sync', this.fetchResponse);
this.collection.fetch();
}
});

Mapping backbone events in a separate backbone class file

I'm using CoffeeScript with a Backbone view class. When I include the class in the same page as the html everything works fine. When I use a separate file and export the class, it initialises but the events aren't mapped.
The class file is:
root = exports ? this
class root.AppView extends Backbone.View
el: $("#app")
events:
'click #appBtn1' : 'handleEvent'
'click #appBtn2' : 'handleEvent'
initialize: =>
alert 'init'
handleEvent: =>
alert 'event'
Only the initialize function fires. What do I need to alter to map the events when the code is in a separate class?
The problem is that the function
$("#app")
runs immediately when you define the class. So when the class is in a separate file, the #app element doesn't (necessarily) exist in the DOM yet.
What you should be doing is using a selector string instead, which Backbone will pass to the $ function when the class is instantiated:
el: "#app"

Representing existing HTML as Backbone.js data structures in CoffeeScript

I'm having a rough time wrapping my head around this.
I have an HTML list, and I want to use Backbone.js to handle events on those list items. Here's what I've got so far. This is a simplified scenario to help me better understand how to structure a larger application. For my example, I simply want to ingest an existing HTML list into the Backbone structure, and handle click events through the Backbone view.
I'm getting an error related to using #model in the view, but I'm fairly certain I'm misunderstanding things conceptually here.
CoffeeScript:
$ ->
class Item extends Backbone.Model
name: null
class ItemList extends Backbone.Collection
model: Item
class ItemView extends Backbone.View
tagName: 'li'
initialize: =>
#model.bind('change', this.render)
#model.view = this
events:
'click' : 'clicked'
clicked: ->
console.log 'clicked'
render: =>
this
class ItemListView extends Backbone.View
el: $('ul#test')
initialize: =>
$('li', #el).each(#addItem)
addItem: (item) ->
item = new ItemView({ el: item })
render: =>
this
Items = new ItemListView
HTML:
<ul id="test">
<li>Hi thar</li>
<li>Yeah</li>
<li>OK</li>
</ul>
Here's a jsfiddle I started earlier: http://jsfiddle.net/Saxx4/
I never really like CoffeeScript (Javascript is so nice, why replace it?), but it looks like there are a few issues here:
You're getting an error on #model because you never set it on the ItemView. This doesn't happen automatically - you have to either instantiate the view's model in initialize() or pass it into the constructor, e.g.:
addItem: (item) ->
model = new ItemView({
el: item,
model: new Item({
// assuming you might want the list item text
// in the model data
text: $(item).text()
})
})
You usually just want to specify a selector in el, not a jQuery object - otherwise the DOM might not be ready when you load your Backbone code: el: '#test'
You need to pass an options object to the ItemListView constructor, not just a single argument, no matter what you do in initialize():
class ItemListView extends Backbone.View
initialize: (opts) =>
opts.items.each(#addItem)
// ...
Items = new ItemListView({ items: $('ul#test li') })

Resources