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']
...
Related
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.
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.
Bit of an odd one...
I have the collection:
class Store.Collections.Product extends Backbone.Collection
url: '/api/products'
model: Store.Models.Product
With the view:
class Store.Views.Origin extends Backbone.View
initialize: ->
#collection = new Store.Collections.Product()
#collection.fetch()
#model.bind('change:formatted', #render, this);
#render()
events:
'change [name=origin]': 'setOrigin'
el: =>
#options.parent.$('.origin-input')[0]
template: JST["backbone/templates/shapes/product"]
render: ->
$this = $(this.el)
$this.html(#template(model: #model.toJSON(), errors: #model.errors))
console.log(#collection)
#collection.each(#appdenDropdown)
#delegateEvents()
this
appdenDropdown: (product) ->
console.log("append trigger")
#view = new Store.Views.Products(model: product)
#$('#history').append(view.render().el)
with the template:
<div id="history"></div>
The collection works... the
console.log(#collection)
shows the data! however
#collection.each(#appdenDropdown)
Does not do anything, doesn't error, or through anything. It just doesn't do anything. I am trying to extract the data out of the collection! But it wont...
It's because there's nothing in the collection yet.
#collection.fetch() in the initializer is an asynchronous method. You have to wait until the fetch is complete before you iterate through the collection items.
The fetch() function takes an optional success callback that is fired when the fetch is complete.
So you can update your initializer code to wait until the collection is fetched before calling render. Here is the code.
initialize: ->
#collection = new Store.Collections.Product()
#collection.fetch
success: =>
#render()
The problem as others have mentioned is that fetch is asynchronous, but the solution is simpler: jQuery's deferred object:
initialize: ->
#collection = new Store.Collections.Product()
#collection.deferred = #collection.fetch()
#model.bind('change:formatted', #render, this);
#collection.deferred.done ->
#render()
What's happening here is that when you call #collection.deferred.done, you're telling jQuery to wait until the collection is loaded before executing render on it, which is what you want. I think that should work.
A couple good references on deferred:
http://lostechies.com/derickbailey/2012/02/07/rewriting-my-guaranteed-callbacks-code-with-jquery-deferred/
http://www.erichynds.com/jquery/using-deferreds-in-jquery/
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.
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') })