Im just starting to us backbone.js and following some simple tutorials, though i have hit a bump in the road. I cant seem to get the FriendCollection.add method to run. I dont get any errors. Is there something im missing? I believe it has something to do with this.FriendCollection.add(friend_model).
var Friend = Backbone.Model.extend({});
var FriendCollection = Backbone.Collection.extend
({
initalize: function(models,options)
{
this.bind("add", options.view.addFriendLi);
}
});
var FriendModel = Backbone.Model.extend
({
name:null
});
var AppView = Backbone.View.extend
({
el:$("body"),
initialize: function()
{
this.FriendCollection = new FriendCollection(null,{view:this});
},
events:
{
"click #add-friend":"showPrompt",
},
showPrompt: function()
{
var friend_name = prompt("Who is your friend?");
var friend_model = new FriendModel({name:friend_name});
this.FriendCollection.add(friend_model);
},
addFriendLi: function(model)
{
$("#friends-list").append("<li>"+model.get('name') + "</li>");
},
});
$(document).ready(function()
{
var appview = new AppView;
});
You misspelled initialize in FriendCollection, so you're never actually binding the collection to the passed in view.
var FriendCollection = Backbone.Collection.extend
({
initialize: function(models,options) // Fixed here
{
this.bind("add", options.view.addFriendLi);
}
});
Related
I have the problem that the event "form:selectedForm" is calling the method "showForm" but when sending this to my view I am getting the following error: TypeError: e[t] is not a function.
This is stated in line 128 in the backbone.js script but I have no clue what he is doing there. It looks like that he is looking for a "to" or "on" event on the collection.
What I am doing wrong here?
MyController = Backbone.Marionette.Controller.extend({
initialize: function(options) {
this.options = options;
this.urls = options.urls;
this.mainRegion = options.mainRegion;
this.view = new MyLayout();
this.mainRegion.show(this.view);
this.view.render();
this.showSelectorView(this.view.formHeader);
},
showSelectorView : function(view) {
var forms = new MyForms();
forms = this.urls.loadForms;
var selectorView = new FormSelectorView({
collection: forms
});
forms.fetch();
this.listenTo(selectorView, "form:selectedForm", this.showForm);
view.show(selectorView);
},
showForm : function(models) {
console.log("showForm");
var form = new FormContentView({
collection: models
});
this.view.form.show(form);
}
});
MyLayout = Backbone.Marionette.Layout.extend({
template: Backbone.Marionette.TemplateCache.get('#content'),
regions: {
formHeader: "#selector",
form: "#formContent",
formContent: "#content",
formFooter: "#save",
formTemplates: "#templates"
}
});
FormSelectorView = Backbone.Marionette.ItemView.extend({
template: Backbone.Marionette.TemplateCache.get('form-selector-template'),
events : {
"click option" : "selectForm"
},
initialize : function() {
this.listenTo(this.collection, "sync", this.render, this);
},
selectForm : function(e) {
e.preventDefault();
var id = $(e.currentTarget).attr("name");
var item = this.collection.get(id);
this.trigger("form:selectedForm", item.attributes.fields);
}
});
I think the error is in your showSelector view function, you are overwriting your forms collection in the second line,
i think your intention in that line was to assing the url of the forms collection so my guess is that this will fix it:
showSelectorView : function(view) {
var forms = new MyForms();
forms.url = this.urls.loadForms; /// Im assuming you were trying to pass the url here
var selectorView = new FormSelectorView({
collection: forms
});
forms.fetch();
this.listenTo(selectorView, "form:selectedForm", this.showForm);
view.show(selectorView);
},
I have a model which has both navid and subnavid .While destroying a model i need to check in the entire collection , for other models which have navid as same as subnavid of the model i'am trying to delete . Please help me out . Thanks in advance . Heregoes my sample code.
Model:
var Node = Backbone.Model.extend({
defaults: {
NavId: '',
SubNavId: ''.
ItemName:''
} }
Collection:
var NodeCollection = Backbone.Collection.extend({ model:Node }
And i have two view one for the Node(i am building tr) and other for
the collection(I need to build table) var NodeCollectionView =
Backbone.View.extend({
initialize: function (options) {
var self = this; self.collection = new NodeCollection({ NavigationId: options.NavigationId });
self.collection.fetch({
success: function () {
/*I am getting hte proper collection from my restful api and iam able to bind it properly
self.render();
}
});
},
render: function () {
var that = this;
_.each(this.collection.models, function (item) {
that.RenderEachNode(item);
}, this);
},
RenderEachNode: function (item) {
var TempJsonNode = item.toJSON();
var self = this;
var nodeView = new NodeView({
tagName: 'tr',
id: 'NavId_' + TempJsonNode.NavItemId,
model: item
});
} });
var ItemTemplate = ""; ItemTemplate += " <td>"; ItemTemplate += " <a><%= ItemName %></a>"; ItemTemplate +=" </td>"; ItemTemplate
+=" <td>"; ItemTemplate +=" <a href='#' original-title='Delete ' class='tip_north Delete'>X</a>"; ItemTemplate +=" </td> ";
var NavigationItemView = Backbone.View.extend({
template: ItemTemplate,
render: function () {
var self = this;
var tmpl = _.template(this.template);
this.$el.html(tmpl(this.model.toJSON()));
return this;
},
events: {
"click .Delete": "DeleteBtnClick"
},
DeleteBtnClick: function () {
var self = this;
self.model.destroy({
success: function (status, data) {
var RetData = JSON.parse(data);
if (RetData.Status == 'Success') {
$(self.el).remove()
}
},
error: function () {
alert('Error In Deleting The Record');
}
});
return false;
} });
I am able to build the table properly but while destroying a model , i am not figuring out a way to destroy the dependent models.My Api is restricted in such a way that i cannot get a nested json ( if so i would have done with backbone relation). so i need to figure out some way that the other models and views which has the NavId of the model am deleting.
Please help me out.
How about something like:
var NodeView = Backbone.View.extend({
initialize: function() {
//when the model gets destroyed, remove the view
this.listenTo(this.model, 'destroy', this.remove);
},
//..clip
DeleteBtnClick: function () {
var self = this;
var collection = self.model.collection;
var navId = self.model.get('NavId');
self.model.destroy({
success: function (status, data) {
var RetData = JSON.parse(data);
if (RetData.Status == 'Success') {
//if parent was part of a collection
if (collection) {
//find related models
var related = collection.filter(function (model) {
return model.get('SubNavId') === navId;
});
//call destroy for each related model.
var promises = _.invoke(related, 'destroy');
//optional: if you want to do something when all the children
//are destroyed:
$.when.apply($, promises).then(function () {
console.log('all destroyed');
});
}
}
},
error: function () {
console.log(arguments);
alert('Error In Deleting The Record');
}
});
return false;
}
});
Edit: JSFiddle here
During the initialize function of my app I would like to default to my search page and pass my LeagueCollection as the model.
I am encountering an issue where I can add a watch to this.searchResults in my App initialize and see models: Array[3] as expected,
but when the this.model.toJSON() in the view is called I get the error object has no method toJSON.
This code was working fine with a in memory collection and then I switched to using backbone.localstorage.js to store the app data locally.
So my question is: why is the model not populated in the view?
In my main.js I have
var AppRouter = Backbone.Router.extend({
routes: {
"": "list",
...
},
initialize: function () {
this.searchResults = new LeagueCollection();
this.searchPage = new SearchPage({
model: this.searchResults.fetch()
});
this.searchPage.render();
},
...
});
In my Search Page view
window.SearchPage = Backbone.View.extend({
initialize:function () {
this.template = _.template(tpl.get('search-page'));
},
render:function (eventName) {
var self = this;
$(this.el).html(this.template(this.model.toJSON()));
this.listView = new LeagueListView({el: $('ul', this.el), model: this.model});
this.listView.render();
return this;
},
...
});
The method collection.fetch doesn't return the collection -- it's asynchronous. What you probably want is to use its success callback:
this.searchResults = new LeagueCollection();
var self = this;
this.searchResults.fetch({
success: function(collection, response) {
self.searchPage = new SearchPage( { model: collection } );
self.searchPage.render();
}
});
The situation I'm experiencing is that my fetch is working fine, I can see the data in my backbone collection, if I step into the method where I assign the item from the collection to the model var or pause for a second or two, all is well: the line of code this.member gets populated
this.member = this.members.get(1);
. If I just let the code run, I wind up passing in a null model to my view. I don't understand what I'm missing. Do I need to bind data in the collection before I access the collection? I'm trying to fall in love with Backbone, but so far, she's been a cruel mistress...
`
//create the namespace
var Endeavor = {
Models: {},
Collections: {},
Views: {},
Templates: {}
};
Endeavor.Models.Member = Backbone.Model.extend({
idAttribute: "Id"
});
Endeavor.Collections.Members = Backbone.Collection.extend({
model: Endeavor.Models.Member,
url: "Http://localhost:60000/api/members/" + "1", // $.cookie('UserId')
initialize: function () {
console.log("Members collections init");
}
});
Endeavor.Views.MemberView = Backbone.View.extend({
id: "memberForm",
template: "#memberTemplate",
initialize: function () {
console.log('init member view');
},
render: function () {
console.log('memberView render called');
console.log(this.model.toJSON());
var html = $(this.template).tmpl();
$(this.el).html(html);
$("#Name").text = this.model.Name;
}
});
jQuery(document).ready(function () {
// router
Endeavor.Router = Backbone.Router.extend({
routes: {
"": "lists",
},
lists: function () {
this.members = new Endeavor.Collections.Members();
this.members.fetch();
this.member = this.members.get(1);
var memberView = new Endeavor.Views.MemberView({ model: this.member });
memberView.render();
$("#content").html(memberView.el);
}
});
// populate local variables
var appRouter = new Endeavor.Router();
Backbone.history.start();
});
`.
Members.fetch is async call. Try to render view on members reset event, or pass success callback in fetch method, or call this.members.fetch({async: false}). There are many different options.
I have a Backbone collection and when I add a new model to it the "add" event doesn't seem to work as I'd expect. I've bound 2 views to listen for add events on the collection, but only one seems to get notified of the event, and when this happens, no PUT request is sent to my server. When I remove the second bind, the other one works and the PUT request is sent. Here's the code snippets:
var FlagList = Backbone.Collection.extend({
model: Flag // model not shown here... let me know if it would help to see
});
var FlagCollectionView = Backbone.View.extend({
el: $('ul.#flags'),
initialize: function() {
flags.bind('add', this.addFlag, this); // this one doesn't fire!!
},
addFlag: function(flag) {
alert("got it 1"); // I never see this popup
}
});
var AddFlagView = Backbone.View.extend({
el: $("#addFlagPopup"),
events: {
"click #addFlag": "addFlag"
},
initialize: function() {
flags.bind('add', this.closePopup, this); // this one fires!!
}
addFlag: function() {
flags.create(new Flag);
},
closePopup: function() {
alert("got it 2"); // I see this popup
}
});
var flags = new FlagList;
var addFlagView = new AddFlagView;
var flagCollectionView = new FlagCollectionView;
A few suggestions:
ID's vs Classes
you've over qualified your selector by combining a class and an id. jQuery allows this, but the ID selector should be unique on the page anyway so change el: $('ul.#flags') to el: $('ul#flags').
Leveraging Backbone
I like to explicitly pass my collections and/or models to my views and use the magic collection and model attributes on views.
var flags = new FlagList;
var addFlagView = new AddFlagView({collection: flags});
var flagCollectionView = new FlagCollectionView({collection: flags});
which now means that in your view, you will automagically have access to this.collection
unbinding events to avoid ghost views
var FlagCollectionView = Backbone.View.extend(
{
initialize: function (options)
{
this.collection.bind('add', this.addFlag, this);
},
addFlag: function (flag)
{
alert("got it 1");
},
destroyMethod: function()
{
// you need some logic to call this function, this is not a default Backbone implementation
this.collection.unbind('add', this.addFlag);
}
});
var AddFlagView = Backbone.View.extend(
{
initialize: function ()
{
this.collection.bind('add', this.closePopup, this);
},
closePopup: function ()
{
alert("got it 2");
},
destroyMethod: function()
{
// you need some logic to call this function, this is not a default Backbone implementation
this.collection.unbind('add', this.closePopup);
}
});
It looks like I have to agree with #fguillen, that your problem must be somewhere in how you initialize the view, as in my comment I mention that it's most likely related to timing, ie: binding your event to the collection after the 'add' event has already fired.
This code works for me:
var FlagList = Backbone.Collection.extend({});
var FlagCollectionView = Backbone.View.extend({
initialize: function() {
flags.bind('add', this.addFlag, this);
},
addFlag: function(flag) {
alert("got it 1");
}
});
var AddFlagView = Backbone.View.extend({
initialize: function() {
flags.bind('add', this.closePopup, this);
},
closePopup: function() {
alert("got it 2");
}
});
var flags = new FlagList;
var addFlagView = new AddFlagView;
var flagCollectionView = new FlagCollectionView;
flags.add({key:"value"});
check the jsFiddle
Your problem is somewhere else.
If you ended up here after making the same stupid mistake I did, make sure you've got:
this.collection.bind( 'add', this.render )
and NOT:
this.collection.bind( 'add', this.render() )