What I am trying to accomplish is pass this Post model ID to a collection so I can populate this specific Post id with other models that are associated with it. For example: In a Blog Post, contains a bunch of Comments. and I want to display those comments pointing to this Blog Post only.
I must be missing something fundamental here.
Below is my Post Model and i am instantiating a new CommentCollection and passing along models and options arguments.
var PostModel = Backbone.Model.extend({
initialize: function() {
/*
* we are connecting Comments collection
* to each post item by passing along post id
* */
this.comments = new CommentsCollection([], { id: this.id });
},
defaults: {
title: 'title here',
body: 'body here'
},
urlRoot: localserver + "/posts"
});
Below is my Comment Collection. console.log(this.id); returns undefined.
var CommentsCollection = Backbone.Collection.extend({
initialize: function(models, options) {
this.id = options.id;
console.log(this.id);
return this;
},
url: function() {
console.log(this.id);
return localserver + "/posts/" + this.id + "/comments";
},
model: CommentModel
});
my console is returning this:
Uncaught TypeError: Cannot read property 'id' of undefined
3
Try this code:
var CommentModel = Backbone.Model.extend({});
var CommentsCollection = Backbone.Collection.extend({
model: CommentModel,
initialize: function(models, options) {
this.id = options.id;
if(typeof this.id === 'undefined') { return; }
this.url();
},
url: function() {
var localserver = "localhost";
console.log('from comment url: ', this.id);
return localserver + "/" + this.id + "/comments";
}
});
var PostModel = Backbone.Model.extend({
urlRoot: "http://jsonplaceholder.typicode.com" + "/posts",
initialize: function(option) {
this.comments = new CommentsCollection([], { id: option.id });
}
});
//var pm = new PostModel();
//pm.comments.fetch();
//console.log('from pm: ', pm.comments.url());
var PostsCollection = Backbone.Collection.extend({
model: PostModel,
url: "http://jsonplaceholder.typicode.com" + "/posts?_sort=views&_order=DESC",
initialize: function() {
this.on('reset', this.getComments, this);
},
getComments: function() {
this.each(function(post) {
post.comments = new CommentsCollection([], { post: post });
post.comments.fetch();
});
}
});
var pc = new PostsCollection();
pc.fetch();
What I did is that I use the option parameter of the PostModal. Below is the code.
var PostModel = Backbone.Model.extend({
urlRoot: "http://jsonplaceholder.typicode.com" + "/posts",
initialize: function(option) {
this.comments = new CommentsCollection([], { id: option.id });
}
});
Related
I'm looking for the correct backbone structure to achieve the following:
Two server APIs:
GET api/event/4 : returns the event object with id 4.
GET api/event/4/registrations : returns the list of registration objects belonging to event with id 4
I want a view displaying the event object and the list of registrations.
This is very straightforward but I cannot figure out how to organize my Event and Registration models.
Should I use backbone-relational?
My Event model is currently like this:
(the collection is expected to contain the next 10 events from now).
How should I define my Registration model and how will I initialize it, knowing that it is always in the context of an Event model?
var app = app || {};
app.EventModel = Backbone.Model.extend({
urlRoot: app.API_server + 'event'
});
app.EventCollection = Backbone.Collection.extend({
model: app.EventModel,
url: app.API_server + 'event',
initialize: function(){
dt = new Date();
start_dt = dt.toISOString();
this.fetch({
data: {limit:10, start_dt:start_dt},
error: function (model, response, options) {
if(response.status == '403') {
app.Session.logout();
}
}
})
}
});
Make a collection for the registration and use the url property as a function. By default, the urlRoot of the models of the RegistrationCollection will be the url of the collection with their id appended.
app.RegistrationCollection = Backbone.Collection.extend({
url: function() {
return app.API_server + 'event/' + this.id + '/registrations';
},
initialize: function(models, options) {
options = options || {};
this.id = options.id;
}
});
Then, on EventModel initializing, add a RegistrationCollection as a property, passing the event id as an option to the collection.
app.EventModel = Backbone.Model.extend({
urlRoot: app.API_server + 'event',
initialize: function() {
this.registrations = new app.RegistrationCollection(null, {
id: this.id
});
}
});
Remove the fetch from the init, you want to make your collection reusable.
app.EventCollection = Backbone.Collection.extend({
model: app.EventModel,
url: app.API_server + 'event',
});
Fetch inside the view or the router, depending on where it makes more sense for your app.
var EventView = Backbone.View.extend({
initialize: function() {
this.collection = new app.EventCollection();
var dt = new Date(),
start_dt = dt.toISOString();
// this should be here, outside of the collection.
this.collection.fetch({
data: { limit: 10, start_dt: start_dt },
error: function(model, response, options) {
if (response.status === 403) {
app.Session.logout();
}
}
});
},
});
I have this bb app that I'm trying to search and return the results of the search, then when cleared, so all results again. I was able to get everything to show before adding the search feature, but now nothing showing up. I think the collection isn't available at the time it's trying to populate, but can't seem to get it to wait. I've tried moving the fetch around to no avail. Any help would be greatly appreciate. For the sake of ease, I've put everything in a fiddle that can be found here...
//Campaign Model w defaults
app.model.Campaign = Backbone.Model.extend({
default: {
title: '',
img: '',
id: '',
slug: '',
image_types: 'small',
tagline: ''
}
});
//Campaign Collection from model
app.collection.Campaign = Backbone.Collection.extend({
//our URL we're fetching from
url: 'https://api.indiegogo.com/1/campaigns.json?api_token=e377270bf1e9121da34cb6dff0e8af52a03296766a8e955c19f62f593651b346',
parse: function(response) {
console.log('parsing...');
return response.response; //get array from obj to add to collection based on model
},
currentStatus: function(status){
return _(this.filter(function(data){
console.log('currentStats', status);
return data.get('_pending') == status;
}));
},
search: function(searchVal) {
console.log('search...');
if (searchVal == '') {
return this;
}
var pattern = new RegExp(searchVal, 'gi');
return _(this.filter(function(data) {
return pattern.test(data.get('title'));
}));
}
});
app.collection.campaigns = new app.collection.Campaign();
app.collection.campaigns.fetch({
success: function(){
console.log('Success...');
var sHeight = window.screen.availHeight - 200 + 'px';
$('#container ul').css('height', sHeight);
},
error: function() {
console.log('error ',arguments);
}
});
//List view for all the campaigns
app.view.CampaignList = Backbone.View.extend({
events: {
'keyup #searchBox': 'search'
},
render: function(data) {
console.log('campaignList',$(this.el).html(this.template));
$(this.el).html(this.template);
return this;
},
renderAll: function(campaigns) {
console.log('campList renderAll', campaigns, $('#campaignList'));
$('#campaignList').html('');
campaigns.each(function(campaign){
var view = new app.view.CampaignItem({
model: campaign,
collection: this.collection
});
console.log(view);
$('#campaignList').append(view.render().el);
});
return this;
},
initialize: function() {
console.log('init campList',app);
this.template = _.template($('#campaignList-tmp').html());
this.collection.bind('reset', this.render, this);
},
search: function(e) {
console.log('listView search');
var searchVal = $('#searchBox').val();
this.renderAll(this.collection.search(searchVal));
},
sorts: function() {
var status = $('#campSorting').find('option:selected').val();
if(status == '') {
status = false;
};
this.renderAll(this.collection.currentStatus(status));
}
});
//Item view for single campaign
app.view.CampaignItem = Backbone.View.extend({
events: {},
render: function(data){
console.log('campItem render...', data);
this.$el.html(this.template(this.model.toJSON()));
return this;
},
initialize: function(){
console.log('campItem init');
this.template = _.template( $('#campaignItem-tmp').html());
}
});
//Router
app.router.Campaign = Backbone.Router.extend({
routes: {
'': 'campaigns'
},
campaigns: function(){
this.campListView = new app.view.CampaignList({
collection: app.collection.campaigns
});
$('#container').append(this.campListView.render().el);
this.campListView.sorts();
}
});
app.router.campaigns = new app.router.Campaign();
Backbone.history.start();
http://jsfiddle.net/skipzero/xqvrpyx8/
I've gone over the other posts on the same topic and perhaps I'm missing something in my own code but it seems to me things should be working. I've never worked with localStorage and backbone and seem to be missing something here. Any thoughts are greatly appreciated!
my instances:
var Address = {
run: function() {
this.router = new AddressRouter();
this.contactscollection = new AddressCollection();
this.addContact = new AddressAddView();
this.listView = new AddressListView();
Backbone.history.start();
}
};
my collection:
var AddressCollection = Backbone.Collection.extend({
model: AddressModel,
localstorage: new Store('backbone-addressbook')
});
my model:
var AddressModel = Backbone.Model.extend({
defaults: {
id: '',
name: '',
email: ''
}
});
and my view:
var AddressAddView = Backbone.View.extend({
el: '#content',
template: _.template($('#addContactTemplate').html()),
events: { 'submit form#addContactForm': 'createContact'},
createContact: function(){
Address.contactscollection.create(this.newAttributes());
this.save();
this.input.val('');
},
newAttributes: function() {
return {
id: $('#id').val(),
name: $('#name').val(),
email: $('#email').val()
}
},
initialize: function() {
_.bindAll(this, 'addContactPage','render');
},
addContactPage: function(id) {
var contact = {},
model = Address.contactscollection.get(id);
if (id !== undefined && model !== undefined) {
contact = model.toJSON();
}
this.$el.html(this.template({contact: contact}));
}
});
Case matters.
localstorage: new Store('backbone-addressbook')
needs to be
localStorage: new Store('backbone-addressbook')
If localStorage isn't set, your collection is assumed to persist to a RESTful API, and a url is required.
i have a problem with backbone.js. I'm creating a frontend for an existing api, for me unaccessable. The problem is that when I try to add a new model to a collection, i can see in my firebug that every time backbone tries to create the model it appends the attribute name to the url.
Example:
default url = /api/database
when i perform a GET = /api/database
when i perform a GET/POST with object {"name": "test"} =
/api/database/test is the result
Anyone knows how to avoid that behaviour?
Greetings Kern
My View:
window.databaseView = Backbone.View.extend({
el: '#content',
template: new EJS({url: 'js/templates/databaseView.ejs'}),
initialize: function() {
var self = this;
this.collection.fetch({
success: function() {
console.log(self.collection);
var test = self.collection.get("_system");
console.log(test);
self.collection.get("_system").destroy();
self.collection.create({name: "test"});
}
});
},
render: function(){
$(this.el).html(this.template.render({}));
return this;
}
});
Model:
window.Database = Backbone.Model.extend({
initialize: function () {
'use strict';
},
idAttribute: "name",
defaults: {
}
});
Collection:
window.ArangoDatabase = Backbone.Collection.extend({
model: window.Database,
url: function() {
return '../../_api/database/';
},
parse: function(response) {
return _.map(response.result, function(v) {
return {name:v};
});
},
initialize: function() {
this.fetch();
},
getDatabases: function() {
this.fetch();
return this.models;
},
dropDatabase: function() {
},
createDatabse: function() {
}
});
By default, Backbone create models URLs this way: {collection url}/{model id}.
It consider the collection URL to be a base URL in a RESTful way.
Here you only want to set the Model url property to the URL you whish to call. That'll overwrite the default behavior. http://backbonejs.org/#Model-url
PaperSection = Backbone.Model.extend({
defaults: {
title: '',
position: ''
},
initialize: function(){
},
renderView: function(){
return "<li>"+this.get('title')+", Position: "+this.get('position')+"</li>"
}
});
PaperSectionsList = Backbone.Collection.extend({
url: '/admin/paper/section/list.html',
size: 6,
initialize: function(){
this.add(new PaperSection({
id:1,
title: "Hello World",
position:1
}));
},
comparator: function(section){
return section.get('position');
},
renderView: function(){
var html = "<ul>";
_.each(this.models, function(section){
html += section.renderView();
});
if(_.size(this.models) < this.size){
html+="<li><a href='#add_section' class='btn btn-success btn-small' id='add_section'>Add Section</a></li>"
}
html+="</ul>";
return html;
}
});
PaperSectionView = Backbone.View.extend({
initialize: function(){
this.render();
},
render: function(){
console.log(this.collection.get(1));
var html = this.collection.renderView();
this.$el.html(html);
}
});
var paper_sections = new PaperSectionsList({
model: PaperSection,
});
var section_view = new PaperSectionView({
collection: paper_sections,
el: $('#paper_sections')
});
When I run the code I get the error that section.renderView() is not a function. Need help with this. How do I iterate over models in my collection?
Your first problem is that you are defining your collection and instantiating it incorrectly.
The model declaration needs to happen in the collection's definition, not in the instantiation:
PaperSectionsList = Backbone.Collection.extend({
model: PaperSection,
And then you just instantiate it:
var paper_sections = new PaperSectionsList();
That will get your code working.
But, I feel compelled to point out that you have some confusion about your coding concerns. Models and Collections should never have functions called render*. These are View concerns. In your case, the idiomatic way of handling it would be to have to views: PaperSectionListView (ul) and PaperSectionListItem (li). The templates and render functions live in those views.
I've got your code working as follows...
But I think the above answer handles the core issue, and I agree with the suggestions that the Models and Collections should not be handling render logic.
Note: I cleaned up some JSLint errors such as and extra comma and missing semicolons.
var PaperSection = Backbone.Model.extend({
defaults: {
title: '',
position: ''
},
initialize: function () {
},
renderView: function () {
return "<li>" + this.get('title') + ", Position: " + this.get('position') + "</li>";
}
});
var PaperSectionsList = Backbone.Collection.extend({
url: '/admin/paper/section/list.html',
model: PaperSection,
size: 6,
initialize: function () {
this.add(new PaperSection({
id: 1,
title: "Hello World",
position: 1
}));
},
comparator: function (section) {
return section.get('position');
},
renderView: function () {
var html = "<ul>";
_.each(this.models, function (section) {
html += section.renderView();
});
if (_.size(this.models) < this.size) {
html += "<li><a href='#add_section' class='btn btn-success btn-small' id='add_section'>Add Section</a></li>";
}
html += "</ul>";
return html;
}
});
var PaperSectionView = Backbone.View.extend({
initialize: function () {
this.render();
},
render: function () {
console.log(this.collection.get(1));
var html = this.collection.renderView();
this.$el.html(html);
}
});
$(function () {
var paper_sections = new PaperSectionsList({
model: PaperSection
});
var section_view = new PaperSectionView({
collection: paper_sections,
el: $('#paper_sections')
});
});