I need to work with backbone.js, i can't go to "render" part inside my view here is my code:
var Vigne = {
Models:{},
Collections: {},
Views:{},
Templates:{}
}
Vigne.Models.Raisin = Backbone.Model.extend({})
Vigne.Collections.Grape = Backbone.Collection.extend({
model: Vigne.Models.Raisin,
url: "./scripts/data/vin.json",
initialize: function (){
console.log("grape initialised");
}
});
Vigne.Views.Grape= Backbone.View.extend({
initialize: function(){
_.bindAll(this,"render");
this.collection.bind("reset",this.render);
},
render: function(){
console.log("render");
console.log(this.collection.length);
}
})
Vigne.Router = Backbone.Router.extend({
routes:{
"": "defaultRoute"
},
defaultRoute: function(){
console.log("defaultRoute");
Vigne.grape = new Vigne.Collections.Grape()
new Vigne.Views.Grape ({ collection : Vigne.grape });
Vigne.grape.fetch();
console.log(Vigne.grape.length);
}
}
);
var appRouter= new Vigne.Router();
Backbone.history.start();
I am expecting it to display my collection's length in the debugger's console, it seem's like it doesn't reset. Any ideas?
Edit:
i added this within the fetch function:
success: function(){
console.log(arguments);
},
error: function() {
console.log(arguments);
}
});
and the fetch function succeed on getting the json file, but it doesn't trigger the reset function.
i solved this problem by setting the attribute within the fetch function to true:
Vigne.grape.fetch({
reset:true,
error: function() {
console.log(arguments);
}
}
);
This book helped me : http://addyosmani.github.io/backbone-fundamentals/
Backbone calls reset() on fetch success which in turns triggers reset event. But If your fetch fails due to some reason, you won't get any event. So you have to pass an error handler in fetch method and use it to identify the error and handle it.
Vigne.grape.fetch({
error: function() {
console.log(arguments);
}
});
You can also pass success call back and you will be able to know the problem in your fetch.
You can also use Charles proxy/Chrome Debuuger Tool to identify if you are getting proper response from your backend.
Can you please paste your response what you are getting from server. You may vary the data but just keep the format right.
Edit:
One more problem I can see is that you have not defined attributes in your model So after Backbone fetch, it refreshes your collection with the new models fetched from server. Fetch method expects an array of json objects from server and each json object in response array should match with the attributes you have set in defaults in your model Otherwise it won't be able to create new models and hence won't be able to refresh your collection. Can you please rectify this and let me know.
Related
I am using a rails server that returns this JSON object when going to the '/todos' route.
[{"id":1,"description":"yo this is my todo","done":false,"user_id":null,"created_at":"2015-03-19T00:26:01.808Z","updated_at":"2015-03-19T00:26:01.808Z"},{"id":2,"description":"Shaurya is awesome","done":false,"user_id":null,"created_at":"2015-03-19T00:40:48.458Z","updated_at":"2015-03-19T00:40:48.458Z"},{"id":3,"description":"your car needs to be cleaned","done":false,"user_id":null,"created_at":"2015-03-19T00:41:08.527Z","updated_at":"2015-03-19T00:41:08.527Z"}]
I am using this code for my collection.
var app = app || {};
var TodoList = Backbone.Collection.extend({
model: app.Todo,
url: '/todos'
});
app.Todos = new TodoList();
However, when trying to fetch the data it states that the object is undefined. I originally thought that my function wasn't parsing the JSON correctly. However, that doesn't look to be the case. I created a parse function with a debugger in it to look at the response. In gives back, an array with three objects.
Here what happens when I try testing the fetch().
var todos = app.Todos.fetch()
todos.length // returns undefined
todos.get(1) // TypeError: undefined is not a function
The todos collection doesn't automatically populate the function get() in console. I am running out of ideas of what can be the problem. Please help. Thanks!
Fetch is a ayncronous, you need to listen to the add event:
var todos = app.Todos.fetch()
todos.on('add', function(model){
console.log(todos.length);
});
If you pass the parameter reset, you could listen for the would new models:
var todos = app.Todos.fetch({reset: true})
todos.on('reset', function(model){
console.log(todos.length);
});
You could also read here.
There are two problems:
Fetch is asynchronous; we don't know exactly when we'll have a result, but we do know that it won't be there when you are calling todos.length.
Fetch sets the collection's contents when it receives a response; calling app.Todos.fetch() will result in app.Todos containing whatever models were fetched by the request. Its return value is not useful for inspecting the collection, so var todos = app.Todos.fetch() won't give you what you want in any case.
If you want to inspect what you receive from the server, your best option is to set a success callback:
app.Todos.fetch({
success: function (collection, response, options) {
console.log(collection);
}
});
Following Backbone/Marionette Controller and Collection won't fetch.
define(["jquery", "backbone","models/Poi"],
function($, Backbone, Poi) {
// Creates a new Backbone Poi class object
var PoiCollection = Backbone.Collection.extend({
model:Poi,
parse: function (response) {
console.log(response);
// Return people object which is the array from response
return response;
}
});
// Returns the Poi class
return PoiCollection;
}
);
define(['App', 'backbone', 'marionette', 'views/MapView', 'views/DesktopHeaderView', 'views/DesktopFooterView', 'models/Poi'],
function (App, Backbone, Marionette, MapView, DesktopHeaderView, DesktopFooterView, Poi) {
return Backbone.Marionette.Controller.extend({
initialize: function (options) {
App.headerRegion.show(new DesktopHeaderView());
App.mainRegion.show(new MapView());
App.footerRegion.show(new DesktopFooterView());
},
//gets mapped to in AppRouter's appRoutes
index: function () {
console.log("Ajax::list of POI");
var p = new Poi();
p.fetch({
success: function (data) {
console.log("data");
}
});
console.log(p);
}
});
});
I have no Idea where to look to debug this. The Network tab tells me that the data was fetched, but the success method is never called.
Thanks
I think your fetch call itself looks OK, but two other apparent bugs could be affecting that call:
1) Your log message in the index function says "list of Poi", but you're using a (single) Poi instance -- should that be PoiCollection instead? I'm assuming the Poi model (not shown above) is for a single item.
2) There's no url property in the PoiCollection, so if you did fetch a PoiCollection instead, that call would fail because PoiCollection doesn't know what URL to use. The most common pattern with Collection + related Model is to put an url only in the Collection, and no url in the single Model for the Collection's individual items (Poi in this case). Backbone will construct the corresponding individual-model URLs as needed based on the parent Collection's url. I think getting the url straightened out will help here.
Finally, one more thing: the fist parameter passed to the fetch call's success function is the Model or Collection instance itself, not the raw data object. That's not relevant for the current success code you have now (you're only logging a static string), but it will be relevant as soon as you try using that parameter. :-)
I'm feeling stupid asking the question, but I'm with it like 3 days ago... I built a php api rest, that returns a json encode info. The json that the api returns is correct, is something like that:
[
{"pk_user":"1","name":"George","surname":"Littlewolf","profile":"Game designer - Developer"},
{"pk_user":"2","name":"Anne","surname":"Carpenter","profile":"Developer"},
{"pk_user":"3","name":"John","surname":"Sullivan","profile":"Designer"},
{"pk_user":"4","name":"Judith","surname":"Lenders","profile":"Developer"},
{"pk_user":"5","name":"Jason","surname":"Middleton","profile":"Manager"}
]
After that, I'm creating a backbone front-end to attack the api. I have developed only one view, that reads the json I wrote above. I have one model (User) and one Collection (Users) where I'd like to save the info read from the json info.
I'm going to put only the code of the render function of the view:
render: function() {
var users = new Users();
users.fetch();
$.each(users['models'], function(i, item) {
console.log(item);
});
this.template = _.template(template, {users : users});
this.$el.html(this.template);
return this;
}
Now, the console returns only what you see in the next image:
I'd like to access directly to the items, but i don't know why but all that I've tried is notworking... Thanks and excuse me for the newbie explanation...
EDIT
I've uploaded the code to a hosting to everyone who want to see the problem "on live".
fetch is an AJAX call so this:
users.fetch();
won't fill the collection right away, it has to wait for the server to respond. So when you go to look at users.models, there won't be anything there and your view ends up doing nothing.
The usual approach is to listen for events from users and render HTML in response to those events:
initialize: function() {
this.collection = new Users;
this.listenTo(this.collection, 'reset', this.render);
// bind to other events as needed...
this.users.fetch({ reset: true });
},
render: function() {
this.collection.each(function(user) {
// render the `user` model in here...
});
}
Note the switch to this.collection.each instead of messing around with the collection's models property directly.
Alternatively, use a success handler when you call fetch:
this.users.fetch({
success: function(collection) {
collection.each(function(user) {
// render the `user` model in here...
});
}
});
Not sure if it is what you want, but if you want to log the attributes of the User just do this:
$.each(users['models'], function(i, item) {
console.log(item.attributes);
});
Scenario
I am working on backbone app. What is happening right now is when user clicks edit link on page then it should show a form. I am trying to implement this using backbone routers rather than events. With events object it works perfectly fine. To use routers, I am using global events.
Problem
The problem is that when user clicks on edit link, it shows me following error in console
Uncaught TypeError: Object 10 has no method 'toJSON' views.js:57
This error is because on line 57 in views.js, I am using this.model.toJSON() whereas I am not passing model via router. I don't know how pass model through router
Here is my router. Note: All of the following codes are in separate files
App.Router = Backbone.Router.extend({
routes: {
'contacts/:id/edit': 'editContact'
},
editContact: function (id) {
console.log('yahhhhh');
vent.trigger('contact:edit', id);
}
});
In above router I am triggering an event inside editContact function. Then I am listening to above event in following initialize function.
App.Views.App = Backbone.View.extend({
initialize: function () {
vent.on('contact:edit', this.editContact, this);
},
editContact: function (contact) {
var editContactView = new App.Views.EditContact({ model: contact });
$('#editContact').html(editContactView.render().el);
}
});
Now in above after listening to event in initialize function, I am calling editContact function and I am also passing model using this keyword. Inside editContact function, I am creating an instance of EditContact, view which is following, and then rendering a form which needs to be shown.
App.Views.EditContact = Backbone.View.extend({
template: template('editContactTemplate'),
render: function () {
var html = this.template(this.model.toJSON()); //<--- this is line 57
this.$el.html(html);
return this;
}
});
After doing all of the above, the form is not shown and I am getting above mentioned error.
Question
How do I pass model to render function inside EditContact via router so that it starts working?
UPDATE
Here is my model
App.Models.Contact = Backbone.Model.extend({
urlRoot : '/contacts'
});
In your editContact method the argument contact refers to the id you pass onwards from the router. When you initialize a new view with new App.Views.EditContact({ model: contact }) the model of the view will, expectedly, be the id.
You need to map the id into a model instance. IMHO the correct place to do this is in the router:
editContact: function (id) {
var contact = new App.Models.Contact({id:id});
vent.trigger('contact:edit', contact);
}
Notice that at this point the model will only have the id property set. If you need to populate the model properties for editing, you should fetch the model from the server, and only then trigger the event:
editContact: function (id) {
var contact = new App.Models.Contact({id:id});
contact.fetch().done(function() {
vent.trigger('contact:edit', contact);
});
}
Edit based on comments: Generally speaking you shouldn't pass anything to the router. The router should be a starting point for every new request (url change). If you want to hold some state between page changes, you should store the data on the router level, and pass the models and collections "down" from the view.
In a simplified scenario this would mean initializing and storing a reference to the collection in the router. Something like:
var Router = Backbone.Router.extend({
initialize: function() {
this.contactCollection = new App.Collections.Contacts();
},
editContact: function (id) {
id = parseInt(id, 10);
if(_.isNaN(id)) {
//error...
}
//try to get a model from the collection
var contact = this.contactCollection.get(id);
//if not found, create, add and fetch
if(!contact) {
contact = new App.Models.Contact({id:id});
this.contactCollection.add(contact);
contact.fetch().done(function() {
vent.trigger('contact:edit', contact);
});
} else {
vent.trigger('contact:edit', contact);
}
}
});
Please note that this is just example code, and not necessarily how you should implement it, line by line. You should consider whether it's OK to display a potentially stale model in the view, or whether you should always fetch it from the server. In practice you might also abstract the collection state in a nice class, instead of handling it directly in the router.
Hope this answers your questions.
How i can refresh model's attributes when invoke save()
I am using backbone and backbone relational. Have the following code:
saveParams: function(event){
var self = this;
this.model.save({}, {
success: function(model, resp, xhr){
model = ...
},
error: function(model, resp){
alert(JSON.stringify(resp));
}
});
$(this.el).effect("highlight", {}, 1000);
event.preventDefault();
},
When pass callback success, the parameters "model" has ald attributes (before save), resp is holding updated attributes. How i can update attributes in a model?
model.set(resp) doesn' help me
model.set(JSON.stringify) doesn't help me
UPD1: I use Backbone RelationModel cause have nested models. Nested models doesn't refresh when success callback invokes. I guess because RelationModel using Backbone.Store.
UPD2: For me works only this:
model.clear()
model.set(resp);
model.change();
I know it's ugly, but it's working )
Normally you don't have to do this!
Backbone automatically parses the response if a save() command, as you can see here:
http://documentcloud.github.com/backbone/docs/backbone.html#section-41
If your response data differs from the default assumed backbone data structure you should take a look at the Backbone.Model#parse method and maybe overwrite it (it's a very simple method).