I'm trying to get some data from the node server, which works fine, but when I try to GET data via the Backbone fetch (or sync), the request fails. I noticed that, for some reason, actual request is wrong: 'GET http://localhost:3000/socket.io/1/' where it should be 'GET http://localhost:3000/cars', since '/cars' is the value of the URL field that Backbone uses by convention for these operations. These are the relevant modules:
var Backbone = require("backbone");
var Car = require('models/car');
var Cars = Backbone.Collection.extend ({
model: Car,
url: '/cars',
// Unselect all Car Cards
resetSelected: function() {
for (var i=1; i<=this.length; ++i) {
var carcard=this.get(i);
carcard.set({"selected": false});
console.log(carcard.attributes.name + ' unselected');
}
},
// Select a specific model from the collection
selectByID: function(id) {
this.resetSelected();
var carcard = this.get(id);
carcard.set({"selected": true});
console.log(carcard.attributes.name + " selected");
return carcard.attributes.id;
}
});
module.exports = Cars;
And a model:
var Backbone = require("backbone");
var Car = Backbone.Model.extend({
defaults: {
year: 2011,
brand: "Brand",
model: "Model",
name: "Car Name",
pictureFle: "img/car.jpg",
kmTraveled: 0,
litresSpent: 0,
selected: false
},
});
module.exports = Car;
I tried to populate the collection like this:
var cars = new Cars();
cars.fetch();
but, as I explained, failed. Any ideas what the problem could be?
Related
I am trying to search my cloudant DB from my node.js back end using the search API, then display what is returned on a webpage.
I am using an angular MVC and I am a bit new to it, so sorry if this is a bit simple.
I have a html front end which contains this -
<button class="btn btn-default" ng-click="getDataandHeadings()"></button>
<table>
<tr ng-repeat="doc in myData">
<td>{{doc.doc.TerritoryTypeName}}</td>
<td>{{doc.doc.TeamOwnerSerialNo}}</td>
<td>{{doc.doc.TeamOwnerName}}</td>
<td>{{doc.doc.TeamOwnerNotesID}}</td>
<td>{{doc.doc.SellerSerialNo}}</td>
<td>{{doc.doc.SellerName}}</td>
<td>{{doc.doc.SellerNotesID}}</td>
</tr>
</table>
A controller -
'use strict';
var app = angular.module('app', false);
app.controller('greetingscontroller', function($scope, $http) {
$scope.getDataandHeadings = function() {
$http.get('/retrieve').success(function(data) {
$scope.myData = data;
}).error(function(err) {
//handle the error
});
};
});
Back end Node application -
var express = require('express');
var secrets = require('./secrets.js');
var Cloudant = require('cloudant');
var csv = require('csv-parser');
var fs = require('fs');
var cloudant = Cloudant({account: secrets.cloudantUser, password:secrets.cloudantPassword});
var cfenv = require('cfenv');
var app = express();
app.use(express.static(__dirname + '/public'));
var arr = [];
var headings = [];
var bufferString;
var i;
var db = cloudant.db.use('my_sample_db');
app.get('/retrieve', function(req, res) {
var db = cloudant.db.use('my_sample_db');
// db.get('_all_docs', {include_docs: true}, function(err, body) {
// if(!err) {
// console.log(body.rows.length +' '+body.total_rows);
// for (var i = 0; i < body.rows.length; i++) {
// console.log('Document id: %s', body.rows[i].id);
// }
//
// res.status(200).json(body.rows);
// }
// if(err) {
// console.log(err);
// }
// });
db.search('app', 'mySearch', {q:'TerritoryTypeName:NHS'}, function(err, body) {
console.log(body.rows.length +' '+body.total_rows);
for (var i = 0; i < body.rows.length; i++) {
console.log('Document id: %s', body.rows[i].id);
}
res.status(200).json(body.rows);
if (err) {
console.log(err);
}
});
});
var appEnv = cfenv.getAppEnv();
app.listen(appEnv.port, '0.0.0.0', function() {
console.log("server starting on " + appEnv.url);
});
As you can see, in my back end I am making 2 similar API calls to the DB - db.get and db.search - (one is commented out). The one the is commented out simply returns everything that is in the database, and the second one does a query using my key:value pair - TerritoryTypeName:NHS.
The commented out call works how I want it to, and when I click the button on the front end, the data in the DB is displayed.
The other API call seems to not be working, as nothing is displayed on the front end when I click. I don't understand why one of them works and the other doesn't when they are so similar?
However, I think the db.search function is working correctly, as the console.log line works well and displays onto the terminal the expected returned entries from the DB based on the query. I think the problem may lie either in the html, or the Controller? If anyone can lend any suggestions that would be great! Here is a copy of the terminal:
server starting on http://localhost:6001
2 2
Document id: 4092be64a755bc04432a5187cdf70006
Document id: 4092be64a755bc04432a5187cdf6f9c2
{ total_rows: 2,
bookmark: 'g1AAAACqeJzLYWBgYMpgTmHQSUlKzi9KdUhJMtFLyilNzc2s0C3N1i0uScxLSSxKMdRLzskvTUnMK9HLSy3JAenKYwGSDAuA1P____dnZTC52R9eXAUSS2RANdGcJBMfQEz8DzbxWc1JsImMWQAEtDc6',
rows:
[ { id: '4092be64a755bc04432a5187cdf70006',
order: [Object],
fields: [Object] },
{ id: '4092be64a755bc04432a5187cdf6f9c2',
order: [Object],
fields: [Object] } ] }
Try adding include_docs to your search query:
{q:'TerritoryTypeName:NHS', include_docs:true}
It looks like the data you are returning does not include the doc objects, which you are trying to access in your HTML:
<tr ng-repeat="doc in myData">
<td>{{doc.doc.TerritoryTypeName}}</td>
<td>{{doc.doc.TeamOwnerSerialNo}}</td>
...
</tr>
It looks like you are expecting the data returned from the server to look like this:
rows : [
{
doc : {
TerritoryTypeName: "xxx",
TeamOwnerSerialNo: "xxx",
...
},
{
doc : {
TerritoryTypeName: "yyy",
TeamOwnerSerialNo: "yyy",
...
}
]
However, it is being returned from the server like this:
rows: [
{
id: '4092be64a755bc04432a5187cdf70006',
order: [Object],
fields: [Object]
},
{
id: '4092be64a755bc04432a5187cdf6f9c2',
order: [Object],
fields: [Object]
}
]
I have a Backbone App with 3 model which have nested collections:
Models Structure:
Layout extends Backbone.Model
-> sections: new Sections extends Backbone.Collection
Section extends Backbone.Model
-> rows: new Rows extends Backbone.Collection
Now if I have two section model in layout and I go and add row model to one of the Section.rows collection, it is adding it to both sections.
BTW, I am adding it from a view on an event.
Thanks in Advance.
got a workaround. I could reproduce your workflow by adding defaults property to my models.
like this:
var Section = Backbone.Model.extend({
defaults: {
rows: new Rows
}
});
var Layout = Backbone.Model.extend({
defaults: {
sections: new Sections
}
});
then really, if i add new row to rows to one of my section its appear to adding to all sections rows collection. So i do this (rude example):
var Row = Backbone.Model.extend({
defaults: {
rowData: 0
}
});
var Rows = Backbone.Collection.extend({
model: Row
});
var Section = Backbone.Model.extend({
//defaults: {
// rows: new Rows
//}
});
var Sections = Backbone.Collection.extend({
model: Section
});
var Layout = Backbone.Model.extend({
//defaults: {
// sections: new Sections
//}
});
var LayoutView = Backbone.View.extend({
});
var lView = new LayoutView({ model: new Layout });
lView.model.set('sections',new Sections());
var sections = lView.model.get('sections');
sections.add({id: 1, name: 's1',rows: new Rows() });
sections.add({id: 2, name: 's2',rows: new Rows() })
var rows = sections.get(1).get('rows');
rows.add({id:'r1',rowsData: 10});
console.log(lView.model.toJSON());
#aleha you are right the issue is of the default attribute settings in Model. As they share the same memoery space(javascript: pass by reference not by value ).
So what I did is in the initialize function
initialize: function() {
this.set( 'rows', new Rows() );
}
So, no need to do it like you are doing above:
sections.add({id: 1, name: 's1',rows: new Rows() });
Hence resolved and automate :)
Thanks for the help though.
im learning BackboneJs using the documentation and a book called "Beginning backbone".
But I have been stuck at the sorting collections part for hours.
Also tried to research but I find the results complicated =/
I know I have to use the comparator, as shown in the documentation but I don't understand how to apply it to the current code syntax-wise
http://backbonejs.org/#Collection-comparator
var Book = Backbone.Model.extend({
defaults:
{
title: "default title",
author: "default author",
pages: 20
},
comparator: function(item)
{
//sort by title
return item.get('title');
}
});
var book1 = new Book({ title:"Book of wonders",author:"author1",pages:1 });
var book2 = new Book({ title:"Zelda",author:"author2",pages:2 });
var book3 = new Book({ title: "Drake's out", author: "author3",pages:3});
var book4 = new Book({ title: "AutoCad",author: "author4",pages: 4});
var Library = Backbone.Collection.extend({
model: Book
});
var library = new Library([book1,book2]);
library.add([book3,book4]);
library.forEach(function(model){
console.log('Book is called '+model.get("title"));
});
console.log('Library contains '+library.length+' books');
This is a working solution, it will sort everything by title.
to sort it by anything else just change the parameter of the get function inside the comparator function
var Book = Backbone.Model.extend({
defaults:
{
title: "default title",
author: "default author",
pages: 20
}
});
var book1 = new Book({ title:"Book of wonders",author:"author1",pages:1 });
var book2 = new Book({ title:"Zelda",author:"author2",pages:2 });
var book3 = new Book({ title: "Drake's out", author: "author3",pages:3});
var book4 = new Book({ title: "AutoCad",author: "author4",pages: 4});
var Library = Backbone.Collection.extend({
model: Book,
initialize: function()
{
console.log("new collection");
},
comparator: function(a,b)
{
//sort by title
return a.get('title') < b.get('title') ? -1 : 1;
}
});
var library = new Library([book1,book2]);
library.add([book3,book4]);
library.sort();
library.forEach(function(model){
console.log('Book is called '+model.get("title"));
});
console.log('Library contains '+library.length+' books');
in my simple backbone application, I am trying to update a model and every time it send a put request instead of post.
Well, this is my model named categoryModel
define(['Backbone'], function (Backbone) {
var CategoryModel = Backbone.Model.extend({
defaults: {
ID: '',
Name: 'Empty',
TagID: '0',
GID: '0'
},
idAttribute: "ID",
initialize: function () {
if (!this.get('Name')) {
this.set({ 'Name': this.defaults.Name });
}
}
});
return CategoryModel;
});
this is the collection
define(['Backbone','../../models/categories/categoryModel'], function (Backbone, categoryModel) {
var CategoryCollection = Backbone.Collection.extend({
url: '/parentcategory/Actions',
model: categoryModel
});
return new CategoryCollection;
});
here are my methods in the view
on a keychange event
createNewItem: function (e) {
var $this = $(e.currentTarget);
$('#selectedCategoryName').html($this.val());
//it creates a new model
globals.NewCategory = new CategoryModel({ Name: $this.val() });
}
on handleDrop event
handleDropEvent: function (event, ui) {
var draggable = ui.draggable;
//check if name has set
if (!globals.NewCategory) {
alert("Please write a category name");
$('#createNewCategory').focus();
return;
}
//get itemID
var itemID = draggable.attr("id").split('_')[1];
var itemDesc = draggable.attr("id").split('_')[0];
//check items category
if (itemDesc == "Tag") {
//check if tagID already exists
if (globals.NewCategory.TagID) {
alert("you have already specify a tag from this category");
return;
}
globals.NewCategory.set("TagID", itemID);
} else if (itemDesc == "gTag") {
if (globals.NewCategory.GID) {
alert("you have already specify a tag from this category");
return;
}
globals.NewCategory.set("GID", itemID);
}
categoriesCollection.create(globals.NewCategory, {
silent: true,
wait: true,
success: function (model, response) {
model.set("ID", response);
alert(model.id);
}
});
}
The categoriesCollection.create is called twice. Firstly for setting the TagID (on a success request it gets an ID ) and secondly for setting the GID.
Since the ID has been set, shouldn't had sent a POST request instead of PUT on the second call?
What am I doing wrong?
Thanks
The standard behaviour is to send a POST if the model is new ( doesn't have an ID attributed ) and send a PUT if the model id is set.
In your case it's working as designed, if you want it to use POST to send UPDATES you have to override Backbone.sync to work as you need, but I think it's easier for you to make your backend RESTful and create a PUT listener controller method for updates.
Another thing, if I got it right you are using create() to update models in your collection, I would advise you not to do that and instead use the save() directly in the model you want to update, the code will be a lot more readable.
Cheers.
I have a defined model and a collection:
var Box = Backbone.Model.extend({
defaults: {
x: 0,
y: 0,
w: 1,
h: 1,
color: "black"
}
});
var Boxes = Backbone.Collection.extend({
model: Box
});
When the collection is populated with the models, I need a new Boxes collection made out of Box models that have a specific color attribute contained in the complete collection, I do it this way:
var sorted = boxes.groupBy(function(box) {
return box.get("color");
});
var red_boxes = _.first(_.values(_.pick(sorted, "red")));
var red_collection = new Boxes;
red_boxes.each(function(box){
red_collection.add(box);
});
console.log(red_collection);
This works, but I find it a bit complicated and unefficient. Is there a way of doing this same thing in a more simple way?
Here is the code I described: http://jsfiddle.net/HB88W/1/
I like returning a new instance of the collection. This makes these filtering methods chainable (boxes.byColor("red").bySize("L"), for example).
var Boxes = Backbone.Collection.extend({
model: Box,
byColor: function (color) {
filtered = this.filter(function (box) {
return box.get("color") === color;
});
return new Boxes(filtered);
}
});
var red_boxes = boxes.byColor("red")
See http://backbonejs.org/#Collection-where
var red_boxes = boxes.where({color: "red"});
var red_collection = new Boxes(red_boxes);