I'm trying to sync my Backbone.js app to the server.
I'll note that I overrided my sync in the collection to use jsonp:
window.Project = Backbone.Model.extend({
initialize:function () {
},
urlRoot:"http://cshosting.webfactional.com/api/v1/projects",
defaults:{
"id":null,
"completion_state":"0",
"last_update_datetime":"0"
}
});
window.ProjectList = Backbone.Collection.extend({
model: Project,
url:"http://cshosting.webfactional.com/api/v1/projects/",
sync: function(method, model, options) {
options.dataType = 'jsonp';
options.url="http://cshosting.webfactional.com/api/v1/projects/? format=jsonp&callback=moshe";
//options.contentType='application/json-p';
options.error=this.errorr;
return Backbone.sync(method, model, options);
},
parse: function(response) {
return response.objects;
}
});
The problem is, while "fetch" is working just fine, when I try to "create" a new model to the collection - I get this network error:
Your application is not using your jsonp configuration in the Collection.create() This is because the Collection delegates in the Model.save() to create elements.
Check the Collection.create() code.
You should override the Project.sync() as well.
Related
I'm creating a collection in backbone js, after this fetch data from the backend, I print in the console inspector in chrome, but something caught my attention in the attributes.
The collection has models, and inside of each model has an attribute called "collections" and inside of this has a attribute call "models" and so on
I'm not interacting yet with the views, only create a collection and fetch de data.
This is what happens in the console:
This is the code that I'm using for the parse:
var TablesCollections = Backbone.Collection.extend({
model: Table,
url: '/api/tables',
parse: function(response) {
if(response.procced == 7) {
return response.data;
} else {
return "An error has trigger";
}
},
initialize: function() {
this.fetch();
}
});
And this is the models:
var Table = Backbone.Model.extend({
defaults: {
'title': '',
'titlestring' : '',
'schema': {},
'manageschema': {},
},
parse: function(response){
if(response.proceed){
if(response.proceed == 4){
response.data.schema = JSON.parse(response.data.schema);
response.data.manageschema = JSON.parse(response.data.manageschema);
response = response.data;
}
} else {
if(response.schema != 'Na'){
response.schema = JSON.parse(response.schema);
}
if(response.manageschema != 'Na'){
response.manageschema = JSON.parse(response.manageschema);
}
}
return response;
},
});
Why is there multiple copies of the collection nested inside the models?
This is normal. Each Backbone Model that is added to a collection has a reference to the collection that it belongs to. (accessed via this.collection where this is the model)
What you see in the console is a circular reference. The collection has models. Each model has a reference to the collection. That same collection has the same models which have the same reference to the collection, etc...
Model's constructor documentation:
The model.collection property is normally created automatically when you first add a model to a collection.
In your screenshot, you can see that cid: "c8". This is the client id which Backbone adds to models and collections. This shows you that it's the same model reference each time.
I have a collection, which fetches data from URL.
BarCollection = Backbone.Collection.extend({
model: BarModel,
url: // Some URL
});
But the problem is that I want to fetch data to this collection not only from URL, but also from local storage. I wish I could do something like that:
BarCollection = Backbone.Collection.extend({
model: BarModel,
url: // Some URL,
localStorage: new Backbone.LocalStorage('bars')
});
But .fetch() method cannot get data both from url and local storage.
Simple workaround is to create two different collections: one for URL and one for local storage. And after fetching just merge them.
BarCollection = Backbone.Collection.extend({
model: BarModel,
url: // Some URL
});
LocalBarCollection = Backbone.Collection.extend({
model: BarModel,
localStorage: new Backbone.LocalStorage('local-contributors')
});
I wonder if there is a more beautiful way of doing that.
To enable any collection or model to synchronize from both the localStorage and a server, Backbone's sync function can be overridden:
Backbone.sync = (function(sync) {
return function(method, model, options) {
options = options || {};
var key = _.result(model, 'localStorage'),
response;
// if the localStorage property exist on the model/collection
// first try to sync with the localStorage
if (key) {
switch (method) {
case 'create':
case 'update':
var data = model.toJSON(),
text = JSON.stringify(data);
localStorage.setItem(key, text);
break;
case 'delete':
localStorage.removeItem(key);
break;
case 'read':
response = JSON.parse(localStorage.getItem(key));
if (response) model.set(response, { parse: true });
break;
}
}
// then, always sync with the server as it normally would
return sync.apply(this, arguments);
};
})(Backbone.sync);
This way, if a model or a collection as a localStorage property, it'll first sync with the localStorage, then it'll make the original sync.
Example model and collection:
var BarModel = Backbone.Model.extend({
urlRoot: 'some/url',
localStorage: function() {
return 'bars-' + this.id;
},
});
var BarCollection = Backbone.Collection.extend({
model: BarModel,
url: '/some/url',
localStorage: 'bars',
});
is my first question here, so I please about some patience and forgive my english:)
When I type link in browser address bar, all is OK. But when I do this inside browser by clicking element, collection is empty. But the main problem is there is always the same response from server, but fetch "dont load" any items, so view render empty collection.
I use Backbone Boilerplate,
Browser.Views.Catalog - it is Backbone.View
Browser.Catalog - it is of Backbone.Collection
My router:
var Router = Backbone.Router.extend({
routes: {
'' : 'browse'
},
refreshCatalog: function(folder){
app.layout.setViews({
"#catalog" : new Browser.Views.Catalog({
collection: app.catalog
})
}).render();
},
browse: function(folder){
app.catalog = new Browser.Catalog();
app.folders.fetch({
error: function() { console.log(arguments); },
success: this.refreshFolders(folder),
data: $.param({folder: folder}),
cache:false
});
//app.catalog = new Browser.Catalog();
app.catalog.fetch({
error: function() { console.log(arguments); },
success: this.refreshCatalog(folder),
data: $.param({folder: folder}),
cache:false
});
},
I belive you should set the catalog in the initialize function
app.catalog = new Browser.Catalog();
should go in here ( add this function)
initialize: function (options) {
app.catalog = new Browser.Catalog();
}
the initialize function is called when the page is loaded so when browsing to #catelog it will have been set http://backbonejs.org/#Router-constructor
I believe my problem relates to scope somehow, as I'm a js newbie. I have a tiny backbone.js example where all I am trying to do is print out a list of items fetched from the server.
$(function(){
// = Models =
// Video
window.Video = Backbone.Model.extend({
defaults: function() {
return {
title: 'No title',
description: 'No description'
};
},
urlRoot: 'api/v1/video/'
});
// VideoList Collection
// To be extended for Asset Manager and Search later...
window.VideoList = Backbone.Collection.extend({
model: Video,
url: 'api/v1/video/'
});
// = Views =
window.VideoListView = Backbone.View.extend({
tagName: 'ul',
render: function(eventName) {
$(this.el).html("");
_.each(this.model.models, function(video) {
$(this.el).append(new VideoListRowView({model:video}).render().el);
}, this);
return this;
}
});
// VideoRow
window.VideoListRowView = Backbone.View.extend({
tagName: "li",
template: _.template("id: <%= id %>; title: <%= title %>"),
className: "asset-video-row",
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
// Router
var AppRouter = Backbone.Router.extend({
routes:{
"":"assetManager"
},
assetManager:function() {
this.assetList = new VideoList();
this.assetListView = new VideoListView({model:this.assetList});
this.assetList.fetch();
$('#content').html(this.assetListView.render().el);
}
});
var app = new AppRouter();
Backbone.history.start();
// The following works fine:
window.mylist = new VideoList();
window.mylistview = new VideoListView({model:window.mylist});
});
If I access mylist.fetch(); mylist.toJSON() from the console, mylist populates fine. I can tell that this.assetList.fetch() is accurately fetching the data from the backend, but it doesn't appear to be adding the objects to this.assetList.
The fetch method on Backbone collections is asynchronous:
Fetch the default set of models for this collection from the server, resetting the collection when they arrive. [...] Delegates to Backbone.sync under the covers, for custom persistence strategies.
And Backbone.sync says:
Backbone.sync is the function that Backbone calls every time it attempts to read or save a model to the server. By default, it uses (jQuery/Zepto).ajax to make a RESTful JSON request.
So fetch involves an (asynchronous) AJAX call and that means that you're trying to use the collection before fetch has retrieved the data from the server. Note that fetch supports success and error callbacks so you can do this instead:
var self = this;
this.assetList.fetch({
success: function(collection, response) {
$('#content').html(self.assetListView.render().el);
}
});
Or you could bind a callback to the collection's reset event as fetch will reset the collection. Then render your assetListView when the collection's reset event is triggered.
Also, your assetList is a collection so you should be doing:
this.assetListView = new VideoListView({collection: this.assetList});
and:
window.VideoListView = Backbone.View.extend({
tagName: 'ul',
render: function(eventName) {
$(this.el).html("");
_.each(this.collection.models, function(video) {
// ...
I wanted to see how i could save a model to server using model.save() method when urlRoot is specified on the extended model, but ajax request never fires when i ask for model.fetch() or do model.save(). note: Is hope this is possible without using Collection i suppose?.
HTML
<div id="placeholder"></div>
<script type="text/template" id="view_template">
Hello <%= name %>, here is your script <%= script %>
</script>
Model
window["model"] = Backbone.Model.extend({
initialize: function () {
console.log("CREATED");
},
defaults:{
name:"Please enter your name",
script:"Hello World"
},
urlRoot: "index.aspx",
validate: function (attrs) {
},
sync: function (method, model, success, error) {
console.log("SYNCING", arguments);
}
});
View
window["view"] = Backbone.View.extend({
template:_.template($("#view_template").html()),
initialize: function () {
console.log("INITIALISED VIEW");
this.model.bind("change","render",this);
},
render: function (model) {
console.log("RENDERING");
$(this.el).append(this.template(model));
return this;
}
});
Application
$("document").ready(function () {
var myModel = new model({
name: "Stack Overflow",
script: "alert('Hi SO')"
});
var myView = new view({
model: myModel,
el: $("#placeholder")
});
console.log("SAVING");
myModel.save();
console.log("FETCHING");
myModel.fetch();
});
as you can see in application i call save & fetch but as per documentation this should fire ajax request with POST -> SAVE & GET -> FETCH. But all it does is log's arguments into console in the sync function.
I think the only reason you are not seeing any Ajax requests is that you have overridden the Model.sync method. Normally you would only do this if you wanted to replace the default Ajax syncing implemented in Backbone.sync. See the following line in Model.fetch in backbone.js:
return (this.sync || Backbone.sync).call(this, 'read', this, options);
I did a quick test with your code and I am seeing the Ajax requests if I rename your Model.sync method.