I am using require.js with backbone and backbone.localstorage and I am trying to figure out how to make use of the data after calling fetch, not sure how to go about it... I am trying to pass the data into my view and make use of it.
Here is the example of the data stored in localstorage:
[{"artist":"Hits 1 Entertainment 4-1-1","title":"Hear Katy's Perry's New Album!"}, ...]
So it is objects within an array.
This is my code for backbone...
var songz = new Songs();
songz.localStorage = new Backbone.LocalStorage("music");
songz.fetch({dataType: 'json'});
var songV = new SongV({collection: songz});
songV.render();
Songs is a collection, that looks like this in the collections file, SongV is the view for each song.
Here is the view with the code above included:
define([
'jquery',
'underscore',
'backbone',
'models/song',
'collections/songs',
'views/song',
'text!templates/page.html'
], function($, _, Backbone, Song, Songs, SongV, PageT){
var Page = Backbone.View.extend({
el: $("#page"),
render: function () {
this.$el.html( PageT );
var songz = new Songs();
songz.localStorage = new Backbone.LocalStorage("music");
songz.fetch({dataType: 'json'});
var songV = new SongV({collection: songz});
songV.render();
}
});
return Page;
});
Here is the collection file:
define([
'jquery',
'underscore',
'backbone',
'models/song',
], function($, _, Backbone, Song){
var Songs = Backbone.Collection.extend({
model: Song,
initialize: function () {
}
});
return Songs;
});
Here is the model file:
define([
'underscore',
'backbone'
], function(_, Backbone) {
var Song = Backbone.Model.extend({
});
return Song;
});
Here is the template file:
<tr>
<th> Number </th>
<th> Title </th>
<th> Artist </th>
<th> Date_Added </th>
<th> Video </th>
</tr>
<% _.each(songs, function(song){ %>
<tr>
<td> <%= song.get("number") %> </td>
<td> <%= song.get("title") %> </td>
<td> <%= song.get("artist") %> </td>
<td> <%= song.get("added_on") %> </td>
<td> <%= song.get("video") %> </td>
</tr>
<% }); %>
You need to fetch and then bind to the reset event on the collection to see when it was successfully pulled the data from the server.
Page = Backbone.View.extend
el: $('#page')
render: ->
songz = new Songs()
# Initialize view
songV = new SongV({collection: songz})
# Render view
songV.render()
# Fetch collection
songz.fetch()
SongV = Backbone.View.extend
initialize: ->
#listenTo #collection, "reset", #onReset
onReset: (collection) ->
# Use populated collection data
...
Songs = Backbone.Collection.extend
model: Song
localStorage: new Backbone.LocalStorage("music")
initialize: ->
This is how Backbone.LocalStorage stores the collections and models --> here
See the table at the bottom, the key for the chain block is you local storage name and then each model has a unique key.
So this means if you have data sitting in local storage that you have put there yourself, you should take it out with a cross-browser local storage device like store.js and then use it to populate your Backbone.Collection.
Alternatively, you could fetch from the server (recommended) and that will populate your collection. Or you could bootstrap the data on page load and reset your collection that way.
Related
I need help with my code, I'm trying to learn Backbone for my Social Project. I'm trying to render a view from a collection that I got from an API (deployd API)
Here is the HTML code for the table:
<div class="container-fluid">
<table id= "teachers">
<thead>
<tr>
<th>Name</th>
<th>Last Name</th>
<th>Code</th>
<th>Last time online</th>
</tr>
</thead>
<tbody id="table-body"></tbody>
</table>
</div>
<script type="text/template" id="teacher-template">
<td><%= name %></td>
<td><%= lastname %></td>
<td><%= code %></td>
<td><%= lastactivity %></td>
</script>
Here is the JS code:
var TeacherModel = Backbone.Model.extend({
defaults: {
id:'',
name: '',
lastname: '',
code: '',
lastactivity: ''
}
});
var TeacherCollection = Backbone.Collection.extend({
url: "/teachers",
model: TeacherModel
});
var teachercollection = new TeacherCollection();
teachercollection.url = '/teachers';
teachercollection.fetch({
success: function(collection, response) {
console.log("Done!!");
}, error: function(collection, response) {
alert(response);
}
});
var TeachersView = Backbone.View.extend({
el: '#table-body',
initialize: function() {
this.render();
},
render: function() {
this.$el.html('');
teachercollection.each(function(model) {
var teacher = new TeacherView({
model: model
});
this.$el.append(teacher.render().el);
}.bind(this));
return this;
}
});
var TeacherView = Backbone.View.extend({
tagName: 'tr',
template: _.template($('#teacher-template').html()),
render: function() {
this.$el.html(this.template(this.model.attributes));
return this;
}
});
// Launch app
var app = new TeachersView;
So my question is, how I can pass a collection to a view, or a model of the collection to a view? I want to render the data in each row from the table. The browser gets the collection, as you can see here:
I've been trying for days, and I just can't understand the logic, I have read the documentation, and a little of the Addy Osmani's book but just can't get my head on it, can someone explain it to me? Been looking for answers in this site but some on them include some "add models" stuff, which confuse me more.
(The parameters of the model in the image, differ from the code. I'd translate to make it more easy to understand.)
how I can pass a collection to a view, or a model of the collection to a view?
You are already doing that in your code:
var teacher = new TeacherView({
model: model
});
Here you're passing a model to view's constructor using model option.
You can pass a collection to view via it's constructor like:
var app = new TeachersView({
collection:teachercollection
});
Which you can access inside the view via this.collection and this.model respectively.
var TeachersView = Backbone.View.extend({
el: '#table-body',
initialize: function() {
this.render();
},
render: function() {
this.$el.html('');
this.collection.each(function(model) {
this.$el.append(new TeacherView({
model: model
}).el);
},this);
return this;
}
});
Note that fetch() is asynchronous, so you'll need to wait till it succeeds before rendering the view.
See the suggestions in this answer regarding the changes I made to your render method.
this answer might help understanding a thing or two.
I'm new to Backbone.js, and just finished running through a basic tutorial to create a "user list" system (https://www.youtube.com/watch?v=FZSjvWtUxYk) where all the templates, scripts, etc are created inline. I got everything working pretty easily, so I decided to try and modularize things since I know that's the best practice. I'm following this guide to the AMD methodology (https://cdnjs.com/libraries/backbone.js/tutorials/organizing-backbone-using-modules) and have everything working properly except for one thing - when editing a user, the "current" data isn't being loaded into the form. All of the issues I've found on SO and other places so far have been solved by putting the template generating code inside the success: callback of the .fetch() call, but I'm already doing that.
Here's the code:
(I'm leaving out the main.js and app.js that handle the require.js configuration, router init, etc. They seem to be working just fine.)
// Filename: router.js
define([
'jquery',
'underscore',
'backbone',
'views/userList',
'views/editUser'
], function($, _, Backbone, UserListView, EditUserView){
var AppRouter = Backbone.Router.extend({
routes: {
'': 'home',
'new': 'editUser',
'edit/:id': 'editUser'
}
});
var initialize = function(){
var app_router = new AppRouter;
app_router.on('route:home', function(){
var userListView = new UserListView();
userListView.render();
});
app_router.on('route:editUser', function(id) {
var editUserView = new EditUserView();
editUserView.render({ id: id });
});
Backbone.history.start();
};
return {
initialize: initialize
};
});
views/editUser.js
// Filename: views/editUser
define([
'jquery',
'underscore',
'backbone',
'models/user',
'text!/templates/editUser.html'
], function($, _, Backbone, UserModel, rawEditUserTemplate) {
var userListView = Backbone.View.extend({
// Element to use for this view
el: $('.page'),
// Function to call when this view is rendered
render: function(options) {
var that = this;
// If there is an ID, we are editing
if ( options.id ) {
// Create the user, passing the ID
that.editUser = new UserModel({ id: options.id });
// Fetch the user data
that.editUser.fetch({
// When the fetch is returned
success: function(userData) {
// Generate the template and pass the data in
var editUserTemplate = _.template( rawEditUserTemplate );
that.$el.html(editUserTemplate({ user: userData }));
}
})
}
else { // We are creating a new user
// Generate the template with an empty user
var editUserTemplate = _.template( rawEditUserTemplate );
this.$el.html(editUserTemplate({ user: null }));
}
},
events: {
'submit .edit-user-form': 'saveUser',
'click .delete': 'deleteUser'
},
saveUser: function(e) {
e.preventDefault();
// Get the details
var userDetails = $(e.currentTarget).serializeObject();
// Create a user model
var user = new UserModel();
// Save the user details
user.save(userDetails, {
success: function(user) {
Backbone.history.navigate('', { trigger: true });
}
});
},
deleteUser: function(e) {
e.preventDefault();
// Destroy the user we are editing
this.editUser.destroy({
// When the destroy is finished
success: function() {
// Back to home
Backbone.history.navigate('', { trigger: true });
}
});
}
});
// Our module now returns our view
return userListView;
});
templates/editUser.html
<form class="edit-user-form">
<legend><%= user ? 'Update' : 'Create' %> User</legend>
<div class="form-group">
<label for="firstname">First Name</label>
<input type="text" class="form-control" name="firstname" id="firstname" value="<%= user ? user.get('firstname') : '' %>" />
</div>
<div class="form-group">
<label for="lastname">Last Name</label>
<input type="text" class="form-control" name="lastname" id="lastname" value="<%= user ? user.get('lastname') : '' %>" />
</div>
<div class="form-group">
<label for="age">Age</label>
<input type="text" class="form-control" name="age" id="age" value="<%= user ? user.get('age') : '' %>" />
</div>
<hr />
<button class="btn btn-success" type="submit"><%= user ? 'Update' : 'Create' %></button>
<% if ( user ) { %>
<input type="hidden" name="id" id="id" value="<%= user.id %>" />
<button class="btn btn-danger delete">Delete</button>
<% }; %>
</form>
Using this code, I get a blank edit form regardless of whether or not I'm editing or creating, HOWEVER the "Create" vs "Update" text switch in the template is working properly. This means that a user object is in fact being passed, and when I add a console.log(user) into the template file, it is in fact showing me user data. When I log user.get('firstname') or any other attribute, however, it logs "undefined".
The issue was in my User model, which I didn't include above because I didn't understand at the time why it could be relevant.
I was defining it as:
var userModel = Backbone.Model.extend({
url: '/users'
});
When it should have been:
var userModel = Backbone.Model.extend({
urlRoot: '/users'
});
The wrong option was causing the API to return a collection rather than a model, so the .get() wasn't able to work properly.
I'm trying to remove a row in a table with Backbone. I'm using Rails 4 and the backbone-on-rails gem for this project.
I'm using a addEntryevent to add row in my Movies table. This adds a new row containing a id, movie_title and user_id.
The index view
class Movieseat.Views.MovieseatsIndex extends Backbone.View
template: JST['movieseats/index']
initialize: ->
#collection.on('update', #render, this)
#collection.on('add', #appendEntry, this)
render: ->
$(#el).html(#template())
#collection.each(#appendEntry)
this
events: ->
"click li": "addEntry"
"click .remove": "removeEntry"
addEntry: (e) ->
movie_title = $(e.target).text()
#collection.create title: movie_title
removeEntry: (e) ->
thisid = #$(e.currentTarget).closest('div').data('id')
console.log thisid
#collection.remove thisid
appendEntry: (entry) ->
view = new Movieseat.Views.Entry(model: entry)
$('#entries').append(view.render().el)
When I click on the .remove element I get a console log result showing the id stored in the element I want to remove.
The Entry view
class Movieseat.Views.Entry extends Backbone.View
template: JST['movieseats/entry']
className: 'movie-frame'
render: ->
$(#el).html(#template(entry: #model))
this
This is my index.jst.eco template
<h1>Movies</h1>
<div id="entries"></div>
This is the Entry template I render in the index.jst.eco template (i'm doing this so I only rerender the movie movies added to to the view.
<div class="movie-frame" data-id="<%= #entry.get('id') %>">
<p><%= #entry.get('title') %></p>
<p class="remove">Remove</p>
</div>
And my Backbone routes,
class Movieseat.Routers.Movieseats extends Backbone.Router
routes:
'': 'index'
initialize: ->
#collection = new Movieseat.Collections.Movieseats()
#collection.fetch()
index: ->
view = new Movieseat.Views.MovieseatsIndex(collection: #collection)
$('#container').html(view.render().el)
But there's no network activity when I click on the .remove element. There is network activity when the addEntry event is triggerd.
So apparently #collection.remove doesn't put out a HTTP request. Changing this to #collection.get(thisid).destroy() does the trick. But you will still need to create a destroy method in your Rails controller,
The removeEntry event in the Backbone view index,
removeEntry: (e) ->
thisid = #$(e.currentTarget).closest('div').data('id')
#collection.get(thisid).destroy()
The destroy method in the Rails controller,
def destroy
movie = Movie.find_by_id(params[:id])
movie.destroy
respond_to do |format|
format.json {render :json => {:msg => "item deleted!"},:status => 200}
end
end
I've looked around a ton, and can't find an answer to this issue. I'm trying to take a local JSON file, load it up using Backbone.js and render it to a template in the browser. My file downloads, and the template appears, but it is never populated by the data. Any thoughts? Thanks in advance.
HTML
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>People list</title>
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.1.1/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<h1>People list</h1>
<hr />
<div class="page"></div>
</div>
<script type="text/template" id="people-template">
<table class="table striped">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Age</th>
<th>Photo</th>
<th>Video</th>
</tr>
</thead>
<tbody>
<% _.each(PersonCollection, function(Person) { %>
<tr>
<td><%= Person.get("firstName") %></td>
<td><%= Person.get("lastName") %></td>
<td><%= Person.get("age") %></td>
<td><%= Person.get("photo") %></td>
<td><%= Person.get("video") %></td>
</tr>
<% }); %>
</tbody>
</table>
</script>
</body>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.2/jquery.min.js" type="text/javascript"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.2/underscore-min.js" type="text/javascript"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.2/backbone-min.js"></script>
JAVASCRIPT
<script>
// MODEL MODEL MODEL
// MODEL MODEL MODEL
var Person = Backbone.Model;
// COLLECTION COLLECTION COLLECTION
// COLLECTION COLLECTION COLLECTION
var PersonCollection = Backbone.Collection.extend({
model: Person,
url: '/people.json',
parse: function (response) {
return response
}
});
// VIEWS VIEWS VIEWS
// VIEWS VIEWS VIEWS
var About = Backbone.View.extend ({
el: '.page',
render: function () {
var that = this;
var people = new PersonCollection();
people.fetch({
success: function (PersonCollection) {
var template = _.template($('#people-template').html(), {PersonCollection: PersonCollection.models});
that.$el.html(template);
}
})
}
});
var About = new About ();
// ROUTES ROUTES ROUTES
// ROUTES ROUTES ROUTES
var Router = Backbone.Router.extend({
routes: {
'': 'home'
}
});
var router = new Router();
router.on('route:home', function () {
About.render();
});
Backbone.history.start();
</script>
JSON SAMPLE
{
"people": [
{
"firstName": "Jane",
"lastName": "Doe",
"age": "32",
"photo": "test_photo",
"video": "test_video"
},
{
"firstName": "James",
"lastName": "Hamm",
"age": "56",
"photo": "test_photo",
"video": "test_video"
},
Thanks again for any suggestions. I'm new to stackoverflow (first question posted) so let me know if I need to provide more information.
If you don't wan't to modify your JSON file, you could change parse function in your PersonCollection to return you the person array. Example:
var PersonCollection = Backbone.Collection.extend({
model: Person,
url: '/people.json',
parse: function (response) {
// Return people object which is the array from response
return response.people
}
});
Backbone Documentation: http://backbonejs.org/#Model-parse
parse is called whenever a model's data is returned by the server, in fetch, and save. 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. Override this if you need to work with a preexisting API, or
better namespace your responses.
U can directly populate JSON data with your Collection. Collection will take care of populating the data according to the array of JSON objects. In your case if you don't want to change JSON data, i.e. if you want to keep the 'people' attribute as it is, you can populate all data by backbone-relational.js. It helps to work with nested JSON data.
I'm a little lost with my view rendering--first time trying to do this. I have my templates set up similar to this application.
So far I have it rendering the template for the CompositeView but it doesn't render any of the ItemViews. It doesn't even trigger the rendering method to try to debug so I'm not sure where I can log pieces to see where it's getting stuck... Here's the code:
This is my ItemView:
define([
'jquery',
'underscore',
'backbone',
'text!templates/service/item.ejs'
], function($, _, Backbone, template) {
ServiceItemView = Backbone.Marionette.ItemView.extend({
tagName: 'tr',
template: '#service-item-template'
});
}
);
This is my CompositeView: Update: Added in my requirejs code to show that ServiceItemView is loaded before ServiceTableView
define([
'jquery',
'underscore',
'backbone',
'views/service/item',
'text!templates/service/table.ejs'
], function($, _, Backbone, ServiceItemView, template) {
var ServiceTableView;
ServiceTableView = Backbone.Marionette.CompositeView.extend({
tagName: 'table',
id: 'service-table',
itemView: ServiceItemView,
itemViewContainer: 'tbody',
template: '#service-table-template',
appendHtml: function(collectionView, itemView){
console.log("here");
//collectionView.$("tbody").append(itemView.el);
}
});
}
);
Here's where I attempt to render it:
service_collection = new ServiceCollection([
new Service({
name: "Men's Cut",
length: 108000,
price: 2500
}),
new Service({
name: "Women's Cut",
length: 324000,
price: 5000
})
]);
service_table = new ServiceTableView({
collection: service_collection
});
App.main_region.show(service_table);
Update: Here are the two templates:
ServiceItemView Template:
<script type="text/html" id="service-item-template">
<td><%= name %></td>
<td><%= length %></td>
<td><%= price %></td>
<td class="actions">
<input type="button" class="icon" value="Delete" />
</td>
</script>
ServiceTableView Template:
<script type="text/html" id="service-table-template">
<thead>
<tr>
<th>Name</td>
<th>Time allotment</th>
<th>Pricing</th>
<th class="actions">Actions</th>
</tr>
</thead>
<tbody>
</tbody>
</script>
Again, the ServiceTableView template is rendered, but none of the Services are rendered underneath.
Any help is appreciated. Even pointers on where to stick log statements to get more information.
Thanks!
Turns out the Collections I had created were written:
Backbone.Model.extend instead of Backbone.Collection.extend. I must have copied the code from the model when creating the collection to speed up writing it.
Fixed now and working if anyone would like to use the above code for an example for their own projects.