I'm trying to follow a Backbone Rails tutorial and I'm getting stuck trying to render my collection of links in a CompositeView, no nesting involved. I suspect that tutorial is drastically outdated, but since I yet lack Backbone skills I can't pin point the problem. Please, take a look at the following code:
Creating a collection of navigational links.
#TestApp.module "Entities", (Entities, App, Backbone, Marionette, $, _) ->
class Entities.Navigation extends Backbone.Model
class Entities.NavigationCollection extends Backbone.Collection
model: Entities.Navigation
API =
getLinks: ->
new Entities.NavigationCollection [
{ name: "one" }
{ name: "two" }
{ name: "three" }
{ name: "four" }
{ name: "five" }
]
App.reqres.setHandler "navigation:entities", ->
API.getLinks()
Core navigation file.
#TestApp.module "NavigationUnit", (NavigationUnit, App, Backbone, Marionette, $, _) ->
#startWithParent = false
API =
listNavigation: ->
NavigationUnit.List.Controller.listNavigation()
NavigationUnit.on "start", ->
API.listNavigation()
Controller, where I'm passing collection to the view.
#TestApp.module "NavigationUnit.List", (List, App, Backbone, Marionette, $, _) ->
List.Controller =
listNavigation: ->
links = App.request "navigation:entities"
navigationView = #getNavigationView links
App.navRegion.show navigationView
getNavigationView: (links) ->
new List.Navigation
collection: links
And the view.
#TestApp.module "NavigationUnit.List", (List, App, Backbone, Marionette, $, _) ->
class List.NavigationLinks extends Marionette.ItemView
template: "navigation/list/templates/_links"
tagName: "li"
class List.Navigation extends Marionette.CompositeView
template: "navigation/list/templates/list_navigation"
itemView: List.NavigationLinks
itemViewContainer: "ul"
Content of the ItemView template is %a{:href => "#"}= #name. And in the CompositeView is a basic wrapper structure with the %ul tag. Now what happens is that the CompositeView renders the template as expected, but it does not populate the %ul with the itemView. Instead it creates amount of divs equals to the amount of models in the collection (five in this case) and inserts there entire wrapper templates, so it looks like this:
#navRegion
.div
.navigation-wrapper
.navigation-content
%ul
.div
.navigation-wrapper
// entire template
.div
.navigation-wrapper
// entire template
//etc +3 divs
What am I doing wrong here?
Your tutorial is probably out of date. Marionette renamed the property from itemView to childView in version 2.0.0.
From the docs
Each childView will be rendered using the childView's template. The CompositeView's
template is rendered and the childView's templates are added to this.
var ChildView = Marionette.ItemView.extend({});
var CompView = Marionette.CompositeView.extend({
childView: ChildView
});
Related
I have two models, User and Picture. User has many Pictures. I want to make a suspend call to backend server query on Picture. Currently when I hit the suspend button I get the User model but I want picture model. In my User view I have following code.
USER VIEW
class MyApp.Views.User extends Backbone.View
initialize: ->
#listenTo(#model, 'change', #render)
#listenTo(#model, 'destroy', #remove)
render: ->
$(#el).html(#template(user: #model))
#fetchPictures()
this
fetchPictures: ->
#picture_collection = new MyApp.Collections.Pictures()
#picture_collection.fetch({
reset: true,
data: { "user_id": #model.get("objectId") }#,
success: (e) ->
for picture in e.models
view = new MyApp.Views.Picture(model: picture)
$("#objects-info").html(view.render().el)
})
PICTURE VIEW
class MyApp.Views.Picture extends Backbone.View
template: JST['flagged_objects/picture']
el: 'td'
events: ->
"click #Picure": "deletePicture"
initialize: ->
#model.set('id', this.model.get('objectId'))
#listenTo(#model, 'change', #render)
#listenTo(#model, 'destroy', #remove)
render: ->
$("#object-info").append(#template(entry: #model))
this
deletePicture: (e) ->
e.preventDefault()
console.log #
PICTURE COLLECTION
class MyApp.Collections.Pictures extends Backbone.Collection
model: MyApp.Models.Picture
url: "/api/pictures"
PICUTRE MODEL
class MyAdmin.Models.Picture extends Backbone.Model
urlRoot: 'api/picture'
idAttribute: 'objectId'
In USER VIEW in #model variable I get User model. Is there any way to get Picture model here so that I can send call to suspend the picture.
In summary I just want to suspend picture model obejct from collection when I press suspend button. Suspend essentially is an update call.
Thanks,
So as we have spoken in the comments, the deletePicture function should be in the Picture sub-view as what you want to suspend is a Picture model.
I think the strange behaviour you are having it's related to the way you are rendering your views.
In the user view you should append the Picture sub-views.
fetchPictures: ->
#picture_collection = new MyApp.Collections.Pictures()
#picture_collection.fetch({
reset: true,
data: { "user_id": #model.get("objectId") }#,
success: (e) ->
for picture in e.models
view = new MyApp.Views.Picture(model: picture)
$("#objects-info").append(view.render().el)
})
And it's in the render of the sub-view where you get access to the html function.
render: ->
this.$el.html(#template(entry: #model))
this
Let me know how it goes!
I have a model as defined below
class Colors extends Backbone.Model
name: -> [#get("name")]
value: -> [#get("value")]
Collection as defined below
class #ColorsCollection extends Backbone.Collection
model: Colors
Select tag as define below
%select{name: "colorslist" type: "hidden" value: "" }
Upon an event, I want to dynamically populate the colorslist select options with data fetched from ColorsCollection.
I have been looking into select2 documentation but unable to find any relevant examples.
Basically, you will bind to the reset event and replace the html and start the select2 plugin.
I know the plugin has some internal ways of doing it - but why deal with having to comb through the documentation.
class View extends Backbone.View
initialize: ->
#collection = new ColorsCollection
# Bind to reset event
#listenTo #collection, "reset", #updateSelect
#collection.fetch()
updateSelect: (collection) ->
# Use template engine (eg. Handlebars) to redraw the html
#$el.find('#selection').html tmpl(#collection)
# Start select2
#$el.find('#selection > select').select2()
I have 2 models, implemented via backbone.relational and backbone.localstorage, they're working good.
and I have 2 views, first is a "single item" viewer and the second one is an item view with a render function to view my single item view in the way I want, the problem is events not working, neither in parent view nor in single item view.
I've reimplemented that code in the similar way to show you how it's not working ( code is in coffeescript ) :
log = console.log
class $.Girl extends Backbone.RelationalModel
localStorage: new Backbone.LocalStorage 'gals'
initialize: -> if typeof #get('id') is 'undefined' then #save() else #fetch()
class $.Girls extends Backbone.RelationalModel
localStorage: new Backbone.LocalStorage 'gals'
relations:[{
type: Backbone.HasMany
key: 'gals'
relatedModel: $.Girl
includeInJson: 'id'
}]
initialize: ->
if typeof #get('id') is 'undefined' then #save() else #fetch()
#fetchRelated()
class $.GirlView extends Marionette.ItemView
tagName: 'tr'
template: (data)-> '<td>'+data.name+' -- '+data.age+'<button>Love</button></td>'
initialize: ->
#listenTo #model,'change',#render
events:
'click button': 'sayLove'
sayLove : -> log 'I Love YOU!'
class $.GirlsView extends Marionette.ItemView
template: (data)->
'<table>
<thead><tr><th>My Gals</th></tr></thead>
<tbody></tbody>
<tfoot><tr><td>I Love Them!</td></tr></tfoot>
</table>'
initialize: (options)->
#models = #model.get('gals').models
#list = []
self = #
_.each #models,(girl)-> self.list.push new $.GirlView {model:girl}
events:
'click th': 'hello'
render: ->
#$el.html(#template {})
self = #
_.each #list,(girl)->
girl.delegateEvents()
self.$('tbody').append girl.render().$el
hello: -> log 'hello'
gal1 = new $.Girl {name:'gal1',age:'22',id:'gal-1'}
gal2 = new $.Girl {name:'gal2',age:'19',id:'gal-2'}
gals = new $.Girls {title:'maGals',id:'gals-1',gals:['gal-1','gal-2']}
gv = new $.GirlsView {model:gals}
gv.render()
$('body').append gv.$el.html()
It's kinda hello world for me.
Any idea how can I implement nested itemViews with events working or any other idea for this snippet is appreciated.
Try calling delegateEvents after appending your child views:
self.$('tbody').append girl.render().el
girl.delegateEvents()
// or maybe #delegateEvents()? the context should be the child here
Edit:
Change $('body').append gv.$el.html() to $('body').append #$el.
I can't get ItemView to render my values defined in the testMap. All I want is the 1, 2, 3 to be dynamically inserted in the <ol class='test-list'> tag as <li> so my html would look like
<ol class="test-list">
<li>1</li>
<li>2</li>
<li>3</li>
</ol>
What am I missing?
testMap =
a: '1'
b: '2'
c: '3'
class TestLayout extends Layout
template: require '/test_template'
regions:
body: 'section'
events:
'click #testme': 'test'
test: -> app.vent.trigger 'test'
class TestItemView extends ItemView
template: require '/test-item'
serializeData: -> {testMap}
class TestListView extends CollectionView
tagName: 'ol'
className: 'test-list'
itemView: TestItemView
module.exports = class TestPlugin extends Application
testList: null
initialize: =>
#test = new Collection
app.vent.on 'test', #showTest, #
showTest: ->
app.layout.test.show #layout = new TestLayout
#layout.body.show #testList= new TestListView collection: #test
My Html file named test-item looks like this:
<span class='test'>
<%= #testMap %>
</span>
All my globals are defined in the extended files.
I think the culprit here is that I can't get my collection to bind to my itemView. Therefore, when I call #showTest function I get nothing because my testMap object is not talking to my #test variable. How can I marry the two object?
#layout.body.show #listView = new TestListView collection: #test
I've been using Marionette for a couple of weeks and just discovered Backbone Relational so I'm trying to figure out how to integrate the two. Ideally, I would like to use a composite view to render data that is structured like this where each 'item' has its own item view:
list : {
name : 'List 1',
items : [
item1 : {
name : 'Item 1',
id : 1
},
item2 : { ... }
item3 : { ... }
]
}
Normally with composite views you need to have a collection of models that it will iterate through to render each item. With relational, I've just got one model (the list) and that model has a collection (items) within it. Is it possible to render this out using Marionette's views or do I need to use a plain Backbone view and handle the rendering and iteration myself?
This is quite common, and easy to do. In your CompositeView definition, you can specify the collection to use in the initialize method.
Backbone.Marionette.CompositeView.extend({
// ...
initialize: function(){
this.collection = this.model.get("childcollection");
}
});