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);
}
});
Related
I'm learning how to use backbone.js. I got 4 JSON APIs, and I need to collect all the responses and fetch the total response to single view
How can I achieve this? Do I need to use collection/model to achieve this?
I implemented a 2 call main view with sub views. It gets a lot more complex then that. But that should get you started.
These are the main 2 collections that get the data. If you want to break it down further (depends on needs) you can create a Model and then assign it to the collection using model: CityModel (in this case).
var Cities = Backbone.Collection.extend({
url: 'http://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=me',
parse: function(response) {
return response.geonames;
}
});
var Earthquakes = Backbone.Collection.extend({
url: 'http://api.geonames.org/earthquakesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&username=me',
parse: function(response) {
return response.earthquakes;
}
});
then the sub views, standard: here you can create events for each.
var EarthquakeView = Backbone.View.extend({
tagName: 'li',
template: _.template('<strong><%=eqid%></strong> <%=lng%> <%=lat%> magnitude: <%=magnitude%>'),
render: function() {
this.$el.html(this.template(this.model.toJSON()))
return this;
}
});
Notice here you can extend a view so you dont have to repeat yourself.
var CityView = EarthquakeView.extend({
template: _.template('<strong><%=toponymName%></strong> <%=lng%> <%=lat%>')
});
The main view..
var View = Backbone.View.extend({
initialize: function(opt) {
// attach to the main body
$(opt['main']).append(this.el)
this.earthquakesEl = $('<ol>');
this.earthquakes = new Earthquakes()
this.earthquakes.on('sync', _.partial(this.renderList, EarthquakeView, this.earthquakesEl), this);
this.earthquakes.fetch()
this.citiesEl = $('<ol>');
this.cities = new Cities()
this.cities.on('sync', _.partial(this.renderList, CityView, this.citiesEl), this);
this.cities.fetch()
// initialize the element
this.render();
},
renderList: function(view, el, collection) {
collection.forEach(function(model) {
el.append(new view({model: model}).render().el)
});
},
render: function() {
this.$el.append(this.earthquakesEl);
this.$el.append(this.citiesEl);
return this;
}
});
new View({ main: $('body') });
http://jsfiddle.net/u9y574y8/2/
There are 2 collection calls that are made independently, and there are 2 listeners that wait for them to finish and then place it on the main view. If you have any issue let me know. I think its pretty straight forward.
I'm trying to parse JSON data using Backbone from a remote API. Here's what I've got so far:
// --------------------------------------------------
// MODELS
// --------------------------------------------------
var VideoModel = Backbone.Model.extend({
idAttribute: '_id',
parse: function(){
this.id = response._id;
}
});
var videoModel = new VideoModel({ parse:true });
// --------------------------------------------------
// COLLECTIONS
// --------------------------------------------------
var VideosCollection = Backbone.Collection.extend({
model: VideoModel,
url: 'redacted',
parse: function(response){
this.videos = response.data;
this.cid = response.cid;
return response.data;
},
render: function(){
this.collection.forEach(this.addone, this);
}
});
var videosCollection = new VideosCollection();
videosCollection.fetch({
success: function(videos){
console.log('success!');
},
error: function(){
console.log('failed.');
}
});
// --------------------------------------------------
// VIEWS
// --------------------------------------------------
var VideoView = Backbone.View.extend({
template: _.template('<%= videoModel.id %>'),
render: function(){
this$el.html(this.template(this.model.attributes));
return this;
}
});
var videoView = new VideoView({});
var VideosCollectionView = Backbone.View.extend({});
var videosCollectionView = new VideosCollectionView({
collection: videosCollection,
render: function(){
this.collection.forEach(this.addOne, this);
},
addOne: function(videoModel){
this.$el.append(videoView.el);
}
});
What I'm having trouble with is that console.log(videoModel.id) is still undefined.
The data is a playlist of videos, which is valid JSON:
{
"total":24,
"per_page":24,
"current_page":1,
"last_page":1,
"from":1,
"to":24,
"data":[
{
"_id":"55d1bb50140ba04c1d8b4583",
Be glad for some prompts in the right direction - especially since I had it working this morning and then ... reverted to a previous version without saving.
Thanks
From documentstion of parse method:
The function is passed the raw response object, and should return the
attributes hash to be set on the model. The default implementation is
a no-op, simply passing through the JSON response.
So by default it works like this:
parse: function(data) {
return data;
}
Your code now returns nothing and model takes no data.
As I see you want to set id attribute but you already have setted idAttribute property with _id and there is _id in your data so it should work fine without parse at all. Try to remove it.
The answer is that I was trying to parse via a Collection instead of a Model. So I would never have got the id I was looking for. For some reason.
I needed to move the URL into the Model and parse from there:
var VideoModel = Backbone.Model.extend({
idAttribute: '_id',
url: 'redacted',
parse: function(response){
var id = response._id;
var cid = response.cid;
return response.data;
},
}),
Now when I do videoModel.get(1) in the console, it returns the value of the first item in my array.
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() }));
}
});
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;
});
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});