I am trying to make ajax request to server. I am using fetch() method on collection. It successfully retrieves data from server after monitoring google chrome network tab.
Here is scenario.
I have Model below
var Model = Backbone.Model.extend({
urlRoot: '/contacts',
});
collection below
var Collection = Backbone.Collection.extend({
model: Model,
url: '/contacts'
});
ViewOne below
var ViewOne = Backbone.View.extend({
initialize: function () {
var collection = new Collection().fetch(); // also tried with these options { wait:true, reset: true, async: false }
new ViewTwo({ collection: collection });
}
});
Below is ViewTwo
var ViewTwo = Backbone.View.extend({
initialize: function () {
this.collection.each(this.render, this); // Uncaught TypeError: undefined is not a function
},
render: function () {
console.log(this.model);
}
});
As you can see I am getting Uncaught TypeError: undefined is not a function error.
this.render exist so that is not a problem. The problem is each function on this.collection. How can I solve this proble?
simply create new view in success callback of fetch
var ViewOne = Backbone.View.extend({
initialize: function () {
var collection = new Collection();
collection.fetch({
success:function(){
new ViewTwo({ collection: collection });
}
});
}
});
Backbone API is not fluent, which means that Collection.fetch does not return itself, it returns the Ajax object used in fetch.
Try
var ViewOne = Backbone.View.extend({
initialize: function () {
var collection = new Collection();
collection.fetch();
new ViewTwo({ collection: collection });
}
});
And note that the collection.fetch is asynchronous, this.collection will be probably empty in ViewTwo.initialize. See Backbone.js - data not being populated in collection even though fetch is successful for example.
Related
I have a backbone view that has a single click event to update a collection. In my console, I can see the object being updated and the number of models being returned is changing, however the view stays static after the event is fired for the first time and any second attempt to fire it gives me the error TypeError: text is undefined. For reference, I have my script loading at the bottom of the page and the template (using underscore) is above it.
Here's my view
app.MyView = Backbone.View.extend({
el: 'body',
events: {
'click #submit': 'fetchData'
},
initialize: function() {
this.collection = new app.MyCollection();
// On my first attempt, I tried having a the render tied to the sync event when the view initiated, although the sync event doesn't actually occur until something is clicked
// this.collection.on('sync', this.render, this);
},
render: function() {
console.log('rendering');
var schedule = $('#schedule');
var template = _.template($('#times-template').html());
schedule.html(template({ collection: this.collection.toJSON() }));
},
fetchData: function() {
that = this;
stationQuery.station_start = $('#start').val();
stationQuery.station_end = $('#end').val();
var query = stationQuery;
this.collection.fetch({
data: query,
success: function(collection, response) {
console.log('fetching data');
console.log(collection);
// attempt #2 - moving the sync event to the success callback of fetch doesnt allow the view to update a second time either
// collection.on('sync', that.render, that);
},
error: function(collection, response) {
console.log('error on fetch', response);
}
});
},
});
app.myView = new app.MyView;
// Third attempt, this time checking if listenTo will allow the view to update every time the event is fired. It still only works on the first time and fails to render for consecutive clicks, even though the console is showing updated models
app.myView.listenTo(app.myView.collection, 'sync', app.myView.render);
Below is a working code, I just added initial call to fetch data at the end
app.myView.fetchData();
And uncommented your .on('sync', ... inside initialize
this.collection.on('sync', this.render, this);
I tested it with jsbin so you can see what's was wrong for you. As a result I see initial rendering of fetched data and clicking the #submit will re-fetch and re-render view.
app.MyView = Backbone.View.extend({
el: 'body',
events: {
'click #submit': 'fetchData'
},
initialize: function() {
this.collection = new app.MyCollection();
// Here is a binding to each fetch data and on success render view
this.collection.on('sync', this.render, this);
},
render: function() {
console.log('rendering');
var schedule = $('#schedule');
var template = _.template($('#times-template').html());
schedule.html(template({ collection: this.collection.toJSON() }));
},
fetchData: function() {
that = this;
var stationQuery = {};
stationQuery.station_start = $('#start').val();
stationQuery.station_end = $('#end').val();
var query = stationQuery;
this.collection.fetch({
data: query,
success: function(collection, response) {
console.log('fetching data');
},
error: function(collection, response) {
console.log('error on fetch', response);
}
});
},
});
app.myView = new app.MyView;
// Here is first call to fetch data and on success render view
app.myView.fetchData();
I have a JSON file, that I need to parse it into collection and render it to HTML pageand then I need to add a button, that will sort this collection and redraw it on page.
That the code, that I made:
That's the part about model, collection and sorting:
var Profile = Backbone.Model.extend();
var ProfileList = Backbone.Collection.extend({
model: Profile,
url: 'profiles.json',
selectedStrategy: "count",
comparator: function (property){
return selectedStrategy.apply(model.get(property));
},
strategies: {
count: function (model) {return model.get("count");},
name: function (model) {return model.get("name");}
},
changeSort: function (sortProperty) {
this.comparator = this.strategies[sortProperty];
},
initialize: function () {
this.changeSort("count");
},
});
It's the View and the Button:
var ProfileView = Backbone.View.extend({
el: "body",
template: _.template($('#profileTemplate').html()),
Sort: null,
initialize: function() {
this.Sort = new ReSortView();
this.bind('all', this.render());
},
render: function() {
_.each(this.model.models, function(profile){
var profileTemplate = this.template(profile.toJSON());
$(this.el).append(profileTemplate);
}, this);
return this;
},
ReSort: function (){
console.log("111");
this.model.changeSort("name");
},
events: {
"click .Sort": "ReSort",
//"click.NSort": "NSort"
},
});
var ReSortView = Backbone.View.extend({
el: $("#Sort")
});
var AppView = Backbone.View.extend({
el: "body",
initialize: function() {
var profiles = new ProfileList();
var profilesView = new ProfileView({
model: profiles
});
profiles.bind('all', function () {
profilesView.render();
});
profiles.fetch({success: function (model,resp) { console.log(resp);}});
}
});
var App = new AppView();
});
The question is why when I run it, everything seems to be ok, but the sorting does't work, and FireBug saying nothing and Button just writing into the consol.
P.S. I'm new in WEB developing and exactly in JS\Backbone.js
Just changing the comparator:
changeSort: function (sortProperty) {
this.comparator = this.strategies[sortProperty];
}
won't re-sort the collection, the collection has no way of know that the comparator has changed unless you tell it. You need to force the issue by calling sort:
changeSort: function (sortProperty) {
this.comparator = this.strategies[sortProperty];
this.sort();
}
And a few other things while I'm here:
Your initial comparator:
comparator: function (property){
return selectedStrategy.apply(model.get(property));
}
is invalid (unless you have a global selectedStrategy defined somewhere), you should probably just leave it out and let initialize set it up by calling changeSort.
this.bind('all', this.render()); does nothing useful, bind wants a function as the second argument but this.render() calls the render method. You probably don't want a this.bind call there at all and if you do, you'd want to say this.bind('all', this.render).
Views handle the collection option similarly to how the handle the model option in their constructor:
There are several special options that, if passed, will be attached directly to the view: model, collection, el, id, className, tagName and attributes.
so, if your view is collection-based, you'd want to say new View({ collection: ... }) and use this.collection instead of this.model to avoid confusion.
Collections have various Underscore functions built-in so don't say:
_.each(this.model.models, ...
when you can say this instead:
this.collection.each(...
View's have a jQuery wrapped version of el built in so you can use this.$el instead of $(this.el) (which rebuilds the jQuery wrapper each time you call it).
You are calling the changeSort method on the model but that method is on your collection (as it should be)
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 am calling my api which returns a task object via json (example return json below):
[{"pkTaskId":"96","fldName":"Change page to template","fldStatus":"Assigned","fldNotes":"http:\/\/williamsconcepts.com\/ci\/codeigniter\/libraries\/template\/reference.html\r\n\r\n111","fldDateDue":"0000-00-00 00:00:00","fldDateCompleted":"0000-00-00 00:00:00"},{"pkTaskId":"103","fldName":"fix list creation","fldStatus":"Assigned","fldNotes":"for some reason there is an SQL syntax error\r\n\r\nok","fldDateDue":"0000-00-00 00:00:00","fldDateCompleted":"0000-00-00 00:00:00"},{"pkTaskId":"104","fldName":"navicat db admin tool","fldStatus":"Assigned","fldNotes":"Try this out:\nhttp:\/\/www.navicat.com\/download\/download.html","fldDateDue":"0000-00-00 00:00:00","fldDateCompleted":"0000-00-00 00:00:00"},{"pkTaskId":"105","fldName":"Styling dropdowns","fldStatus":"Assigned","fldNotes":"Link:\nhttp:\/\/jqueryui.com\/demos\/autocomplete\/#combobox","fldDateDue":"0000-00-00 00:00:00","fldDateCompleted":"0000-00-00 00:00:00"},{"pkTaskId":"147","fldName":"api create task","fldStatus":"Assigned","fldNotes":"","fldDateDue":"0000-00-00 00:00:00","fldDateCompleted":"0000-00-00 00:00:00"}]
$(function(){
window.Task = Backbone.Model.extend({});
window.TaskList = Backbone.Collection.extend({
model: Task,
url: "http://localhost/tasker/index.php/api/tasks/username/lucasmp"
});
window.tasks = new TaskList();
window.AppView = Backbone.View.extend({
initialize: function() {
tasks.fetch({
success: function() {
console.log(tasks.toJSON());
}
});
}
});
window.App = new AppView;
});
$('#fetch').click(function(){
tasks.fetch({
success: function() {
alert("success");
console.log(tasks.toJSON());
},
error: alert("error")
});
});
I'm having an issue though with my fetch.click occurring twice; Once returns fetch error, then returns fetch success. What could be causing this to be fired twice?
Pls try this
error: function(){alert("error")}
I have problems when i try to fetch data to my Backbone model from the server. You get a response in JSON from from the server which I think looks to be right formatted. Can you see anything wrong with it? It looks like:
[{"id":"1","name":"Fawad Hassan","email":"fawad#test.com"},{"id":"2","name":"Bill
Gates","email":"bill#test.com"},{"id":"3","name":"Steve
Jobs","email":"steve#test.com"},{"id":"4","name":"Naveed
Ahmad","email":"naveed#test.com"},{"id":"5","name":"Mr Zee","email":"zee#test.com"}]
My code for the Backbone project looks like this, and I can't really find the problem there either.
window.AppModel = Backbone.Model.extend({
url: function() {
return 'http://dev.local/ci/index.php/site/userpost';
}
});
window.AppContr = Backbone.Collection.extend({
model: AppModel,
initialize: function() {
this.model = new AppModel;
}
});
window.App = new AppContr({name: "Markus"});
window.AppView = Backbone.View.extend({
el: $("#content"),
initialize: function() {
this.render();
},
render: function() {
console.log(App.model.toJSON());
}
});
App.model.fetch();
window.View = new AppView;
You are doing a fetch on a Model, but returning Collection in response. That is main problem.
Second problem is that you are calling render on AppView totally random, i.e. it does not have anything to do with model or collection. Maybe there would be nothing in model when you render view. You should bind rendering to collection or model with bind. Than whenever you call fetch your view will re-render, which is one of main benefits of Backbone :)
Here comes the code :)
window.Person = Backbone.Model.extend({});
window.Addressbook = Backbone.Collection.extend({
url: 'http://dev.local/ci/index.php/site/userpost',// declare url in collection
model: Person
}
window.Addresses = new AddressBook();
window.AppView = Backbone.View.extend({
el: $('#content'),
initialize: function() {
Addresses.bind('reset', this.render); // bind rendering to Addresses.fetch()
},
render: function(){
console.log(Addresses.toJSON());
}
});
window.appview = new AppView();
Addresses.fetch();