Backbone 101 - Using JSON data with backbone - javascript

I'm in the beginning stages of moving my application to the backbone framework.
I have some data that comes in as json from an ajax call
{f1:"f1_value", f2:"f2_value", f3:"f3_value"},
{f1:"f1_value", f2:"f2_value", f3:"f3_value"},
{f1:"f1_value", f2:"f2_value", f3:"f3_value"},
This data set always has 3 columns but may be as long as needed for each set as far as rows goes.
It is used to populate a div after processing it client side into HTML, which correlatively may extend down as far as needed. I was planning on this data chunk representing one view in the framework.
<div id = "data_hold"></div>
How do I match this up to the framework:
var ModelTest,
CollectionTest,
ViewTest;
ModelTest = Backbone.Model.extend({
});
CollectionTest = Backbone.Collection.extend({
model: ModelTest
}
ViewTest = Backbone.View.extend({
});

Backbone 101:
var ModelTest,
CollectionTest,
ViewTest;
ModelTest = Backbone.Model.extend({ });
// associate your collection with a URL. This is static here; it can be
// passed in as an option using the Collection's initialize function()
// instead.
CollectionTest = Backbone.Collection.extend({
model: ModelTest,
url: 'http://localhost/my_json_source'
});
ViewTest = Backbone.View.extend({
// Have a target to render into. This can be an existing element, as
// here, or it can be dynamically generated and attached to the DOM
// programattically.
el: $('#data_hold'),
// specify than when the collection is updated, call the local render
// method.
initialize: function(options) {
this.collection.bind('reset', _.bind(this.render, this));
},
// Empty the element, then append subsequent rows of the collection
// to it as paragraphs. The '_this = this' idiom allows us to access
// the outside context (the View's context), since the each() call
// will create a new inner context.
render: function() {
var _this = this;
this.$el.html('');
this.collection.each(function(l) {
_this.$el.append('<p>' + l.get('f2') + '</p>');
});
}
});
// initialize the collection and view, then fetch the collection, which
// will trigger the render after the collection has been updated.
$(function() {
ct = new CollectionTest();
vt = new ViewTest({collection: ct});
ct.fetch();
});

Related

How to get an url with collection and model ids

I made a website to create some maps. So, all my maps have an id, and my map has some elements with ids.
So I create a collection Map with an id and its model is my map object :
app.collections.mapDetailsCollection = Backbone.Collection.extend({
model: app.models.mapDetailsModel,
initialize: function(options) {
this.id = options.id;
},
url: function () {
return this.absURL + '/api/maps/' + this.id;
},
parse: function(response){
return response.features;
},
toGeoJSON: function(){
var features = [];
this.models.forEach(function(model){
var feature = model.toGeoJSON();
if (! _.isEmpty(feature.geometry)) {
features.push(feature);
}
});
return {
'type': 'FeatureCollection',
'features': features
};
}
});
But my model have an id too. I don't know for a model how to return url with a collection id.
I need to return /api/maps/id_collection/id_model.
When a model is added to a collection, it receives a reference to the collection. So each model has a collection property this.collection set if it's in a collection.
From the Backbone model constructor documentation (emphasis mine):
If you pass a {collection: ...} as the options, the model gains a
collection property that will be used to indicate which collection the
model belongs to, and is used to help compute the model's url. The
model.collection property is normally created automatically when you
first add a model to a collection. Note that the reverse is not true,
as passing this option to the constructor will not automatically add
the model to the collection. Useful, sometimes.
Then, you could use that to build the full url of a model:
var app.models.mapDetailsModel = Backbone.Model.extend({
urlRoot: function() {
// Backbone adds the model id automatically
return this.collection.url() + '/my-model/';
}
// ...snip...
});
Note that the default url for a model is what you want.
Generates URLs of the form: [collection.url]/[id] by default, but
you may override by specifying an explicit urlRoot if the model's
collection shouldn't be taken into account.

backbonejs - accessing collection in view

backbonejs and oop js newbie here. i am trying to bind my collection to a view, when i go to my console, i only get this
this is a collection: [object Object]
is there something am i missing here?
var root = "http://jsonplaceholder.typicode.com";
var Post = Backbone.Model.extend({});
var Posts = Backbone.Collection.extend({
model: Post,
url: root + "/posts",
parse: function(response) {
return response;
}
});
var posts = new Posts();
posts.fetch();
// View wrapper to render view child items
var PostsView = Backbone.View.extend({
collection: new Posts(),
initialize: function() {
console.log('this is a collection: ' + this.collection);
},
render: function() {
// STEPS:
// filter through all items in a collection
// for each item, create a new PostView
// append to root element, ex. ul
}
});
var postsview = new PostsView();
When you do 'this is a collection: ' + this.collection you're forcing the collection to be a string, and javascript sees an object and turns it into your [object Object].
The correct way to show a collection as a string is to just its .toJSON() function and do console.log('this is a collection: ' + JSON.stringify(this.collection.toJSON())); but since most devtools are capable of properly showing an object natively you're better off using
console.log('this is a collection: ', this.collection);
That should give you a nice interactive representation of the collection in your console, at least on Chrome.

BackboneJS : the model is fetched, but toJSON() and get() methods don't work [duplicate]

This question already has answers here:
View's collection only working in console.log
(3 answers)
Closed 7 years ago.
Still learning Backbone, here is my code
const API_URL = 'http://api.brewerydb.com/v2';
const API_KEY = '********************************';
var Categories = Backbone.Model.extend({
url: API_URL + '/categories/?key=' + API_KEY
});
var CategoriesView = Backbone.View.extend({
tagName: 'ul',
id: 'categories',
render: function()
{
var html = '';
for (var i = 0; i < this.model.get('data').length; i++)
{
html += '<li>' + this.model.get('data')[i].name + '</li>';
}
this.$el.html(html);
}
});
var categories = new Categories();
categories.fetch();
console.log(categories.toJSON());
My console.log(categories.toJSON()); returns an empty object.
But when I do a console.log(categories);, the attributes property contains the data. And if I try a get on any of those attributes, it also doesn't work (undefined).
Since JavaScript executes asynchronously, your console.log(categories.toJSON()); executes before categories.fetch(); has finished execution.
This can be solved in a lot of different ways, but here are two common ones:
The first is using Backbone.Model's fetch() method, which accepts success and error callbacks:
categories.fetch({
success: function() {
console.log(categories.toJSON());
}
});
Here is an example of this (see fiddle), which does the same thing (but pulls data from GitHub API instead as an example, since they have a public API).
The second way is a bit more complicated, but is a more common pattern in Backbone apps. This way involves adding an initialize function in your CategoriesView, and adding an event listener for Backbone's sync event. That event will fire a callback, after which you can execute your render function.
It also requires you to pass in a reference to your model when you call your new view constructor (see the very last line in the code below to see how this is done.)
This might seem a bit confusing, so I made another fiddle which shows this in action, pulling data from the GitHub API again.
GitHub API example aside, your code would now look like this:
const API_URL = 'http://api.brewerydb.com/v2';
const API_KEY = '********************************';
var Categories = Backbone.Model.extend({
url: API_URL + '/categories/?key=' + API_KEY
});
var CategoriesView = Backbone.View.extend({
tagName: 'ul',
id: 'categories',
initialize: function()
{
this.model.fetch();
this.listenTo(this.model, 'sync', this.render);
},
render: function()
{
// Your data will show up
// in this case
console.log(categories.toJSON());
var html = '';
for (var i = 0; i < this.model.get('data').length; i++)
{
html += '<li>' + this.model.get('data')[i].name + '</li>';
}
this.$el.html(html);
}
});
var categories = new Categories();
// Create a new instance of the view,
// and pass in the model you just
// created
var categoriesView = new CategoriesView({ model: categories });
One weird thing you might notice is that in your view, you listenTo the sync event, not the fetch event. This is because as of Backbone 1.0, model.fetch() actually triggers the sync event (source). I've always thought this was weird, so I thought I'd throw that in here :)

populate collection on Backbone

Tutorial.Views.Layout = Backbone.Layout.extend({
model: Tutorial.Model,
});
initialize: function () {
_.bindAll(this);
this.model = new Tutorial.Model({id : $("#data").data('user') });
this.collection = new Tutorial.Collection();
var success1 = function(){
console.log('OK');
};
this.collection.fetch({success : success1});
},
showTuto: function(){
step = this.collection.first(1);
console.log(step);
},
My problem is my collection is empty. My model has 4 items, but i see none of them .
Thanks in advance
We need to see some more code to make a closer suggestion, but hopefully this explanation will help you out. You should pass your model directly to the collection, OR deal with it in the fetch.
//simplified view
YourView = Backbone.View.extend({
initialize : function(){
var testModel = new Tutorial.Model({id : $("#data").data('user') });
this.collection = new Tutorial.Collection();
this.collection.add(testModel);
}
});
In this case, you would be adding that model directly to your collection. If you want to asyncronously call and get data from the fetch and then pass a callback, you could do something like this:
//simplified view
YourView = Backbone.View.extend({
initialize : function(){
this.collection = new Tutorial.Collection();
this.collection.fetch(function(){
console.log('okay');
});
}
});
Tutorial.Collection = Backbone.Collection.extend({
fetch : function(callback){
// grab a ref to your collection
var thisCollection = this;
// call your data function which has the ajax call
getYourDataFunction(function(data){
// if your data returned is an array of objects
thisCollection.add(data);
if (typeof callback === "function"){
//if you included a callback, call it with a reference to the collection
callback.call(thisCollection);
}
});
});
});

How do I render multiple records out to a html table with Backbone.js ?

var ContractModel = Backbone.Model.extend({
url: "${g.createLink(controller:'waiverContract', action:'index')}"
})
var contract = new ContractModel({});
contract.fetch();
var contracts = new Backbone.Collection.extend({
model: contract
});
var ContractView = Backbone.View.extend({
initialize: function(){
this.render();
},
render: function() {
var root = this.$el;
_.each(this.model, function(item) {
var row = '<tr><td>' + item + '</td></tr>';
root.find('tbody').append(row);
});
return this;
}
});
var cView = new ContractView({ model: contract, el: $('#contracts') });
I have Chrome's developer tools open. If I do a console.log(this.model) inside of the render function, I can see a mess of an object, of which the two records are stored in .attributes. But instead of two rows being added to the table, I get 7. 6 of which are objects. (Though I see 9 subobjects in Chrome's console).
Not much of this makes sense to me. Can anyone help me not only get this working, but also understand it? I know that render() fires off as soon as I have instantiated cView, and I know that it's doing the ajax as soon as I do .fetch() into the model. But that's the limit of what I can understand in this.
You should fetch and iterate on the collection, not the model. A model is one "thing" and a collection has many "things". Assuming you are fetching a JSON formatted array into your model, it will end up with properties like "1", "2", and so on, and each of these will just be a normal Javascript object, not a ContractModel instance.
Here is how you might restructure your code:
var ContractModel = Backbone.Model.extend();
var ContractCollection = Backbone.Collection.extend({
//ContractModel class, not an instance
model: ContractModel,
//Set the url property on the collection, not the model
url: "${g.createLink(controller:'waiverContract', action:'index')}"
})
var ContractView = Backbone.View.extend({
initialize: function(){
//Bind the collection reset event, gets fired when fetch complets
this.collection.on('reset', this.render, this);
},
render: function() {
//This finds the tbody element scoped to your view.
//This assumes you have already added a tbody to the view somehow.
//You might do this with something like
//this.$el.html('<table><tbody></tbody></table>');
var $tbody = this.$('tbody');
this.collection.each(function(contract) {
//Add any other contract properties here,
//as needed, by calling the get() model method
var row = '<tr><td>' + contract.get('someContractProperty') + '</td></tr>';
//Append the row to the tbody
$tbody.append(row);
});
return this;
}
});
//Instantiate collection here, and pass it to the view
var contracts = new ContractCollection();
var cView = new ContractView({
collection: contracts,
el: $('#contracts')
});
//Makes the AJAX call.
//Triggers reset on success, and causes the view to render.
//Assumes a JSON response format like:
// [ { ... }, { ... }, ... ]
contracts.fetch();

Categories

Resources