I'm trying making multi paged app using backbone, marionette and coffeescript.
apps/home.coffee
define [
'App'
'./index/index'
],
(App, HomeIndex) ->
class HomeApp extends App
constructor : () ->
super
console.log typeof HomeIndex #return object
#homeIndex = new HomeIndex() #TypeError: HomeIndex is not a constructor
#initialize()
initialize: ->
console.log 'app initialize'
App.contentArea.show homeIndex
apps/home/index/index.coffee
define [],
()->
class HomeIndex extends Backbone.Marionette.Layout
template: '<div>Hello, Backbone! </div>'
constructor: () ->
console.log '!'
initialize: (options) ->
#template = _.template #template
console.log "Home Index initialized"
render: () =>
#$el.html #template
I can not initialize HomeIndex, anybody know what I am doing wrong?
please advice
Updated :
define [
'App'
'apps/home/index/index'
],
but still typeof HomeIndex return object, not function.
First of all, when you got such an issue, check the files loaded by Requirejs. You'd probably seen that ./index/index is either not what you expected, or not loaded at all.
Basically, AMD does not support relative links. Only link relative to the baseUrl.
Related
I'm trying to get my head around using CommonJS modules within a Backbone application, so I have a skeleton Backbone View defined in /views/categories/edit.js:
app.Views.quoteCategoriesEdit = app.Ui.ModalView.extend({
className: '',
template: JST["templates/quotes/categories/quote-categories-edit.html"],
events: {
'click [data-key="save"]': 'save',
'click [data-key="cancel"]': 'cancel'
},
initialize: function (options) {
var that = this;
_.bindAll(this, 'save', 'cancel');
app.Collections.quotesCategories.on('change add', function () {
that.remove();
});
},
render: function () {
var that = this;
// boilerplate render code
return this;
}
});
If someone could show me how I can convert this into a CommonJS module to be used with Browserify, then I would be very grateful and it'd really help me understand how I go about modularising the rest of the application! Thanks
//once you get things into folders/files, this path may change
//but for now I'm assuming all your views will live in the same directory
var ModalView = require('./modal-view');
var QuoteCategoriesEdit = ModalView.extend({
className: '',
template: JST["templates/quotes/categories/quote-categories-edit.html"],
events: {
'click [data-key="save"]': 'save',
'click [data-key="cancel"]': 'cancel'
},
initialize: function (options) {
var that = this;
_.bindAll(this, 'save', 'cancel');
app.Collections.quotesCategories.on('change add', function () {
that.remove();
});
},
render: function () {
var that = this;
// boilerplate render code
return this;
}
});
//Simplest convention is just 1-class-per-module
//Just export the constructor function
module.exports = QuoteCategoriesEdit;
Follow-up question from the comments:
Very much appreciate this! How would you approach: app.Collections.quotesCategories as I house everything under the app namespace? Do I just require the Collection itself?
So the idea of an "app" namespace is the opposite of being modular/commonjs/browserify/requirejs. You don't need an app object anymore. Any module that needs to create a new instance of this collection would just do var QuotesCategories = require('app/collections/quotes-categories'); and that is all. No more globals or namespace objects. Mostly your views will get the models/collections they need in their constructor function options. Most of your models will get created by calling fetch on a collection, and most of your collections will be instantiated by your router.
Oh, and yes in this specific example it's probably best if non-view code creates the collection and passes it to the view via the constructor options.collection parameter. However, if you decided yes you really wanted your view to instantiate the collection, it wouldn't come from the app global namespace object, it would just come from a require call as you describe in your comment.
I have backbone view with inheritance:
class App.Views.User.Base extends Backbone.View
el: '#main'
initialize: ->
events:
'click .save': 'save'
render: ->
#$el.html #template user: #model
#
class App.Views.User.Registration extends App.Views.User.Base
template: JST['template/users/registration']
save: ->
if not #model.expired()
#model.save {},
error: (model, response) ->
console.log response.responseText
When I'm trying to render this:
(new FancySurvey.Views.User.Registration(model: model)).render()
I got the error:
Property 'template' of object #<Registration> is not a function
What might be a problem? Thanks
The problem was with wrong path to template file, in my case it should be
class App.Views.User.Registration extends App.Views.User.Base
template: JST['users/registration']
...
I have a collection of Tools displayed as a CompositeView. Each of the rendered items in this collection is an ItemView. The name of the region that holds these is called toolNameRegion.
I have another region named toolDetailsRegion in that page and it has its supposed to render the attributes of the clicked tool in the toolNameRegion.
Here is the view:
#Tools.module "AboutApp.Show", (Show, App, Backbone, Marionette, $, _) ->
class Show.Layout extends Backbone.Marionette.Layout
template: JST['backbone/apps/about/templates/about']
regions:
toolNameRegion: "#tool-name"
toolDetailsRegion: "#tool-details"
class Show.Tool extends Backbone.Marionette.ItemView
template: JST['backbone/apps/about/templates/_tool']
tagName: "li"
events:
"click a.tool-link" : ->
#trigger "tool-name:link:clicked", #model # How the hell do I pass this to the Show.ToolDetail class?
console.log #model # shows the model attributes that was clicked
class Show.Tools extends Backbone.Marionette.CompositeView
template: JST['backbone/apps/about/templates/tools']
itemView: Show.Tool
itemViewContainer: "ul"
triggers:
"click .tool-link" : "tool:link:clicked"
class Show.ToolDetail extends Backbone.Marionette.ItemView
template: JST['backbone/apps/about/templates/tool_details']
itemView: Show.Tool
onShow: -> console.log "onShow"
onClose: -> console.log "onClose"
Here is the controller:
#Tools "AboutApp.Show", (Show, App, Backbone, Marionette, $, _) ->
Show.Controller =
showAbout: ->
tools = App.request "get:tools"
#aboutLayout = #getAboutLayout()
#aboutLayout.on "show", =>
#showTools tools
#showInitialTool tools
App.mainRegion.show #aboutLayout
showTools: (tools) ->
toolsView = #getToolsView tools
console.log toolsView
toolsView.on "tool:link:clicked", (tool) =>
console.log "model: #{tool}"
tool = #getInitialToolView tool
#aboutLayout.toolDetailsRegion.show tool
#aboutLayout.toolNameRegion.show toolsView
getToolsView: (tools) ->
new Show.Tools
collection: tools
showInitialTool: (tools) ->
initial_tool = tools.at(1)
toolView = #getInitialToolView initial_tool
#aboutLayout.toolDetailsRegion.show toolView
getToolDetailsView: ->
App.request "tool:detail:view"
toolDetailsRegion: ->
toolDetailView = #getInitialToolView
#about.toolDetailsRegion.show toolDetailView
getInitialToolView: (tool) ->
new Show.ToolDetail
model: tool
getAboutLayout: ->
new Show.Layout
How do I pass in the #model (model that was clicked on) to the controller so #model can be passed to the view class Show.ToolDetail so that the toolDetailsRegion can be updated dynamically?
Here's my Entities (resources):
#Tools.module "Entities", (Entities, App, Backbone, Marionette, $, _) ->
class Entities.Tool extends Backbone.Model
class Entities.ToolCollection extends Backbone.Collection
model: Entities.Tool
url: -> Routes.tools_path()
API =
setTools: (tools) ->
new Entities.ToolCollection (tools)
getToolEntities: ->
tools = new Entities.ToolCollection()
tools.fetch
reset: true
tools
App.reqres.setHandler "set:tools", (tools) ->
API.setTools tools
App.reqres.setHandler "tool:entities", ->
API.getToolEntities()
Thanks for your response #David Sulc. It is still not passing the model through. Maybe I'm not formatting it correctly?
The way I grab the model from the view:
class Show.Tool extends Backbone.Marionette.ItemView
template: JST['backbone/apps/about/templates/_tool']
tagName: "li"
events:
"click a.tool-link" : ->
App.request "get:new:tool", #model
console.log #model
In the controller:
showTools: (tools) ->
toolsView = #getToolsView tools
console.log toolsView
toolsView.on "tool:link:clicked", (tool) =>
console.log "model retrieved from click: #{tool}" # comes up undefined; how to obtain?
tool = App.request "get:new:tool" # could this be the path, but since tool is undefined, won't work?
new_tool = #getInitialToolView tool
#aboutLayout.toolDetailsRegion.show new_tool
#aboutLayout.toolNameRegion.show toolsView
Thanks to #David Sulc for showing me the way!
In the controller:
showTools: (tools) ->
toolsView = #getToolsView tools
console.log toolsView
Tools.AboutApp.Show.on "tool-name:link:clicked", (tool) =>
console.log tool.get('name')
new_tool = #getInitialToolView tool
#aboutLayout.toolDetailsRegion.show new_tool
In the view code:
class Show.Tool extends Backbone.Marionette.ItemView
template: JST['backbone/apps/about/templates/_tool']
tagName: "li"
events:
"click a.tool-link" : ->
Tools.AboutApp.Show.trigger "tool-name:link:clicked", #model
console.log #model
class Show.ToolDetail extends Backbone.Marionette.ItemView
template: JST['backbone/apps/about/templates/tool_details']
itemView: Show.Tool
Tools.AboutApp.Show.on "tool-name:link:clicked", (tool) =>
console.log tool
onShow: -> console.log "onShow"
onClose: -> console.log "onClose"
You can use events, scoped to your current module. Trigger the event in your view with
Tools.AboutApp.Show.trigger "my:event", #model
Then, in your controller you can listen for that event and update your other view:
Tools.AboutApp.Show.on "my:event", (model) ->
console.log model
The syntax you've used in the Show.Tools view will be limited in scope to the item view (and to a certain extent its collection view). Since you need to pass data between different views, we need to widen the scope, and therefore use the call as above: trigger and listen for events in the Tools.AboutApp.Show scope.
In your view:
events:
"click a.tool-link" : ->
Tools.AboutApp.Show.trigger "tool-name:link:clicked", #model
And in your detail view:
Tools.AboutApp.Show.on "tool-name:link:clicked", (tool) =>
console.log "model retrieved from click: #{tool}"
Notice we need to use the same scope and same trigger name.
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.
I have the view:
class FoursquareSearch.Views.SearchNew extends Backbone.View
tagName: 'sidebar'
template: JST["templates/search/new_search"]
#events:
# 'submit': 'create'
initialize: ->
#render
render: ->
console.log('hello')
$this = $('#sidebar')
$this.html('<p>All new content. <em>You bet!</em></p>')
$this
with this Router
class FoursquareSearch.Routers.Maps extends Backbone.Router
routes:
'': 'index'
index: ->
FoursquareSearch.Views.maps = new FoursquareSearch.Views.Maps()
#model = new FoursquareSearch.Models.Map()
#originForm = new Traveltime.Views.ShapesOriginForm(model: model, map: Traveltime.Views.map)
newSearch = new FoursquareSearch.Views.SearchNew(model: model, map: FoursquareSearch.Views.maps)
And this HTML
<div id="top"></div>
<div id="sidebar">
</div>
Init code:
window.FoursquareSearch =
Models: {}
Collections: {}
Views: {}
Routers: {}
init: ->
new FoursquareSearch.Routers.Maps()
Backbone.history.start()
$(document).ready ->
FoursquareSearch.init()
I can see the console.log message however the HTML class / id does not get updated!
If I run:
$this = $('#sidebar')
$this.html('<p>All new content. <em>You bet!</em></p>')
in console I can see the HTML change on the page, it just seems that Backbone.js does not want to update the view for me?
Update #render to be #render() in your initialize method. You are simply returning the method, but never calling it.