Backbone - json data not displayed in browser but rendering in console - json

I am able to see the json data in console, but not displayed in the html. I am not sure what change must be done to make it render in the browser.
Here is my code.
Model.js
var agent = Backbone.Model.extend({
});
var agentList = Backbone.Collection.extend({
model: agent,
url: 'data/agents.json',
});
View.js
var agentListView = Backbone.View.extend({
el: '.container',
initialize: function() {
this.template = _.template( tpl.get('agentList'));
},
render: function() {
var agents = new agentList();
agents.fetch({
success: function(agents) {
console.log(agents.toJSON());
}
});
this.$el.html(this.template({ AgentsList: agents.toJSON()}))
},
});
HTML
<% _.each(AgentsList, function(item) { %>
<tr>
<td>data</td>
<td><%= item.name%></td>
<td><%= item.gender%></td>
<td><%= item.birthYear%></td>
<td><%= item.skills%></td>
</tr>
<% }); %>

The problem is that you don't render the view in the callback, and therefore you won't have anything to show.
Besides, getting the list of agents inside the render function is definitely not something to do if you want to respect MVC principles. You should have something like:
var agentCol = new AgentList();
var agentView = new AgentListView({collection: agentCol});
agentCol.fetch({
success: function() {
agentView.render();
}
});
and your AgentListView should look like
var AgentListView = Backbone.View.extend({
el: '.container',
initialize: function() {
this.template = _.template(tpl.get('agentList'));
},
render: function() {
this.$el.html(this.template({ agents: this.collection.toJSON()}));
},
});
(I took the liberty to rename some variables to match naming conventions)

Its maybe because the time when the $el update code runs the agents collection is not loaded (fetched) yet.
May be you can update your render method with the code below, so that the $el shall be updated only when the collection fetch is returned from the server.
render: function() {
var $this = this;
var agents = new agentList();
agents.fetch({
success: function(agents) {
console.log(agents.toJSON());
$this.$el.html($this.template({ AgentsList: agents.toJSON() }));
}
});
}
Or an an alternative you can listen to the collection event (reset maybe) which fires whenever the data is fetched from the remote data source - json file in this case.
In that case the view code could be,
var agentListView = Backbone.View.extend({
el: '.container',
initialize: function() {
this.template = _.template( tpl.get('agentList'));
this.collection = new agentList();
this.listenTo(this.collection, "reset", this.addAgentsToDom);
},
render: function() {
this.collection.fetch({
success: function(agents) {
console.log(agents.toJSON());
}
});
},
addAgentsToDom: function(collection, options) { // please name the method whatever you want :)
this.$el.html(this.template({ AgentsList: this.collection.toJSON() }));
}
});

Related

Backbone insert data inside Model from another

I have a simple app in backbone where the logic isn't clear for me, It's my second app and I don't know if is good the logic and how I have construct it.
The goal is when I load a page I want to retrieve all folders and files of a specific directory and print it.
I have create an app general where I include another app called Folder.
When I load the page I create the general app, search inside a folder and retireve folders and files into a json with an ajax function.
I have succesfull do that but my problem is how now populate my FolderView, FolderModel and Folder Collection?
Is necessary to do that I think but I don't know how to pass this json to FolderCollection/FOlderModel.
This is my appView
define(['jquery' , 'backbone', 'views/folder', 'models/folder', 'collections/folder', 'models/app'],
function($, Backbone, FolderView, FolderModel, FolderCollection, AppModel){
var AppView = Backbone.View.extend({
model: AppModel,
el:$('#folder'),
initialize: function(){
console.log('initialize AppView');
this.retrievFolderFile('uploads');
//prendo le cartelle e le metto nel json
this.folders = new FolderCollection();
this.render();
//this.todos.bind('sync', this.render, this);
//this.todos.fetch();
},
render: function(){
var element = this.$el;
element.html('');
this.folders.each(function(model){
var folderView = new FolderView({model:model});
element.append(folderView.el);
});
},
retrievFolderFile: function(dir){
var here = this;
console.log('retrieveFolderFile');
$.ajax({
type: "POST",
dataType: "json",
data: {"dir": dir},
url: 'function/retrieve_folder.php',
success: function(data) {
console.log(data);
// ------------------ HERE I HAVE RESULT HOW TO PASS TO FOLDER VIEW/ COLLECTION?------
}
});
}
})
return AppView;
});
This is my folderModel
define(['backbone'],function(Backbone){
var FolderModel = Backbone.Model.extend({
defaults:{
name:'Folder',
path:''
},
initialize:function(){
console.log('Initialized Folder model');
}
});
return FolderModel;
});
This is my FolderCollection
define(['backbone', 'models/folder'], function(Backbone, FolderModel){
var folderCollection = Backbone.Collection.extend({
model: FolderModel,
});
return folderCollection;
});
This is my FolderView
define(['jquery', 'underscore', 'backbone', 'text!templates/folder.html'], function($, _, Backbone, FolderTemplate){
var FolderView = Backbone.View.extend({
el:$('#folder'),
template: _.template(FolderTemplate),
initialize: function(){
console.log('FolderView initialized');
this.render();
},
render: function(){
console.log('FolderView render');
//$(this.el).html(this.template({model: this.options.model}));
}
});
return FolderView;
});
To retrieve folder and files I use this file PHP that return me a valid json to backbone
<?php
$dir = $_POST['dir'];
$data = fillArrayWithFileNodes( new DirectoryIterator( '../'.$dir ) );
function fillArrayWithFileNodes( DirectoryIterator $dir )
{
$data = array();
foreach ( $dir as $node )
{
if ( $node->isDir() && !$node->isDot() )
{
//$data[$node->getFilename()] = fillArrayWithFileNodes( new DirectoryIterator( $node->getPathname() ) );
$arr_to_insert = array();
$arr_to_insert['name'] = $node->getFilename();
$arr_to_insert['type'] = 'folder';
array_push($data, $arr_to_insert);
}
else if ( $node->isFile() )
{
//$data[] = $node->getFilename();
$arr_to_insert = array();
$arr_to_insert['name'] = $node->getFilename();
$arr_to_insert['type'] = 'file';
array_push($data, $arr_to_insert);
}
}
return $data;
}
echo json_encode($data);
?>
How to pass from appview the json that return me?
Is correct to do the folderand filde search inside appview or I have to do it inside folderModel?
Because after when I click on a folder I want to retrieve its file and folders again.
Help me to understand the logic and how to pass it.
I thought to call a function inside FolderCollection like parseFolderFile and insert it into a model but how?
Thanks
AJAX is asynchronous.. So you are making an ajax call and then calling the render method which is iterating over the collection and renders them.
But your data is not ready until and after the sucess callback, where I guess your collection will be populated. So you are supposed to populate the collection inside the success call back and then call render.
So something in these lines
initialize: function () {
console.log('initialize AppView');
this.folders = new FolderCollection();
this.retrievFolderFile('uploads');
},
render: function () {
var element = this.$el;
element.html('');
this.folders.each(function (model) {
var folderView = new FolderView({
model: model
});
element.append(folderView.el);
});
},
retrievFolderFile: function (dir) {
var here = this;
console.log('retrieveFolderFile');
$.ajax({
type: "POST",
dataType: "json",
data: {
"dir": dir
},
url: 'function/retrieve_folder.php',
success: function (data) {
console.log(data);
here.folders.set(data);
here.render();
}
});
}
Also why do you want to handle an ajax request , where there is already a native one provided by backbone.
You can declare the url in the collection do this.folders.fetch() , and then listen to the sync or the resent event which will be lot more cleaner in my view.
Using native Fetch
define(['jquery', 'backbone', 'views/folder', 'models/folder',
'collections/folder', 'models/app'],
function ($, Backbone, FolderView, FolderModel,
FolderCollection, AppModel) {
var AppView = Backbone.View.extend({
model: AppModel,
el: $('#folder'),
initialize: function () {
this.folders = new FolderCollection();
// This will automatically call the render once the
// collection is reset
this.listenTo(this.folders, 'reset', this.render);
this.folders.data = 'uploads';
this.folders.fetch();
},
render: function () {
var element = this.$el;
element.html('');
this.folders.each(function (model) {
var folderView = new FolderView({
model: model
});
element.append(folderView.el);
});
}
})
return AppView;
});
define(['backbone', 'models/folder'], function(Backbone, FolderModel){
var folderCollection = Backbone.Collection.extend({
model: FolderModel,
url : 'function/retrieve_folder.php' + this.data;
});
return folderCollection;
});

backbone fetch specific object from json file, not rendering

Okay, im going slightly insane trying to display data from a json file in 2 seperate views/collections.
I will paste my entire code here and try to explain where i am now, and what i need done.
Its probably a very small thing i just need to do in order for it to work, but i cant see it..
Here is a screen shot of how my page looks like now, as you can see the data is being loaded, i just cant get it into the views properly..
In my Collection class i call parse:
parse:function(response, options) {
return options.parseField ? response[options.parseField] : response;
},
i call sync: (not sure its needed at all)
sync: function(method, model, options) {
options.contentType = "application/json";
options.cache = false;
options.dataType = "json";
return Backbone.sync(method, model, options);
},
Then near the bottom, i create 2 new collections and use fetch to get the specific json data i need for each collection like so:
var links = new App.Collections.Links();
links.fetch({
parseField: 'links_1',
success: function () {
console.log(links.toJSON());
return links.toJSON();
}
});
var links2 = new App.Collections.Links();
links2.fetch({
parseField: 'links_2',
success: function () {
console.log(links2.toJSON());
return links2.toJSON();
}
});
I do console.log and can see that my json data is getting loaded just fine, but its not getting rendered ?
What am i doing wrong here..
For sake of debugging and understanding i have included my entire js file below.
(function() {
// Helper functions
// Defining namespacing rules
window.App = {
Models: {},
Collections: {},
Views: {}
};
// Setting global template function, for simpel declaration later on by setting template('name'); for the built in template function.
window.template = function(id) {
return _.template( $('.' + id).html() );
};
// Capitalize first letter in a link by adding .capitalize(); to the string.
String.prototype.capitalize = function() {
return this.charAt(0).toUpperCase() + this.slice(1);
};
// Extending Backbone
//Modellen
App.Models.Link = Backbone.Model.extend({
defaults: {
navn : 'i haz a name!',
link : 'http://www.lolcats.com/',
counter : 0
}
});
//Collection
App.Collections.Links = Backbone.Collection.extend({
model: App.Models.Link,
url: 'data1.json',
parse:function(response, options) {
return options.parseField ? response[options.parseField] : response;
},
sync: function(method, model, options) {
options.contentType = "application/json";
options.cache = false;
options.dataType = "json";
return Backbone.sync(method, model, options);
},
// Sort the models 'highest first'
comparator: function(link) {
return -link.get('counter');
}
});
//Singel view
App.Views.LinkView = Backbone.View.extend({
tagnavn: 'li',
template: template('Links'),
initialize: function() {
this.model.on('change', this.render, this);
this.model.on('destroy', this.remove, this);
},
events: {
'click .edit' : 'retLink',
'click .delete' : 'destroy',
'click .LinkUrl' : 'addCounter'
},
retLink: function() {
var newLinkNavn = prompt('What should the new name be?', this.model.get('navn'));
if ( !newLinkNavn ) return;
newLinkNavn = newLinkNavn.capitalize();
this.model.set('navn', newLinkNavn);
var newLinkUrl = prompt('What should the new url be?', this.model.get('link'));
if ( !newLinkUrl ) return;
this.model.set('link', newLinkUrl);
},
destroy: function() {
this.model.destroy();
},
// Increment the counter then user clicks it
addCounter: function(e) {
e.preventDefault();
var newCounter = this.model.get('counter');
this.model.set('counter', newCounter + 1);
},
remove: function() {
this.$el.remove();
},
render: function() {
this.$el.html(this.template(this.model.toJSON()) );
return this;
}
});
//Collection View
App.Views.LinksView = Backbone.View.extend({
tagName: 'ul',
className: 'liste',
initialize: function() {
_.bindAll(this);
this.collection.on('add', this.addOne, this);
this.collection.on('reset', this.render);
// Render view when a user has changed a model
this.collection.bind('change', this.render, this);
$('.navnClass').focus();
this.load();
this.render();
},
load: function() {
this.collection.fetch({
add: true,
success: this.loadCompleteHandler,
error: this.errorHandler
});
},
loadCompleteHandler : function(){
this.render();
},
errorHandler : function(){
throw "Error loading JSON file";
},
render: function() {
// Empty the UL before populating it with the new models and sorting it.
this.$el.empty();
this.collection.sort();
this.collection.each(this.addOne, this);
return this;
},
addOne: function(link) {
var linkView = new App.Views.LinkView({ model: link });
this.$el.append(linkView.render().el);
}
});
// Create link view
App.Views.AddLink = Backbone.View.extend({
el: '#addLink',
events: {
'submit' : 'submit'
},
submit: function(e) {
e.preventDefault();
var linkNavn = $(e.currentTarget).find('.navnClass').val(),
linkNum = $(e.currentTarget).find('.linkClass').val();
// Tildel link navn en værdi, hvis det er tomt
if ( ! $.trim(linkNavn)) {
linkNavn = 'I haz a name!';
}
// Tildel link url en værdi, hvis det er tomt
if( ! $.trim(linkNum)) {
linkNum = 'http://www.lolcats.com/';
}
// Tilføj http:// foran værdi, hvis den ikke indeholder det i forvejen.
if( linkNum.indexOf( "http://" ) == -1 ) {
addedValue = 'http://',
linkNum = addedValue + linkNum;
}
// nulstil og sæt fokus på link navn feltet.
$(e.currentTarget).find('.navnClass').val('').focus();
$(e.currentTarget).find('.linkClass').val('');
this.collection.add({ navn:linkNavn, link: linkNum });
}
});
// new links collection
// populate collection from external JSON file
// change navn to match the link heading
var links = new App.Collections.Links();
links.fetch({
parseField: 'links_1',
success: function () {
console.log(links.toJSON());
return links.toJSON();
}
});
var links2 = new App.Collections.Links();
links2.fetch({
parseField: 'links_2',
success: function () {
console.log(links2.toJSON());
return links2.toJSON();
}
});
// new collection view (add)
var addLinkView = new App.Views.AddLink({ collection: links });
// new collection view
var linksView = new App.Views.LinksView({ collection: links });
$('.links').html(linksView.el);
// new collection view
var linksView2 = new App.Views.LinksView({ collection: links2 });
$('.links2').html(linksView2.el);
})();
Could you try this:
var links2 = new App.Collections.Links();
links2.on("reset", function(collection){
console.log("reset event", collection);
}
links2.fetch({
parseField: 'links_2',
wait:true, #wait for the server to respond
success: function (collection, response) {
console.log(collection, response);
}
});
a return in the success call doesn't do anything (it would return to the $.ajax object). I've added a wait because it would call the successcall instantly if it passes the clientside validation (you have none, so it would always call success first).
You say it doesn't render anything. But to render something you need a View. I don't see a view in your code.
Here's a quick example of the view:
var Link = new Backbone.View.extends({
el: $('body'),
initialize: function(){
this.listenTo(this.collection, 'reset', this.render, this)
},
render: function(){
this.$el.empty();
this.collection.each(function(item){
this.$el.append(item.get('id') + '<br />');
});
}
})
var view = new Link({collection: links2});

Accessing model data from a view with backbone.js

I have the following code but am struggling to get my view to render the template instead of my model. It all works fine if I render the handlebars template through my model but would like to separate my code into the view.
var DataModel = Backbone.Model.extend({
initialize: function () {
$.getJSON('js/data.json',function(data){
$('.one-wrapper').append(Handlebars.compile($('#one-template').html())(data));
$('.one-asset-loader').fadeOut('slow');
});
},
defaults : function () {
},
});
var StructureView = Backbone.View.extend ({
initialize: function () {
}
});
var structureView = new StructureView({model: new DataModel()});
You can access the model inside the view using this.model.
Your code should look something like:
var StructureView = Backbone.View.extend ({
initialize: function () {
_.bindAll(this);
this.render();
this.model.on('change',this.render);
},
render: function() {
$('.one-wrapper').empty().append(Handlebars.compile($('#one-template').html())( this.model.toJSON() ));
}
});
This will work assuming your model actually contains the data. To do this you need to use the model.url and model.fetch() (not $.getJSON)

How to get json with backbone.js

I am trying to use backbones.js fetch to get json from a twitter search
and my code below can someone tell me where I am going wrong?
(function($){
var Item = Backbone.Model.extend();
var List = Backbone.Collection.extend({
model: Item,
url:"http://search.twitter.com/search.json?q=blue%20angels&rpp=5&include_entities=true&result_type=mixed"
});
var ListView = Backbone.View.extend({
el: $('#test'),
events: {
'click button#add': 'getPost'
},
initialize: function(){
_.bindAll(this, 'render', 'getPost');
this.collection = new List();
this.render();
},
render: function(){
var self = this;
$(this.el).append("<button id='add'>get</button>");
},
getPost: function(){
console.log(this.collection.fetch());
}
});
// **listView instance**: Instantiate main app view.
var listView = new ListView();
})(jQuery);​
I am just getting started with backbone and I just want to console.log the json
you can see my example here. jsfiddle.net/YnJ9q/2/
There are two issues above:
Firstly, you need to add a success/fail callback to the fetch method in order for you to have the fetched JSON logged to the console.
getPost: function(){
var that = this;
this.collection.fetch(
{
success: function () {
console.log(that.collection.toJSON());
},
error: function() {
console.log('Failed to fetch!');
}
});
}
Another problem is the issue of "same-origin-policy'. You can find out how to resolve that by taking a look at this link.
Update:
I modified your code and included the updated sync method. It now works! Take a look here!
Basically, update your collection to include the parse and sync methods as below:
var List = Backbone.Collection.extend({
model: Item,
url: "http://search.twitter.com/search.json?q=blue%20angels&rpp=5&include_entities=true&result_type=mixed",
parse: function(response) {
return response.results;
},
sync: function(method, model, options) {
var that = this;
var params = _.extend({
type: 'GET',
dataType: 'jsonp',
url: that.url,
processData: false
}, options);
return $.ajax(params);
}
});

Pulling JSON subarray items into a template in Backbone

a question fron a total Backbone newbie:
I want to pull items from a JSON subarray and place them in a specific spot on my template. I'm not sure how to target those specific items.
The subarray is represented in the code below: it's the two arrays named images:
(function () {
//JSON object containing gallery content
var galleries = [
{ name: 'Renovations', images: [
'../img/Galleries/01/pic01.jpg',
'../img/Galleries/01/pic02.jpg'
]
},
{ name: 'Kitchens', images: [
'../img/Galleries/01/pic03.jpg',
'../img/Galleries/01/pic04.jpg'
]
}
];
//Gallery Class...this is a Backbone Model
var Gallery = Backbone.Model.extend({
initialize: function(){
console.log('this gallery is up and running');
}
});
//A collection...also a class & the 'C' in MVC in this case
var SingleGallery = Backbone.Collection.extend({
model: Gallery,
parse: function(response){
return response.items;
console.log(response.items);
}
});
//A view...also a class
var GalleryGroup = Backbone.View.extend({
tagName: 'article',
className: 'contact-container',
template: $('#galleryTemplate').html(),
render: function () {
var tmpl = _.template(this.template);
this.$el.html(tmpl(this.model.toJSON()));
return this;
}
});
//Another view...also a class
var GalleryView = Backbone.View.extend({
el: document.getElementById('container'),
initialize: function () {
this.collection = new SingleGallery(galleries);
this.render();
},
render: function () {
var that = this;
_.each(this.collection.models, function (item) {
that.renderGallery(item);
}, this);
},
renderGallery: function (item) {
var galleryView = new GalleryGroup({
model: item
});
this.$el.append(galleryView.render().el);
}
});
var gal1 = new GalleryView();
} ());
My template looks like this so far:
<script id="galleryTemplate" type="text/template">
<p><h1><%= name %></h1>
<div><img src='<%= images %>' /></div></p>
</script>
If I only had one image to load into <%= images %> section, this would be easy, but I would like to construct a well-formatted JSON object and be able to find each individual item in the two 'images' arrays. I want to think that fetch() is the way to go but I'm not sure...any ideas?
Thanks in advance.
is
You could use underscore's each method to iterate over the images array along these lines:
<% _.each(images, function (image) { %>
<div><img src='<%= image %>' /></div>
<% }); %>