Fetch backbone collection fail from django-tastypie - backbone.js

I'm using backbone-tastypie from https://github.com/PaulUithol/backbone-tastypie, and I can't fetch a collection data.
Thats my code:
var User = Backbone.Model.extend({
url: '/api/v1/user'
});
var HoraExtra = Backbone.Model.extend({
url: '/api/v1/horasextra/'
});
var HorasExtra = Backbone.Collection.extend({
url: '/api/v1/horasextra/',
model: HoraExtra
});
var Horas = new HorasExtra();
var activeUser = new User();
var HorasExtraView = Backbone.View.extend({
initialize: function() {
_.bindAll(this, "render");
},
render: function() {
var plantilla = Handlebars.compile($("#horas_extra_template").html());
var html = plantilla(Horas);
this.$el.html(html);
console.log(JSON.stringify(Horas));
}
});
var HorasExtraWidget = new HorasExtraView({el: $('#base')});
Horas.fetch({
data: {
"usuario__id": 2,
"hor_com__month": 11
}
});
HorasExtraWidget.render();
And that's the result of console.log(JSON.stringify(Horas):
[]
Many thanks

I would suggest to you that you take tastypie, backbone and build your own simple project in 1-2 days.
Then you will get to know basic issues of coupling those 2 frameworks.
Without that knowledge it is pointless you try out other people's projects and then wonder "what isn't working".
And from my personal experience, both tastypie and javascript are pretty straightforward and are easy to couple.
And I am NOT an expert.
couple of points: try explicitly fetching models with fetch, manipulate fetched model from success callback, and watch your model url's, forward slashes in them etc.
Seems your collection is empty.
See in console if there is a GET call to server.
If not, you're not actually fetching anything from the server.

I used https://github.com/amccloud/backbone-tastypie and then your code should look like this, I guess. Not tested.
var HorasCollection = Backbone.Tastypie.Collection.extend({
url: '/api/v1/horasextra/'
});
var HorasExtraView = Backbone.View.extend({
el: $('#base'),
entries: new HorasCollection(),
render: function() {
var that = this;
this.entries.fetch({
success: function(entries){
console.log("Entries", entries.models);
// var t = _.template(template, {hello: 'world'});
// that.$el.html(t);
},
error: function(model, response){
console.log("Error", response);
}
});
}
});
If you're trying to create a table for your data, try my jquery plugin :)
https://github.com/sspross/tastybackbonepie

Related

Getting view to update on save using Backbone.js

I am learning Backbone.js and as a trial project I am creating a little WordPress user management application. So far my code shows a listing of all WordPress users and it has a form which enables you to add new users to the application.
This all works fine however when you add a new user the listing of users doesn't update automatically, you need to refresh the page to see the new user added which isn't ideal and defeats one of the benefits of Backbone.js!
I have a model for a user and then a collection which compiles all the users. I have a view which outputs the users into a ul and I have a view which renders the form. How do I make my code work so when the .save method is called the view which contains the users updates with the new user? Or is there another way to approach this?
//define the model which sets the defaults for each user
var UserModel = Backbone.Model.extend({
defaults: {
"username": "",
"first_name": "",
"last_name": "",
"email": "",
"password": "",
},
initialize: function(){
},
urlRoot: 'http://localhost/development/wp-json/wp/v2/users'
});
//define the base URL for ajax calls
var baseURL = 'http://localhost/development/wp-json/wp/v2/';
//function to define username and password
function authenticationDetails(){
var user = "myUserName";
var pass = "myPassword";
var token = btoa(user+':'+pass);
return 'Basic ' + token;
}
//add basic authorisation header to all API requests
Backbone.$.ajaxSetup({
headers: {'Authorization':authenticationDetails()}
});
//create a collection which returns the data
var UsersCollection = Backbone.Collection.extend(
{
model: UserModel,
// Url to request when fetch() is called
url: baseURL + 'users?context=edit',
parse: function(response) {
return response;
},
initialize: function(){
}
});
// Define the View
UserView = Backbone.View.extend({
model: UserModel,
initialize: function() {
// create a collection
this.collection = new UsersCollection;
// Fetch the collection and call render() method
var that = this;
this.collection.fetch({
success: function () {
that.render();
}
});
},
// Use an external template
template: _.template($('#UserTemplate').html()),
render: function() {
// Fill the html with the template and the collection
$(this.el).html(this.template({ users: this.collection.toJSON() }));
return this;
},
});
var userListing = new UserView({
// define the el where the view will render
el: $('#user-listing')
});
NewUserFormView = Backbone.View.extend({
initialize: function() {
this.render();
},
// Use an external template
template: _.template($('#NewUserTemplate').html()),
render: function() {
// Fill the html with the template and the collection
$(this.el).html(this.template());
return this;
},
events: {
'click .create-user':'addNewUser'
},
addNewUser: function(){
var newFirstName = $('.first-name').val();
var newLastName = $('.last-name').val();
var newEmail = $('.email').val();
var newPassword = $('.password').val();
var newUserName = newFirstName.toLowerCase();
var myNewUser = new UserModel({username:newUserName,first_name:newFirstName,last_name:newLastName,email:newEmail,password:newPassword});
console.log(myNewUser);
myNewUser.save({}, {
success: function (model, respose, options) {
console.log("The model has been saved to the server");
},
error: function (model, xhr, options) {
console.log("Something went wrong while saving the model");
}
});
}
});
var userForm = new NewUserFormView({
// define the el where the view will render
el: $('#new-user-form')
});
All backbone objects (models, collections, views) throw events, some of which would be relevant to what you want. Models throw change events when their .set methods are used, and Collections throw add or update events... a complete list is here.
Once you know which events are already being thrown, you can listen to them and react. For example, use listenTo - in your view's initialize, you can add:
this.listenTo(this.collection, 'add', this.render);
That will cause your view to rerender whenever a model is added to your collection. You can also cause models, collections, whatever, to throw custom events using trigger from anywhere in the code.
EDIT: For the specific case of getting your user listing view to rerender when a new user is added using the form, here are the steps you can take... In the initialize method of your UserView, after the initialize the collection, add:
this.listenTo(this.collection, 'add', this.render);
Then in your form view... assuming you want to wait until the save is complete on your server, in the addNewUser method, in the success callback of your save, add:
userlisting.collection.add(model);
This will work, since the instance of your UserView is in the global scope. Hope this one works for you!

Manage global Backbone events while writing Qunit unit tests

I am writing unit tests for my Backbone app. Certain tests trigger events, which is causing interference among the different tests.
Here are my tests
test('user setting a company should update the departmentslists url', function() {
var Acme = new Company({ id:274, name: "Acme Solutions" });
var companies = new CompanyList;
var departments = new DepartmentList;
new CompanySelectorView({ el: '#company-input', collection: companies });
events.trigger('userSet:company', Acme);
equal(_.result(departments, 'url'), 'http://'+document.location.host+'/data/companies/274/departments');
});
asyncTest('user setting a company should retrieve that companys departments', function() {
var Acme = new Company({ id:274, name: "Acme Solutions" });
var companies = new CompanyList;
var departments = new DepartmentList;
new CompanySelectorView({ el: '#company-input', collection: companies });
events.trigger('userSet:company', Acme);
events.on('fetched:departments', function(response) {
deepEqual(response, [{id: "8",name: "Accounting"},{id: "1",name: "Client Services"},{id: "470",name: "Systems"},{id: "1187",name: "Managers"}]);
start();
})
});
and the relevant part of my collection:
var DepartmentList = Backbone.Collection.extend({
initialize: function() {
var self = this;
events.on("userSet:company", function(company) {
self.selectedCompany = company;
self.fetch({
success: function(collection, response, options) {
events.trigger("fetched:departments", response);
}
});
});
},
model: Department,
selectedCompany: '',
url: function() {
return 'http://'+document.location.host+'/data/companies/'+this.selectedCompany.id+'/departments';
}
});
What's the solution here? I want to split these two tests out from each other, since they're different things, but I also want the event trigger included in my test.
PS: I'm new to Backbone & unit testing, any criticism is more than welcome.
The simple way to solve this is to use events.once instead of events.on. This way your events are cleaned up after each test.

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 boilerplate: "this.model is undefined"

I'm a backbone newbie, so I'm sort of fumbling on getting an app set up. I'm using the backbone-boilerplate (https://github.com/tbranyen/backbone-boilerplate) and github-viewer (https://github.com/tbranyen/github-viewer) as a reference, though when running I seem to be getting a "this.model is undefined".
Here is my current router.js:
define([
// Application.
"app",
//Modules
"modules/homepage"
],
function (app, Homepage) {
"use strict";
// Defining the application router, you can attach sub routers here.
var Router = Backbone.Router.extend({
initialize: function(){
var collections = {
homepage: new Homepage.Collection()
};
_.extend(this, collections);
app.useLayout("main-frame").setViews({
".homepage": new Homepage.Views.Index(collections)
}).render();
},
routes:{
"":"index"
},
index: function () {
this.reset();
this.homepage.fetch();
},
// Shortcut for building a url.
go: function() {
return this.navigate(_.toArray(arguments).join("/"), true);
},
reset: function() {
// Reset collections to initial state.
if (this.homepage.length) {
this.homepage.reset();
}
// Reset active model.
app.active = false;
}
});
return Router;
}
);
And my homepage.js module:
define([
"app"
],
function(app){
"use strict";
var Homepage = app.module();
Homepage.Model = Backbone.Model.extend({
defaults: function(){
return {
homepage: {}
};
}
});
Homepage.Collection = Backbone.Collection.extend({
model: Homepage.Model,
cache: true,
url: '/app/json/test.json',
initialize: function(models, options){
if (options) {
this.homepage = options.homepage;
}
}
});
Homepage.Views.Index = Backbone.View.extend({
template: "homepage",
el: '#mainContent',
render: function(){
var tmpl = _.template(this.template);
$(this.el).html(tmpl(this.model.toJSON()));
return this;
},
initialize: function(){
this.listenTo(this.options.homepage, {
"reset": function(){
this.render();
},
"fetch": function() {
$(this.el).html("Loading...");
}
});
}
});
return Homepage;
});
Thanks in advance for the help!
Update: After much googling (you should see how many tabs I have open), I think I made a little bit of headway, but still no luck. I updated my router to have the following:
app.useLayout("main-frame").setViews({
".homepage": new Homepage.Views.Index()
}).render();
I made a number of modifications to my homepage.js module to now look like this:
define([
"app",
["localStorage"]
],
function(app){
"use strict";
var Homepage = app.module();
Homepage.Model = Backbone.Model.extend({
defaults: function(){
return {
homepage: {}
};
}
});
Homepage.Collection = Backbone.Collection.extend({
//localStorage: new Backbone.LocalStorage("Homepage.Collection"),
refreshFromServer: function() {
return Backbone.ajaxSync('read', this).done( function(data){
console.log(data);
//save the data somehow?
});
},
model: Homepage.Model,
cache: true,
url: '/app/json/test.json',
initialize: function(options){
if (options) {
this.homepage = options.homepage;
}else{
//this.refreshFromServer();
}
}
});
Homepage.Views.Index = Backbone.View.extend({
template: "homepage",
el: '#mainContent',
initialize: function(){
var self = this;
this.collection = new Homepage.Collection();
this.collection.fetch().done( function(){
self.render();
});
},
render: function(){
var data = this.collection;
if (typeof(data) === "undefined") {
$(this.el).html("Loading...");
} else {
$(this.el).html(_.template(this.template, data.toJSON()));
}
return this;
}
});
return Homepage;
});
As you can see, I have localStorage code but commented out for now because I just want to get everything working first. The ultimate goal is to have an initial call that loads data from a JSON file, then continues afterwards using localStorage. The app will later submit data after the user does a number of interactions with my app.
I am getting the main view to load, though the homepage module isn't populating the #mainContent container in the main view.
I did all of the googling that I could but frustrated that it's just not sinking in for me. Thanks again for looking at this and any feedback is appreciated!
I think your class hierarchy is a bit wonky here. Your instance of Homepage.Collection is actually assigning a homepage property out of options, for instance. Then you pass an instance of Homepage.Collection into Homepage.Views.Index as the homepage option... It's a bit hard to follow.
That said, it seems to me your problem is simply that you aren't supply a model option when you construct your Homepage.Views.Index:
new Homepage.Views.Index(collections)
collections doesn't have a model property, and thus I don't see how this.model.toJSON() later on in the view can have a model to access. Basically, you seem to want Homepage.Views.Index to handle a collection of models, not just one. So you probably need a loop in your render function that goes over this.collection (and you should change your construction of the view to have a collection option instead of homepage option).
If I'm missing something here or I'm unclear it's because of this data model oddness I mentioned earlier. Feel free to clarify how you've got it reasoned out and we can try again :)
This example code you have is a little bit confusing to me, but I think the problem lies in the following two lines of code:
".homepage": new Homepage.Views.Index(collections)
$(this.el).html(tmpl(this.model.toJSON()));
It looks like you pass a collection to the view, but in the view you use this.model, hence the error "this.model is undefined", since it is indeed undefined.
If you aren't in any rush, may I suggest that you start over. It seems you are trying too much too quickly. I see that you have backbone, requirejs (or some other module loader), and the boilerplate, which is a lot to take in for someone new to backbone. Trust me, I know, because I am relatively new, too. Maybe start with some hello world stuff and slowly work your way up. Otherwise, hacking your way through bits of code from various projects can get confusing.

backbone.js each function not receiving the models

I am trying to receive a json data and append to element. all are work fine up to i use the static assignments. while i start to fetch the data from server side, or using fetch nothing work for me.. something wrong with my fech process, any can help me to correct my fetch process and update my code.(instead of simply placing the correct code)..
my JSON(sample):
nameing = [
{name:'student4'},
{name:'student5'},
{name:'student6'}
]
Backbone code:
(function($){
var list = {};
list.model = Backbone.Model.extend({
defaults:{
name:'need the name'
}
});
list.collect = Backbone.Collection.extend({
model:list.model,
url : 'data/names.json', //this is correct path.
initialize:function(){
this.fetch();
}
});
list.view = Backbone.View.extend({
initialize:function(){
this.collection = new list.collect();
this.collection.on("reset", this.render, this);
},
render:function(){
_.each(this.collection.models, function(data){
console.log(data); // i am not get any model here... any one correct my code?
})
}
});
var newView = new list.view();
})(jQuery)
thanks in advance.
Your JSON is not valid. Wiki
[
{"name":"student4"},
{"name":"student5"},
{"name":"student6"}
]

Resources