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. :-)
Related
I thought that initializing the collection returned a ready instance of Backbone.Collection. After the fetch, however, the collection contains some models, but in require, _this.serviceCollection.toJSON() gives me undefined object.
My Backbone.Collection:
var ServiceCollection = Backbone.Collection.extend({
model: Backbone.Model,
initialize: function(options){
this.fetch({
url: 'service/' + options + '/',
async: false,
success: function(collection, response, options){
collection.set(response.result);
}
});
}
});
return ServiceCollection;
My CollectionView showing:
OpenServices: function(category){
var _this = this;
require([
'service/js/service_collection',
'service/js/service_collection_view'
], function(ServiceCollection, ServiceCollectionView){
_this.serviceCollection = new ServiceCollection(category);
_this.serviceCollectionView = new ServiceCollectionView({
collection: _this.serviceCollection
});
_this.categoryLayout.categoryItems.show(_this.serviceCollectionView);
});
}
What's wrong this code?
Mistake was in sync/async request. My fetch was async: false, so I'm just change it to async: true and a set event on reset collection for that collectionView.
I suspect the issue you are having is related to your success callback. There should be no need to call collection.set(...) to add the models to the collection; that is done automatically for you when the fetch succeed. In fact, the documentation around Collection.set provides this nugget:
... if the collection contains any models that aren't present in the list, they'll be removed.
I suspect that response.result in your success callback doesn't contain any of the data you think it does. Because of that, your call to collection.set(...) is actually removing all items from the collection.
Try removing that success callback & see what else happens.
Somewhat unrelated but still important:
Using Collection.fetch(...) synchronously is considered bad practice; if your server takes longer than a few hundred milliseconds to return the data, the browser may lock up.
Specifying the url as a parameter to Collection.fetch(...) is not a terrible idea, but consider extending Backbone.Model and specifying the urlRoot parameter instead. If your server follows REST-ful conventions, it makes it very easy to create/update/delete data that way.
I am very new in backbone js.
I am trying to filter some specific key and values in backbone js model extend here is the code below.
var items = ["open","close"];
var ReportModel = Backbone.Model.extend({
url: function() {
return tab+".json";
}
});
where tabe is dynamic json file name.In my json file many key value pair are there but I want to load only those key which is mentioned in items list.
I saw some where using parse function but that a;so did not work out.Please do let me know how to filter the specific keys form json using the backbone.
I also tried creating a dict from json and pass it to model like.
var ReportModel = Backbone.Model.extend({
"open":{.......}
});
but there I am getting issue.
throw new Error('A "url" property or function must be specified');
Please help me out with this.
You are missing some steps to be succesfull on your task.
First a note about the error: Backbone expects a string on the url property while you're passing a function. If you want to use a function to return your url dinamically use urlRoot.
Now onto the real coding:
since you talk about a json file that has multiple key value, maybe you should declare your model as a key-value object, and then create a Backbone.Collection that will wrap your models.
A Backbone.Collection expose a lot of utilities that can help us modeling the results, in this case by using the where() function of our collection you will be able to filter the data after you have retrieved from the remote file.
Alternatively to filter your collection if you need more control over the function you can always call the undescore function filter() .
Please refer to the official documentation of underscore and backbone, as you will find a lot of functions that can help you and most of them have an example that shows how to use them.
Now that we have everything lets create our Backbone.Collection that will wrap your already defined model:
var ReportCollection = Backbone.Collection.extend({
model: ReportModel,
urlRoot: function(){
return 'yoururl.json';
}
});
now if you want to filter the result you can simply fetch the collection and perform a filter on it:
var myReports = new ReportCollection();
//call the fetch method to retrieve the information from remote
myReports.fetch({success: function(){
//the collection has been fetched correctly, call the native where function with the key to be used as a filter.
var filteredElements = myReports.where({my_filter_key : my_filter_value});
});
in your filteredElements you will have an array of object made up of all the model that matched the key/value passed to the where function.
If you need a new Collection from that you just need to pass the result as argument: var filteredCollection = new ReportCollection(filteredElements);
You can use _.pick() in the parse method as shown below:
var items = ["open", "close"];
var ReportModel = Backbone.Model.extend({
url: function() {
return tab + ".json";
},
parse: function(response) {
return _.pick(response, items);
}
});
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);
}
});
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.
How can I set a default url/server for all my requests from collections and models in Backbone?
Example collection:
define([
'backbone',
'../models/communityModel'
], function(Backbone, CommunityModel){
return Backbone.Collection.extend({
url: '/communities', // localhost/communities should be api.local/communities
model: CommunityModel,
initialize: function () {
// something
}
});
});
I make an initial AJAX call to get my settings including the url of the API (api.local).
How can I reroute the requests without passing it to all my models or hardcoding the url in the models and collections?
your url takes a string or a function.
with your settings ajax call you can store it in a proper location,
and go fetch that from the function
to use your example:
suppose your ajax call, saved the url in a myApp.Settings.DefaultURL
define([
'backbone',
'../models/communityModel'
], function(Backbone, CommunityModel){
return Backbone.Collection.extend({
url: function(){
return myApp.Settings.DefaultURL + '/communities';
},
model: CommunityModel,
initialize: function () {
// something
}
});
});
remark
make sure this url is somehow caught or handled when it would be fired before your settings are set, if your initial ajax call fails or takes its time, your app might already be started without it's settings set, should one use a model.save() at that point, you would need to handle this.
By over-riding (but not over-writing) the Backbone.sync method you can achieve the same result without having to add the same code to every single model/collection.
define(['underscore', 'backbone', 'myApp'], function (_, Backbone, myApp) {
'use strict';
// Store the original version of Backbone.sync
var backboneSync = Backbone.sync;
// Create a new version of Backbone.sync which calls the stored version after a few changes
Backbone.sync = function (method, model, options) {
/*
* Change the `url` property of options to begin with the URL from settings
* This works because the options object gets sent as the jQuery ajax options, which
* includes the `url` property
*/
options.url = myApp.Settings.DefaultURL + _.isFunction(model.url) ? model.url() : model.url;
// Call the stored original Backbone.sync method with the new url property
backboneSync(method, model, options);
};
});
Then in your model/collection you can just declare the url as usual (e.g. url: '/events)
Don't forget to require the file with the new Backbone.sync code somewhere.