I have a view called Entry.
class Movieseat.Views.Entry extends Backbone.View
template: JST['movieseats/entry']
className: 'movie-frame'
initialize: ->
#collection.on('change', #render, this)
#collection.on('remove', #render, this)
render: ->
$(#el).html(#template(entry: #collection))
this
events: ->
"click .remove": "removeEntry"
removeEntry: (e) ->
console.log #collection
This view creates a Entries template.
<div data-id="<%= #entry.get('id') %>">
<p><%= #entry.get('title') %></p>
<p><%= #entry.get('id') %></p>
<p class="remove">Remove</p>
</div>
What I want to do is to remove a model from the collection (movieseats) and then rerender the template. If I click on a entry I fire the console.log #collection event. This logs the following,
Backbone.Model {cid: "c4", attributes: Object, collection: Movieseats, _changing: false, _previousAttributes: Object…}
How would I target the model's cid and then remove it from the collection?
Update
If I use this code,
removeEntry: (e) ->
thisid = $(e.currentTarget).closest('div').data('id')
console.log #collection
modelToRemove = #collection.findWhere({cid: thisid });
#collection.remove(modelToRemove);
I get the following result in the console log.
Backbone.Model {cid: "c4", attributes: Object, collection: Movieseats, _changing: false, _previousAttributes: Object…}_changing: false_events: Object_pending: false_previousAttributes: Objectattributes: Objectchanged: Objectcid: "c4"collection: Movieseatsid: 531__proto__: Object
Uncaught TypeError: undefined is not a function
The problem seems to be this part,
modelToRemove = this.collection
You can do it using this code (see collection#remove method):
var modelToRemove = collection.findWhere({cid: "SOME_ID_HERE"});
collection.remove(modelToRemove);
See also findwhere docs.
Edit
From the output of the console.log method it appears that the #collection variable in your above code is not a true Backbone.Collection, but rather a model pointing to the collection. Thus, you should modify the above code to be:
trueCollection = #collection.get("collection") // or #collection.collection
modelToRemove = trueCollection.findWhere( cid: "SOME_ID_HERE" )
trueCollection.remove(modelToRemove)
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'm starting using parse.com to develop a web app but i'm stuck on a simple problem.
I defined a model (or object in Parse SDK) as:
Book.Model = Parse.Object.extend("book", {
// Default attributes for the book.
defaults: {
title: "placeholder...",
},
// Ensure that each book created has `title`.
initialize: function() {
if (!this.get("title")) {
this.set({"title": this.defaults.title});
}
},
});
and a collection:
Book.List = Parse.Collection.extend({
// Reference to this collection's model.
model: Book.Model,
initialize: function() {
},
});
Then, if i try something like
var books = new Book.List();
books.fetch({
success: function(collection) {
console.warn(collection);
},
error: function(collection, error) {
// The collection could not be retrieved.
}
});
Everything goes fine. Log:
child {length: 5, models: Array[5], _byId: Object, _byCid: Object, model: function…}
_byCid: Object
_byId: Object
length: 5
models: Array[5]
__proto__: EmptyConstructor
BUT if i try to use event callback instead of success method i get an empty array. Code:
books.on('reset', this.log());
books.fetch();
log: function() {
console.log(books);
}
and log:
child {length: 0, models: Array[0], _byId: Object, _byCid: Object, model: function…}
_byCid: Object
_byId: Object
length: 5
models: Array[5]
__proto__: EmptyConstructor
which is quite strange (because i think that each solution wait for the collection to be populated from the server). Does anybody know why is this happening?
I'm actually using Backbone Boilerplate and Parse.com js SDK.
The Collection#fetch behavior has changed, it used to reset the collection by default but as of 1.0.0 it merges the new models using set:
When the model data returns from the server, it uses set to (intelligently) merge the fetched models, unless you pass {reset: true}, [...]
and set doesn't trigger "reset" events, it triggers other events:
All of the appropriate "add", "remove", and "change" events are fired as this happens.
If you want your fetch to reset the collection then you have to say so:
books.fetch({ reset: true });
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'm trying to use Backbone.localStorage as a backend to an app.
I wrote a Model called Era, and a corresponding EraView. Pressing enter in the EraView causes the model to be saved, and this is where I get the error:
Uncaught Error: A "url" property or function must be specified
urlError backbone.js:1509
_.extend.url backbone.js:515
_.result underscore-min.js:1060
Backbone.sync backbone.js:1410
Backbone.sync backbone.localStorage.js:188
_.extend.sync backbone.js:276
_.extend.save backbone.js:476
karass.EraView.Backbone.View.extend.close era.js:61
karass.EraView.Backbone.View.extend.updateOnEnter era.js:75
Here is the code to the EraView
var karass = karass || {};
// Era Item View
// the DOM element for an era item
karass.EraView = Backbone.View.extend({
tagName: 'li',
className: 'era',
template: _.template( $('#era-template').html() ),
// The DOM events specified to an item
events: {
'dblclick .edit-input': 'edit',
'keypress .edit-input': 'updateOnEnter',
//'blur .edit': 'close',
},
// The EraView listens for changes to its model, re-rendering. Since there's
// a one-to-one correspondence between an era and a EraView in this karass,
// we set a direct reference on the model for convenience.
initialize: function(){
_.bindAll(this);
this.model.on('change', this.render, this);
},
// Re-renders the era item to the current state of the model and
// updates the reference to the era's edit input within the view
render: function(){
this.$el.html( this.template(this.model.attributes));
this.$era_start = this.$('.era-start');
this.$era_end = this.$('.era-end');
this.$era_start.attr('disabled', true);
this.$era_end.attr('disabled', true);
return this;
},
// Switch this view into editing mode, displaying the input field
edit: function(){
this.$('.edit-input').removeAttr('disabled');
this.$el.addClass('editing');
this.$('.edit-input').addClass('editing');
},
// Close the editing mode, saving changes to the era
close: function(){
this.$('.edit-input').attr('disabled', true);
var start = this.$era_start.val().trim();
var end = this.$era_end.val().trim();
if(start && end){
this.model.save({from: start, until: end});
}
this.$el.removeClass('editing');
this.$('.edit-input').removeClass('editing');
this.trigger('close');
},
updateOnEnter: function(e){
if(e.which !== ENTER_KEY && (!this.$era_start.val().trim() || !this.$era_end.val().trim())){
return;
}
this.close();
}
});
And this is the code for the era model:
var karass = karass || {};
karass.Era = Backbone.Model.extend({
defaults: {
from: '',
until: ''
},
});
I thought I didn't need a url while using localStorage.
Edit: I forgot to mention that while this behavior occurs in the Era/EraView itself, it also occurs in the Name model, which extends Era. Name in turn belongs in a Names collection. I don't know if this makes a difference, but I figured I add it.
Edit 2: The Names collection looks like this:
karass.Names = History.extend({
model: karass.Name,
localStorage: new Backbone.LocalStorage('karass-names'),
});
Edit 3: I posted all the code on jsfiddle: http://jsfiddle.net/herrturtur/CRv6h/
You don't need an url while using localStorage. But you need to set the localStorage property on your model or on your collection (if you set the localStorage on a collection the models inside the collection will "inherit" this setting):
karass.Era = Backbone.Model.extend({
localStorage: new Backbone.LocalStorage("EraCollection"),
// the EraCollection should be an unique name within your app.
defaults: {
from: '',
until: ''
},
});
If you don't setup the localStorage property the plugin falls back to the default ajax sync so you get the uri missing exception.