How To Change Backbone Dynamic URL with Backbone Events/Vent? - backbone.js

Edited This Below
In this image below I have two main regions.
One for the user list on the left: allusersRegion
And another for the the right side where a layout is displayed, which contains unique attributes to the user that was clicked in the allusersRegion and a list of articles by the user: middleCoreRegion
**If you noticed the middleCoreRegion is showing all articles by all users..This is wrong and I am trying to show all articles of the individual user (in this case. "kev")
I tried to see if my problem was with my JSON api (served via node/rest/mongoose) or with my underscore templates, but if it displays both list then I suppose I need to filter from inside backbone.
At first I tried using a Marionette.vent to simply change the url, but somhow I can't get the _id name into the url: function(), it says undefined...
var someuser = this.model.get("_id");
myApp.vent.trigger("showarticles", someuser);
I add a listener in the backbone collection on the same page:
myApp.vent.on("showarticles", someuser);
**The Edit (A Different Way of Doing this) Here is my code
var usertab = Poplive.module('usertab', {
startWithParent: true,
});
usertab.addInitializer(function() {
User = Backbone.Model.extend({});
UniqueArticle = Backbone.Model.extend({});
//Collections
Users = Backbone.Collection.extend({
model: User,
url: '/api/user2'
});
UniqueArticles = Backbone.Collection.extend({
model: UniqueArticle,
url: '/api/survey'
});
//Layout
var VisitingLayoutView = Backbone.Marionette.Layout.extend({
template: "#visiting-layout",
regions: {
firstRegion: "#listone",
secondRegion: "#listtwo",
thirdRegion: "#listthree",
playRegion: "#playhere",
articlesRegion: "#articleshere"
}
});
AllUserView = Backbone.Marionette.ItemView.extend({
template: "#tab-alluser-template",
tagName: 'li',
events: {
"click #openprofile" : "OpenProfile"
},
OpenProfile: function(){
console.log("Profile is open for " + this.model.get("username"));
var modelo = this.model.get("_id");
var vlv = new VisitingLayoutView({model: this.model});
Poplive.middleCoreRegion.show(vlv);
var ua = new UniqueArticles();
var uacoll = new UniqueArticlesView({collection: ua});
vlv.articlesRegion.show(uacoll);
}
})
//ItemViews
UniqueArticleView = Backbone.Marionette.ItemView.extend({
template: "#unique-article-template"
});
//CollectionViews
AllUsersView = Backbone.Marionette.CompositeView.extend({
template: "#tab-allusers-template",
itemView: AllUserView
});
UniqueArticlesView = Backbone.Marionette.CollectionView.extend({
template: "#unique-articles-template",
itemView: UniqueArticleView
});
//Render Views
var alluserview = new AllUserView();
var allusersview = new AllUsersView();
//Fetch Collections
var theusers = new Users();
theusers.fetch();
var userscoll = new AllUsersView({collection: theusers});
Poplive.allusersRegion.show(userscoll);
});

Assuming UniqueArticle to be the Backbone Model, for the Model with a specific id to be fetched you would need to define the urlRoot property which will append the id of the model to the request.
So the id attribute will be appended to the end of the request the model from the server when you do a fetch on it
var UniqueArticle = Backbone.Model.extend({
idAttribute : 'someuser',
urlRoot : function(someuser){
return '/api/visitingarticles/'
}
// this would send a request for
// /api/visitingarticles/someId
});
var UniqueArticles = Backbone.Collection.extend({
model: Article,
url : function(someuser){
return '/api/visitingarticles/'
}
// /api/visitingarticles -- All Articles will be fetched
});

I think what you want, is to define url as a function, and have a user attribute on your collection:
var UniqueArticles = Backbone.Collection.extend({
model: Article,
initialize: function(){
var self = this;
myApp.vent.on("showarticles", function(someuser){
self.user = someuser;
self.fetch();
}
},
url : function(){
var fragment = '/api/visitingarticles/';
if(this.user && this.user.id){
return fragment + this.user.id;
}
return fragment;
}
});
(Disclaimer: untested code, but it works in my head :D)
Then each time you trigger the event, the userattribute is updated, the collection is reset with the updated url.
As a side note, you might want to look into using a filtered collection. I've implemented that idea in my book, based on Derick Bailey's code here: http://jsfiddle.net/derickbailey/7tvzF/
Here is my version: https://github.com/davidsulc/marionette-gentle-introduction/blob/master/assets/js/entities/common.js
And an example of its use (lines 38-41): https://github.com/davidsulc/marionette-gentle-introduction/blob/master/assets/js/apps/contacts/list/list_controller.js#L38

Related

Connection between model and collection in backbone and parse.com

i'm trying to connect model and collection using parse.com but i'm confused. I'm tring to fetch by collection using backbone and javascript api parse.com but compare this error:POST https://api.parse.com/1/classes 404 (Not Found).
Model:
var Person = Backbone.Model.extend({
defaults:{
},
initialize:function(){
console.log("inperson");
this.validate();
this.send();
},
validate:function(){
console.log("validate");
},
send:function(){
var user = new Parse.User();
user.set("username", this.get("username"));
user.set("password", this.get("password"));
user.set("email", this.get("email"));
user.signUp(null, {
success: function(user) {
// Hooray! Let them use the app now.
},
error: function(user, error) {
// Show the error message somewhere and let the user try again.
alert("Error: " + error.code + " " + error.message);
}
});
}
});
return Person;
});
Collection:
var Usercollection = Parse.Collection.extend({
model:Person,
initialize:function(){
}
});
return Usercollection;
});
and finally the view that call the colletion and fetch:
var HomeView = Backbone.View.extend({
template: Handlebars.compile(template),
events: {
},
initialize: function() {
console.log("inhomeview");
var amici = new Usercollection();
amici.fetch({
success: function(collection) {
amici.each(function(object) {
console.warn(object);
});
},
error: function(amici, error) {
// The collection could not be retrieved.
}
});
},
render: function() {
}
});
return HomeView;
});
Cant you just swap the backbone collection and model to Parse's ones? (You only used the Parse type of the collection, not the model!)
Try switch that Backbone model to a Parse.Object .
Step by step below:
First of all Lets create a new app on Parse.com, mine is called FunkyAppartments.
Insert the script tag for loading Parse javascript lib into index.html or whathever:
<script src="http://www.parsecdn.com/js/parse-1.5.0.min.js"></script>
Switch the backbone model and collection to use parse types instead (and rename the fetch method if you have extended backbones, since we do not want to overide the one of parse):
//var Appartment = Backbone.Model.extend(); Backbone wo. Parse.com
var Appartment = Parse.Object.extend("Appartment");
//var Appartments = Backbone.Collection.extend({ Backbone wo. Parse.com
var Appartments = Parse.Collection.extend({
model: Appartment,
loadAppartments: function(callback){
debugger;
this.query = new Parse.Query(Appartment);
this.fetch();
}
});
I added a debugger tag in the load appartments so that developer tools breaks in the middle of the controller, here I have access to the Appartment private type of the controller, hence i can store some data on the parse server and verify by pasting the below in the developer tools console.
var testAppartment = new Appartment();
testAppartment.save({name: "foobars"}).then(function(object) {
alert("yay! it worked");
});
Yei, the data shows up in the parse.com UI for the app we just added there. And more importantly it shows up in our frontend. That was easy!
UPDATE: PROBLEMS W BACKBONE 1.2.1, MARIONETTE 2.4.2, UNDERSCORE 1.8.3
I noticed that I actually had been using old versions of marionette, backbone and underscore.js. An initial update appeared to break the application.
After some research i found that it was the parse part that did not return objects that would successfully render. Hence I changed the collection type back to an extension of: Backbone.collection instead of Parse.collection.
I also had to override the query method, since the objects would not save on the correct id, updating an object resulted in a new object being added instead of an old one being updated.
var Apartment = Parse.Object.extend('Appartment');
var Apartments = Backbone.Collection.extend({
model: Apartment,
query: new Parse.Query(Apartment),
initialize: function(){
MyApp.vent.on('search:param', function(param){self.search(param); });
var self = this;
this.query.find({
success: function(results){
self.reset();
results.forEach(function(result){
result.attributes.id__ = result.id
var ap = new Apartment(result.attributes);
self.add(ap);
});
}
});
}
});
I added an attribute: id__ to hold the parse id (naming it just id did not work since it backbone interfered with it, making it disappear).
Finally in saving the model to parse i utilized id__ as id in the save call:
var ApartmentEditView = Backbone.Marionette.ItemView.extend({
template: "#apartment-edit-template",
className: "apartmentDetail",
events: {
"click .store": "storeEdit",
"click .close": "closeEdit"
},
storeEdit: function(){
var priceNum = Number($('#price_field').val().replace(/\s/g, ''));
this.model.set({
id: this.model.attributes.id__,
name:$('#name_field').val(),
price:priceNum,
description:$('#desc_field').val(),
url:$('#url_field').val()
});
this.model.save();
this.closeEdit();
},
closeEdit: function(){
var detailView = new ApartmentDetailView({model: this.model});
MyApp.Subletting.layout.details.show(detailView);
}
});
Now the object is updated correctly in the database.

backbone.js getting data into collection and template

i am new to backbone.js and need a little help sending data to a template. Im using a model with fetch, and a collection. here is the code :
(function($) {
var UserModel = Backbone.Model.extend({
urlRoot : '/users',
defaults : {
name : '',
email : ''
},
initialize : function() {
_.bindAll(this);
this.fetch();
},
parse : function(res) {
return JSON.stringify(res);
},
});
var users_coll = Backbone.Collection.extend({
//model: UserModel
initialize : function() {
var u = new UserModel();
this.model = u;
}
});
var displayView = Backbone.View.extend({
initialize : function() {
this.collection = new users_coll();
//_.each(this.collection.models, alert);
//console.log(this.collection);
//alert(JSON.stringify(this.collection.models));
this.render();
},
render : function() {
var tmpl = _.template($("#data-display-tpl").html());
this.$el.html(tmpl);
}
});
var view = new displayView({
el : $("#data-display")
});
})(jQuery);
it's working fine upto the model part. In the parse function of the model, i have used console.log() and everything seems fine. i get a properly formated json, and the fetch works fine too.
however in my collection i get nothing when i try console.log(user_coll.models).
i think i am probably missing something really small. not sure what, maybe the flow of things is all wrong.
I tried to modify your code just a bit to get poin trough...hope it helps clarify few basics.
I also didn't try provided example, but in theory it should work ;)
Here is how his example should be done...
Let's imagine Twitter app for example. Twitter app has only one model that represents one user in system. That's UserModel
var UserModel = Backbone.Model.extend({
urlRoot : '/user', // this is just for modifying one specific user
defaults : {
name : '',
email : ''
},
initialize : function() {
_.bindAll(this);
//this.fetch(); // WRONG: This call was "wrong" here
// fetch() should be done on Collection not model
},
parse : function(res) {
return JSON.stringify(res);
},
});
Now, you can have many lists of users on Twitter right. So you have two lists. In one list you have Friends users, and in other Family users
var UsersFriendsCollection = Backbone.Collection.extend({
model: UserModel // you tell Collection what type ob models it contains
url: '/users/friends',
initialize : function() {
// jabadaba whatever you need here
}
});
var UsersFamilyCollection = Backbone.Collection.extend({
model: UserModel // you tell Collection what type ob models it contains
url: '/users/family',
initialize : function() {
// jabadaba whatever you need here
}
});
...
var displayView = Backbone.View.extend({
initialize : function() {
this.collection = new UsersFriendsCollection();
this.collection.fetch(); // so you call fetch() on Collection, not Model
console.log(this.collection); // this should be populated now
//_.each(this.collection.models, alert);
//alert(JSON.stringify(this.collection.models));
this.render();
},
render : function() {
// collection data is avail. in templating engine for iteration now
var tmpl = _.template($( "#data-display-tpl" ).html(), this.collection);
this.$el.html(tmpl);
}
});
A collection's model attribute is meant for specifying what type of model the collection will contain and if specified you can pass the collection an array of raw objects and it will add and create them. From the docs
Override this property to specify the model class that the collection
contains. If defined, you can pass raw attributes objects (and arrays)
to add, create, and reset, and the attributes will be converted into a
model of the proper type
So when in your code you have
var u = new UserModel();
this.model = u;
You aren't actually adding the model to the collection. Instead you can use the collections add or fetch methods.

Backbone.js model property is getting not defined error

I'm very new to Backbone.js and am trying to get this simple example working. Basically, in jsFiddle when I run the code it tells me that the property "firstname" is not defined.
Here's a link to the fiddle:
http://jsfiddle.net/cpeele00/YjUBG/16/
var User = Backbone.Model.extend({});
var UserList = Backbone.Collection.extend({
model: User
});
var UserView = Backbone.View.extend({
el: $('#user-list ul'),
template: _.template($('#user-list-template').html()),
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var user1 = new User();
user1.set({
firstname: 'Momo',
lastname: 'Peele'
});
var user2 = new User();
user2.set({
firstname: 'Bobo',
lastname: 'Peele'
});
var users = new UserList([user1, user2]);
var userView = new UserView({model: users});
userView.render();
​
Any help figuring this out would be greatly appreciated.
V/R
Chris
Since the model is actually a collection, you need to iterate over it, and apply the template to each model in the collection. One way is to use the Underscore extension Collection.each:
render: function() {
// clear the view
this.$el.empty();
// save a reference to the view object
var self = this;
// iterate over the collection
this.model.each(function(singleModel) {
// render the model
self.$el.append(self.template(singleModel.toJSON()));
});
return this;
}
Here's the updated Fiddle.
(You could also put the iteration into the template itself if you like, but I think it's generally preferable to keep code in the view, rather than the template.)

Empty backbone collection/model at working API?

i try to fetch a record of a rails-api (same host) into my backbone collection. i have the following code:
// Models
App.GeeksModel = Backbone.Model.extend({
urlRoot: "/geeks",
idAttribute: "id"
});
// Collections
App.GeeksCollection = Backbone.Collection.extend({
url: "/geeks",
model: App.GeeksModel
});
in my router i have the following
// Router
App.GeekRouter = Backbone.Router.extend({
routes: {
"": "index"
},
initialize: function() {
console.log("router - init");
},
index: function() {
console.log("route - index");
var geekCollection = new App.GeeksCollection();
var mapView = new App.GeeksMapView({ el: $("#foo"), model: geekCollection });
geekCollection.fetch();
}
});
when browsing the url, the view loads correctly and at the server i see, that one entry is fetched from the database. but as soon as i check the model length in my view using
this.model.length
the collection is empty... any advice on this?
thanks
EDIT 1:
when changing the index router method to
var mapView = new App.GeeksMapView({ el: $("#map"), collection: geekCollection });
and e.g. check for the collection length in the views intialize method
...
initialize: function() {
this.render();
console.log(this.collection.length);
},
...
it retunes 0 as well... so nothing changed!
I believe you want to do collection.length or if accessing from the model - each model holds reference to collection in which it was created model.collection.length - if this is referencing to collection doing just this.length should be enough, if it's a model then this.collection.length will do it for you.
Models have no property length so should always be undefined unless you define it yourself.

How to set a Collection's url

let's say I have :
var Book = Backbone.Model.extend();
var Collection = Backbone.Collection.extend({
model: Book,
url: '/books',
initialize: function(){
this.fetch();
})
})
How can I change the Collection's url when instantiating a new collection ?
var AdventureBooks = new Books({ url: '/books/adventure' }) does not work
var AdventureBooks = new Books({ category: 'adventure' })
and in the Collection definition:
url : '/books/' + this.category does not work either.
Thanks.
The following should work:
var AdventureBooks = new Books();
AdventureBooks.url = '/books/adventure';
var Book = Backbone.Model.extend({
"url": function() {
return '/books/' + this.get("category");
}
});
For some reason the parameters passed to Collection constructor (for example "url") are not set to the object. The collection uses only few of those (model and comparator).
If you want to pass the url via constructor you need to create initialize method that copies the necessary parameters to the object:
var Book = Backbone.Model.extend({
initialize: function(props) {
this.url = props.url;
}
}
var book = new Book({url: "/books/all"});
Like Daniel Patz pointed out , the problem lies in how you're instantiating the collection. I just struggled with this for a bit right now, so I thought I'd update this, even though the question is somewhat old.
The first argument is expected to be an array of models, with the options coming after. This should work:
var AdventureBooks = new Books([], { url: '/books/adventure' })
If you want a dynamic URL, then Raynos' answer might be the way to go.
If you want to have dynamic urls for your collection, try this (tested with backbone 1.1.2):
Create an instance of your backbone collection and pass the dynamic url parameter as an option (the options object needs to be the the second argument as the first one is an optional array of models):
var tweetsCollection = new TweetsCollection(null, { userId: 'u123' });
Then inside of your collection, create a dynamic url function that uses the value from the options object:
var TweetsCollection = Backbone.Collection.extend({
url: function() {
return '/api/tweets/' + this.options.userId;
},
model: TweetModel
});
The best solution for me is the initialize method, look at this example:
Entities.MyCollection = Backbone.Collection.extend({
model: Entities.MyModel,
initialize: function(models,options) {
this.url = (options||{}).url || "defaultURL";
},
}
use it as follows:
var items = new Entities.MyCollection(); //default URL
var items = new Entities.MyCollection([],{url:'newURL'}); //changed URL
I know that this a late reply, but I had a similar although slightly more complicated situation, and the selected answer didn't really help me.
I have a Conditions collection, and each Experiment model has multiple conditions, and I needed my url to be /api/experiments/:experimentId/conditions, but I didn't know how to access the experimentId from the empty Conditions collection.
In my Conditions collection url function, I did a console.log(this.toJSON()) and discovered
that Backbone inserts a single dummy model in the empty collection with whatever attributes you passed in at it's creation time.
so:
var Conditions = new ConditionsCollection({
experimentId: 1
});
I somehow doubt that this would be considered a best practice, hopefully someone else will respond with a better solution, but here's how I defined my Collection:
var ConditionsCollection = Backbone.Collection.extend({
model: Condition,
url: function(){
var experimentId = this.at(0).get("experimentId");
return "/api/experiments/" + experimentId + "/conditions";
}
});
This work for me (tested with backbone 1.2.1):
var serverData = Backbone.Collection.extend({
url: function() {
return '//localhost/rest/' + this.dbname;
},
constructor: function(a) {
if(a.dbname){
this.dbname = a.dbname;
}
Backbone.Collection.apply(this, arguments);
}
});
use it as follows:
var users = new serverData({dbname : 'users'});

Resources