I am trying to send some JSON data from express to Backbone model.
Update
console.log(response) in parse function of Model prints the values as {"version":"1.0","balance":"80.0"}
console.log(this.model) in the render() function of View gives {}
Server Side Node JS
var express = require('express');
var app = express();
app.listen(3000);
app.get('/getInfo', function(req, res){
//res.setHeader('Content-Type', 'application/json');
res.json({version: "1.0", balance: "80.0"});
});
On Node JS side I have tried following:
app.get('/getInfo', function(req, res){
res.setHeader('Content-Type', 'application/json');
res.send({version: "1.0", balance: "80.0"});
});
Backbone Model
var Bitcoin = Backbone.Model.extend({
url:'http://localhost:3000/getInfo',
parse: function(response) {
console.log(JSON.stringify(response));
return response;
}
});
var info = new Bitcoin ();
info.fetch();
It works fine if I change it to
var info = new Bitcoin ({version: "1.0", balance: "80.0"});
Backbone View
var BitcoinView = Backbone.View.extend({
id:'info',
class:'bitcoin',
template: _.template('<span> <%= balance %> </span>'+
'<span><%= version %></span>'),
render: function() {
console.log(JSON.stringify(this.model));
var attributes = this.model.toJSON();
this.$el.html(this.template(attributes));
}
});
var bitcoinView = new BitcoinView({model: info});
bitcoinView.render();
$('#app').html(bitcoinView.el);
Console
Uncaught ReferenceError: balance is not defined (from View)
XHR finished loading: "http://localhost:3000/getInfo".
{"version":"1.0","balance":"80.0"} (from parse function)
You are likely trying to render a model that has no values for certain attributes. The template of your view expects these attributes to be present. Add defaults to your model to prevent the template method from causing an error.
var Bitcoin = Backbone.Model.extend({
url:'http://localhost:3000/getInfo',
parse: function(response) {
console.log(JSON.stringify(response));
return response;
},
defaults: {
balance: "",
version:""
}
});
In your view you bind the render to the change event of your model. This way the view will rerender when the model changes (the data is fetched).
this.model.bind("change", this.render, this);
Alternativaly, you need to make sure the data is fetched before rendering the view. You can use the callback function of the fetch function.
info.fetch({
success: function(){
//create view here
}
});
Related
I have properly coded a simple REST api and several backbone models. My parent model is called Topic and child model called Questions.
I'm trying to call a get method on the REST api and display the received Topic object to the user in a presentable manner. I am receiving the json (can be seen in the network tab on Chrome), but it is not getting sent to the view correctly.
Model:
var Topic = Backbone.Model.extend({
urlRoot: ROOT + '/topic',
idAttribute: 'topicId',
initialize: function () {
this.questions = new Questions([], {parent: this});
},
toJSON: function () {
var json = Backbone.Model.prototype.toJSON.call(this);
json.questions = this.questions.toJSON();
return json;
}
});
var Topics = Backbone.Collection.extend({
model: Topic,
url: ROOT + 'topic',
parse: function (response) {
return response.results;
}
})
REST URL:
http://localhost/Project/index.php/rest/resource/topic/
Backbone View: This is where I think the error is...(console log below prints an empty object)
var TopicListView = Backbone.View.extend({
el: '.page',
render: function () {
var that = this;
var topics = new Topics();
topics.fetch({
success: function (topics) {
console.log(topics);
var template = _.template($('#topic-list-template').html(), {topics: topics.models});
that.$el.html(template);
}
})
}
});
Using the above functions:
var topic = new Topic();
topic.fetch();
topicListView = new TopicListView();
var Router = Backbone.Router.extend({
routes: {
"": "home"
}
});
var router = new Router;
// render topic list for 'home'
router.on('route:home', function () {
topicListView.render();
});
Edit: Solution: Overriding the parse function in the collection proved to be the error. I wonder why...
The argument topics in your success handler is shadowing the variable topics.
The argument contains the parsed JSON response, not the Backbone Collection. You don't need that, so you can remove the argument.
The reference to topics will now be to your Collection, so topics.models will have the value you expect.
topics.fetch({
success: function () { // argument removed here so `topics` is no longer shadowed
var template = _.template($('#topic-list-template').html(), { topics: topics.models });
that.$el.html(template);
}
})
var elementUrlRoot = api_url + '/elements';
var elementModel = Backbone.Model.extend({
'idAttribute': '_id' //mongoDB
, 'urlRoot': elementUrlRoot
, defaults: {
"signature": "",
"group": 0
}//defaults
});
var elementCollection = Backbone.Collection.extend({
model: elementModel
, 'url': elementUrlRoot
});
var testmodel = new elementModel({DOM_id: 111});
testmodel.save({signature: "test"},
{
error: function (model, response, options) {
console.log('test model save error:', response);
},
success: function () {
console.log('test model save success');
}
}
);
My backbone model is not saved to the server when I update it.
I have set the urlRoot attribute of the Model (which according to the documentation should not be necessary). But there are still no HTTP requests being issued.
Update:
I have added a success method in the callback. It is being executed.
But there are no requests being sent to the server.
Update:
I found the error. I had added this code to save a whole collection.
Backbone.Collection.prototype.syncCollection = function (options) {
console.log('syncing the collection');
Backbone.sync("create", this, options);
};
It worked and I was able to save collections with it.
But it seems to have caused a problem with saving individual models. Requests are issued when I removed it.
Your urlRoot is needed because your model is not part of a collection.
Try unquoting your urlRoot attribute on the left side of the assignment
http://backbonejs.org/#Model-urlRoot
The issue here is, that i don't know how to pass some scope.data to expressjs when using ngResource, so then it can be used with express route to insert something to DB.
ExpressJS REST
router.route('/Data')
.get(function(req,res){
var username = req.username;
var collection = db.collection('users');
collection.find({username:username}).toArray(function (err, doc){
res.send(doc[0].pets);
});
})
.post(function(req,res){
!!//I would like to use some data from angular here//!!
var name = req.body.name;
var surname = req.bodysurname;
collection.update({username: username}, {
$push: {
"details": {
name: name,
surname: surname
}
}
}, function (err, result) {
if (err) throw err;
});
});
Angular Factory
(function() {
'use strict';
angular
.module('App')
.factory('Factory', function ($resource) {
return $resource("/Data",{},
{ get:{ method:"GET",
cache:true,
isArray:true},
save:{ method:"POST",
cache:true,
isArray:false
}});
});
})();
Controller.js
This one works fine i use this function with ng-click()
$scope.load = function(){
Factory.get(function (data){
$scope.data = data;
});
};
With this one i have problem i have ng-models name and surname in view and i would like to send them to server so it can be used in REST route as req.body.name and req.body.surname.
$scope.AddData = function(){
Factory.save()
});
};
I think that data should be passed here in this function AddData, however i haven't succeeded yet.
So i tried as Jesus said but without results?
$scope.AddData = function(){
Factory.save($scope.name) //I tried ({name:$scope.name}) too
});
};
After advices from Jesús Quintana I checked the details of method POST and it occurred that everything was all right on angular side i was missing extension of body parser on server Express side
app.use(bodyParser.json())
So now it looks like this
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
NgResource works like a class with private and public methods:
For example Factory.save() is a public method and you must pass the data to be stored in the server, for example:
$scope.AddData = function(factoryData){
Factory.save(factoryData);
});
};
But also have a private method and the above example is the same to this:
$scope.AddData = function(factoryData){
var factory = new Factory(factoryData);
factory.$save(); // Is the same method but is private because factory is a instance of the factory
});
};
Both example are valid methods but must be used of differents ways.
EDIT
I create this little plunkr to see the network request: http://plnkr.co/edit/1bdblyrsW0jr7rXIAVNn?p=
I seem to be fetching correctly from the server using backbone. A GET request is made to a MongDB collection, via the Node.js server code here:
exports.getTeams = function(req,res,next){
var system_db = req.system_db;
var user_id = req.mainUser._id;
var teams = teamModel.getNewTeam(system_db,user_id);
teams.find({}, function (err, items) {
res.json(items);
});
};
I am fetching from Backbone like so:
var teamCollection = new TeamCollection([]);
teamCollection.url = '/api/teams';
teamCollection.fetch(
{success:function(){
console.log('teamCollection length:',teamCollection.length);
console.log('teamCollection[0]:',teamCollection[0]);
}}
);
using this model and collection:
var Team = Backbone.Model.extend({
idAttribute: "_id",
urlRoot: 'http://localhost:3000/api/teams'
});
var TeamCollection = Backbone.Collection.extend({
model: Team,
initialize: function() {
this.bind('add', this.onModelAdded, this);
this.bind('remove', this.onModelRemoved, this);
this.bind("change", this.onModelChanged, this);
},
/* parse: function(data) {
//return JSON.stringify(data).objects;
//return JSON.parse(data).objects;
return data.objects;
},*/
onModelAdded: function(model, collection, options) {
console.log("added, options:", options);
},
onModelRemoved: function (model, collection, options) {
console.log("removed, options:", options);
},
onModelChanged: function (model, collection, options) {
console.log('Collection has changed.');
},
comparator: function (model) {
return model.get("_id");
}
});
the problem is that the logging statements above log the following in the browser console:
It says I am sending 4 items from the server to the Backbone client, but the first one is undefined. How could this be?
A Backbone.Collection is not an array-like object : it has a length attribute representing the number of models but you can't access individual models via indexes, hence
console.log(teamCollection[0]); //undefined
To get a model at a given position, use collection.at .Try
console.log(teamCollection.at(0));
And a demo of those behaviors http://jsfiddle.net/nikoshr/013gjpny/
I am trying to load data from an API into a view. However the data doesn't turn up in my view.
I tried getting the collection information in de router, as well as in the model.
However the date won't even console.log the data. Let alone that I can load the data into the view.
I am using require to load the JavaScript files. Can you have a look and see what I am doing wrong here?
I do see this console.log:
console.log("People Collection is initialized");
And I can also see the page loaded and the json. But not the console.log of the data in the url function... In fact I get this error in the console:
Error: A "url" property or function must be specified
In the Backbone Router:
var OF = OF || {};
OF.AdminRouter = Backbone.Router.extend({
routes: {
"users": "goToUsers",
"users/*other": "goToUsers"
},
goToUsers: function() {
require(['./models/users', './views/users_view', './views/menu_view', './collections/user_collection'], function(UsersMdl, UsersView, MenuView, UsersCollection) {
OF.usersView = new OF.UsersView;
OF.usersView.render();
});
}
});
The Collection:
var OF = OF || {};
OF.UsersCollection = Backbone.Collection.extend({
initialize: function() {
console.log("People Collection is initialized");
},
url: function() {
var that = this;
var sendObj = {
"admin": OF.login.attributes.admin,
"session": OF.login.attributes.session
};
$.ajax({
url: 'php/api/users/',
type: "POST",
dataType: "json",
data: sendObj,
success: function(data) {
console.log(data);
},
error: function(data) {
console.log("ERR: " + data);
}
});
},
model: OF.UsersMdl
});
The Model:
var OF = OF || {};
OF.UsersMdl = Backbone.Model.extend({
default: {
username: '',
homefoldersize: '',
email: ''
},
initialize: function(){
//on change functions can be done here
OF.usersCollection = new OF.UsersCollection();
OF.usersCollection.fetch();
},
result: {
success: false,
message: ''
},
validate: function(att) {
}
});
The View:
var OF = OF || {};
OF.UsersView = Backbone.View.extend({
el: '#content',
remove: function() {
this.$el.empty();
this.stopListening();
return this;
},
initialize: function() {
//set the new address variable.
OF.usersMdl = OF.usersMdl || new OF.UsersMdl();
},
render: function() {
/*
//first check if you are allowed to see this page
if (!OF.login || !OF.login.isValid()) {
OF.router.navigate('login', {trigger: true});
return;
}
*/
//save this in that
var that = this;
//when importing of login page (and fill it with info) is done
$.when(OF.template.get('users-usersField', function(data) {
var htmlSource = $(data).html();
var template = Handlebars.compile(htmlSource);
var compiled = template(OF.usersMdl.attributes);
//now place the page
that.$el.html(compiled);
//then start the menu
})).then(function(){
setTimeout(function(){
OF.menuView = new OF.MenuView;
OF.menuView.render();
}, 100);
});
$('#logout').show();
}
});
Thanks.
It seems to call the initialize of the collection twice and then continues to call the json function.
In your model's initialization you have
OF.usersCollection = new OF.UsersCollection();
OF.usersCollection.fetch();
But when you fetch your collection, it's going to initialize models for every result it gets back ... which will then trigger fresh collection fetches.
You don't need to create collections for your models inside your models, especially if the model is being created by the collection. Whenever you add a model to a collection (including when the collection creates the model after a fetch) the collection will associate itself with the model.
The general order of things should be:
You define a url function on your collection which returns the URL where you can get the (raw JSON) models of that collection.
You instantiate that collection, and then call fetch on the instance
The collection makes an AJAX call (which you can affect by overriding fetch or sync) and gets back the raw JSON for all of the models.
The collection instantiates new models for each result it gets back; those models are automatically added to the collection, and their .collection is automatically set to the collection.
Once OF.usersCollection.fetch().done(function() {... you can have your views start doing things, as your collection should now be all set.