Backbone fetch to get collection - javascript

I have a collection of records that I am trying to log to the console after a fetch. I am not sure why the console is logging a lot of methods instead of the data in the records when I log the collection.
How do I log the data in the collection?
The records in the collection contain this:
{
"color": "yellow",
"date": "March 24, 2014",
"manufacturer": "Ford",
"name": "Mustang"
},
{
"color": "green",
"date": "July 1, 2014",
"manufacturer": "Toyota",
"name": "Corolla"
},
{
"color": "red",
"date": "February 2, 2014",
"manufacturer": "Honda",
"name": "Civic"
}
Here is my Backbone.js code:
var TheModel = Backbone.Model.extend({
defaults: {
'id': 'null',
'color': '',
'date': '',
'name': ''
}
});
var TheCollection = Backbone.Collection.extend({
models: TheModel,
url: 'https://api.mongolab.com/api/1/databases/testdatabase/collections/Content?apiKey=xcdsdfsdczdcdsdfs'
});
var aCollection = new TheCollection();
var TheView = Backbone.View.extend({
initialize: function () {
this.collection = aCollection.fetch();
},
render: function () {
console.log('this.collection');
console.log(this.collection);
return this;
}
})
var aView = new TheView();
aView.render();

this.collection will be an instance of a Backbone collection and that's an object with a bunch of methods and data (most of which you won't care about). If you want to look at just the models, then this.collection.models is for you:
models collection.models
Raw access to the JavaScript array of models inside of the collection.
But that will leave with an array of objects which have a bunch of methods and data that you don't care about.
If you just want the data, then you probably want this.collection.toJSON():
toJSON collection.toJSON([options])
Return an array containing the attributes hash of each model (via toJSON) in the collection.
If you've overridden toJSON anywhere then you can get around that by extracting the model attributes with something like:
console.log(this.collection.map(function(m) { return m.attributes })
or, if you don't want to deal with live references in the console:
console.log(this.collection.map(function(m) { return _(m.attributes).clone() })
That _(m.attributes).clone() call is pretty much what the default toJSON for models does.
Your problems calling methods on this.collection stem from this:
this.collection = aCollection.fetch();
The fetch call returns a jqXHR (the same thing that $.ajax returns), not the collection itself. You want your initialize to look more like this:
initialize: function() {
this.collection = aCollection;
}
Or you could let Backbone hook up the collection for you. Backbone views will attach some options to the view instances for you, collection is one of these so you could leave out initialize and say:
var aView = new TheView({ collection: aCollection });

Related

Fill javascript object dynamically

I'm using responsive calendar in a mvc project.
When setting up the calendar, I need to fill an object called events with my data.
$(".responsive-calendar").responsiveCalendar({
time: '#DateTime.Now.Year.ToString()' + '-' + '#DateTime.Now.Month.ToString()',
events: { //object to fill with my model data
"2013-04-30": { "number": 5, "url": "http://w3widgets.com/responsive-slider" },
"2013-04-26": { "number": 1, "url": "http://w3widgets.com" },
"2013-05-03": { "number": 1 },
"2013-06-12": {},
"2015-06-12": { "number": 1 }
}
});
However this object isn't an array. How to achieve this
Update :
My model is a list of DateEvents :
class DateEvents
{
DateTime Date {get;set;}
int Count {get;set;}
}
When you have an array with data, but must supply it like in your example in the 'events' property, you can create an object like below.
var myEvents = {};
myEvents["2013-04-30"] = { "number": 5, "url": "http://w3widgets.com/responsive-slider" };
If you can do this for one item, you can do this also in a loop (forEach) to get the data from an existing source (array?) and add it to the myEvents object. After completion of 'myEvents', you can set the value of 'events' of the responsiveCalendar to 'myEvents'.

Add data to end of ko.observablearray

I'm trying to add data to the end of an observable array but it's just not working as expected. I bet it is something minor but I just can't get my head around it.
What I am doing:
self.businesses = ko.observableArray();
function Business(business) {
var self = this;
self.BusinessID = ko.observable(business.BusinessID );
self.Type = ko.observable(business.Type);
self.Location = ko.observable(business.Location);
}
/*ajax get array of businesses as follows:
[
{
"$id": "1",
"BusinessID ": 62,
"Type": "Data",
"Location": "Data"
},
{
"$id": "2",
"BusinessID ": 63,
"Type": "Data",
"Location": "Data"
},
{
"$id": "3",
"BusinessID ": 64,
"Type": "Data",
"Location": "Data",
} ]
*/
var mappedBusinesses = $.map(data, function (business) { return new Business(business) });
self.businesses(mappedBusinesses);
This all works as expected and the obersablearray is populated.
However if I go to add another business, it wont work. For example, if I call the ajax that returns this (as newBusiness):
{
"$id": "1",
"BusinessID ": 68,
"Type": "Data",
"Location": "Data"
}
and I do:
self.businesses().push(newBusiness);
It adds to the array as an "Object" not a Business. So I thought I would do:
var bus = $.map(newBusiness, function (business) { return new Business(business) });
self.businesses().push(bus);
But I get the error in the JS console "Uncaught TypeError: Cannot read property 'BusinessID' of null
So I made a new var and added the brackets: [] in and it adds to the observable array but not as a "Business" object but rather as an "Array[1]" object at the end and this doesn't function as per the others. Code as follows:
var newBus = {
BusinessID: newBusiness.BusinessID,
Type: newBusiness.Type,
Location: newBusiness.Location
}
var bus = $.map(newBus, function (business) { return new Business(business) });
self.businesses().push(bus);
As mentioned this adds to the observable array but doesn't actually add as a "business" object but rather as an "array[1]" object.
I bet it's something so basic but just can't get it working!
Argh I knew it would be simple!
It was posting the whole array to the ObservableArray...not just the object.
The fix:
self.businesses.push(newBusiness[0])
Had to add the [0] in to get it to push the actual data into the array, not the object!
Thanks for the answers!
You're evaluating the array with your push:
self.businesses().push(newBusiness);
Observable Arrays have their own array functions, you should just do this (no parens):
self.businesses.push(newBusiness);
See this page: http://knockoutjs.com/documentation/observableArrays.html

Backbone Marionette Composite View Rendering Template

I'm trying to render a list with a Marionette CompositeView. I am not sure why the rendered list just has an item displaying the word result. I was expecting the first item to display Level 1.
Here is a fiddle to my current code: http://jsfiddle.net/16L1hen4/
Here is my JS, template, and data:
JavaScript:
var App = new Backbone.Marionette.Application();
App.addRegions({
mainRegion: '#main'
});
var TreeModel = Backbone.Model.extend({
});
var TreeCollection = Backbone.Collection.extend({
model: TreeModel,
url: 'https://api.mongolab.com/api/1/databases/backbone-tree/collections/tree?apiKey=somekey'
});
var TreeView = Backbone.Marionette.CompositeView.extend({
initialize: function() {
console.log(this.collection);
},
tagName: 'ul',
template: _.template( $('#tree-template').html() )
});
var treeCollection = new TreeCollection();
treeCollection.fetch().done(function () {
var treeView = new TreeView({collection: treeCollection});
App.mainRegion.show(treeView);
});
Template:
<div id="main"></div>
<script type="text/template" id="tree-template">
<li><%- name %></li>
</script>
JSON Data:
{
"_id": {
"$oid": "54adab80e4b0aa674b256836"
},
"name": "Level 1",
"children": [
{
"name": "Child 1 - Level 2",
"children": [
{
"name": "Jon - Level 3"
},
{
"name": "Mary - Level 3"
}
]
},
{
"name": "Child 2 - Level 2",
"children": [
{
"name": "Bill - Level 3"
}
]
}
]
}
Read the marrionnete docs a bit closer - you need a childView defined....
You are using a CompositeView to display a Collection, but you need to define a childView to render the models
var LeafView = Backbone.Marionette.ItemView.extend({
// ...
});
var TreeView = Backbone.Marionette.CollectionView.extend({
childView: LeafView
})
here is an updated fiddle. http://jsfiddle.net/6ok1rptq/
Now the "result" showing in the html, without being familiar with the underscore source, I believe this is caused by the fact that the data given to the template is null, and a quick look at the source of underscore shows that it is using with
http://underscorejs.org/docs/underscore.html#section-148
"If a variable is not specified, place data values in local scope."
Meaning that the template can't find a "name" variable, and will instead look it up in the global scope (window)
Result is just the name of the jsfiddle iframe containing the result of the fiddle
<iframe name="result" ...>
I didn't test this, but I assume that the error lies with the fact that you didn't define a Marionette Itemview on the CompositeView.
The logical structure is to pass the Compositeview a collection as you did in the question, and the models will be rendered in separate itemviews.
In the itemview you can call:
this.model.get("property");
To access the properties from within the view.

Ember.js get nested resources attributes from parent

I have these models:
TravelClient.Tour = DS.Model.extend({
title: DS.attr('string'),
description: DS.attr('string'),
seats: DS.attr('number'),
takenSeats: DS.hasMany('TravelClient.TakenSeat', {embedded:'always'})
TakenSeats: function() {
console.log(this.get('takenSeats').toArray())
}.property('takenSeats')
});
TravelClient.TakenSeat = DS.Model.extend({
tour: DS.belongsTo('TravelClient.Tour'),
number: DS.attr('number')
});
JSON looks like this:
{
"tours": [
{
"id": "5110e8b5a8fefe71e0000197",
"title": "qui deserunt dolores",
"description": "Id velit nihil.",
"seats": 12,
"taken_seats": [
{
"id": "5110e8b5a8fefe71e0000196",
"number": "5"
},
{
"id": "5110e8b5a8feffffe0000196",
"number": "2"
}]
}
But yeah, when I do console.log(this.get('takenSeats').toArray() in Tour model's method, it returns Uncaught TypeError: Cannot call method '_create' of undefined, so, it seems that takenSeats did not load with parent model. What's wrong?
UPDATE
added tour_id to JSON, but now, when I want to use calculated property:
freeSeats: function() {
var seats = this.get('seats');
var takenSeats = this.get('takenSeats');
if (takenSeats) {
return (seats - takenSeats.length);
}
else {
return seats;
}
}.property('seats', 'takenSeats')
takenSeats is undefined.
UPDATE 2:
TravelClient.RESTSerializer = DS.RESTSerializer.extend({
init: function() {
this._super();
this.map('TravelClient.Tour',{
images:{embedded:'always'},
options:{embedded:'always'},
takenSeats:{embedded:'always'}
});
}
});
TravelClient.CUSTOMAdapter = DS.RESTAdapter.extend({
bulkCommit: false,
serializer: TravelClient.RESTSerializer.create(),
url: "http://192.168.1.27:3000",
buildURL: function(record, suffix) {
var s = this._super(record, suffix);
return s + ".json";
}
});
TravelClient.Store = DS.Store.extend({
revision: 11,
adapter: TravelClient.CUSTOMAdapter.create()
});
TravelClient.store = TravelClient.Store.create();
the TakenSeats computed property is perceived as a class because it's capitalized. Next to that embedded loading has to configured differently. Like so: This way the tour object becomes dirty when a takenseat changes.
DS.RESTAdapter.map('TravelClient.Tour', {
takenSeats: { embedded: 'always' },
});
Or: This way the tour doesn't become dirty.
DS.RESTAdapter.map('TravelClient.Tour', {
takenSeats: { embedded: 'load' },
});
Do this before you initialize your Store and Adapter. This will make the computed property unnecessary. You can just do tour.get('takenSeats');
Oh and you don't have to specify that the type is embedded anymore. The id's in the taken_seats array that link back to the tour need to be called tour_id.
{
"tours": [{
"id": "5110e8b5a8fefe71e0000197",
"taken_seats": [{
"id": "5110e8b5a8fefe71e0000196",
"tour_id": "5110e8b5a8fefe71e0000197"
"number": "5"
}]
}]
}
I had a similar problem to this. ember-model doesn't map through nested objects.
Your JSON output currently has all the data nested beneath the tours root.
If you have access to the API, then i suggest trying to remove the root, otherwise look into using your as the main object and then grabbing all the nested objects from there on down.
instead of this:
"tours": [{
"id": "5110e8b5a8fefe71e0000197",
"taken_seats": [{
"id": "5110e8b5a8fefe71e0000196",
"tour_id": "5110e8b5a8fefe71e0000197"
"number": "5"
}]
}]
make it look like this:
[{
"id": "5110e8b5a8fefe71e0000197",
"taken_seats": [{
"id": "5110e8b5a8fefe71e0000196",
"tour_id": "5110e8b5a8fefe71e0000197"
"number": "5"
}]
}]
its possible my syntax is off, but this similar idea worked for me.

merge few json files in to one

After days of searching on google, I finally pushed myself to ask the question over here.
I have few json files.
localhost/feed01.json
localhost/feed02.json
localhost/feed03.json
all the json file structures similar to this one. but random data including random, id, date type etc...
[
{
"id":1,
"date": "12/12/2011",
"type": "Run",
"distance": "3 miles",
"comments": "This was really hard",
"minutes": 36
},
{
"id":2,
"date": "12/11/2011",
"type": "Bike",
"distance": "6 miles",
"comments": "All down hill...felt like nothing",
"minutes": 30
},
{
"id":3,
"date": "12/10/2011",
"type": "Walk",
"distance": "2.5 miles",
"comments": "Shouldn't have taken the dog",
"minutes": 45
}
]
Now I want to merge all this json files to a one and sort by date by default (on initialization). What is the best way to merge and sort using backbone.js using collections?
Backbone lets you override some of backbone.sync through options
you can pass a target url as an option to collection.fetch to override the default url
and passing add: true appends the models to the collection instead of resetting it
which means you can build a collection with
var c = new Backbone.Collection();
c.fetch({
url: ('localhost/feed01.json'),
add: true
});
c.fetch({
url: ('localhost/feed02.json'),
add: true
});
...
To invoke a callback when all fetches have completed, you can build a jQuery deferred object
var files = ['localhost/feed01.json', 'localhost/feed02.json', 'localhost/feed03.json'];
var c=new Backbone.Collection();
var dfds = [], dfd, i, l;
for (i=0, l=files.length; i<l;i++) {
dfd = c.fetch({
url: (files[i]),
add: true
});
dfds.push(dfd);
}
$.when.apply(null, dfds).done(function() {
console.log(c.pluck('id'));
});
To merge data just create a collection and all the arrays with json objects/model representations to this collection. To achieve the ability you can override the fetch method and make it do 3 ajax calls and using jquery deferreds wait until all three of them are resolved and added returned data to collection before you resolve the fetch request.
To alter the sorting logics you need to overwrite the collections comparator method to sort based on your data.
you could specify it as for example as this
comparator: function(model) {
return +new Date(model.get('date'));
}
and it would sort it by date.
If you would for whatever reason specify the comparator after adding the models to the collection you'd need to call the sort method so that the models get sorted according to the rule specified by comparator

Categories

Resources