I'm working with Backbone and for some reason I can't seem to show anything with the reset event. The stranger part is that when I trigger the add event it shows all my collection.
I'm using version 0.9.2 of Backbone:
Here's the code:
var TodoView = Backbone.View.extend({
tagName: 'li',
template: _.template($('#item-template').html()),
render: function(){
this.$el.html(this.template(this.model.toJSON()));
return this; // enable chained calls
}
});
var CoursesPageView = Backbone.View.extend({
el: '.page',
initialize: function () {
this.input = this.$('#new-todo');
todoList.on('add', this.addAll2, this);
todoList.on('reset', this.addAll, this);
console.log("getting ready to fetch localstorage");
todoList.fetch(); // Loads list from local storage
// Also triggers reset event
},
render: function(){
console.log("function: render2");
var template = _.template($("#courses").html(),{});
this.$el.html(template);
},
events: {
'keypress #new-todo': 'createTodoOnEnter'
},
createTodoOnEnter: function(e){
if ( e.which !== 13) { // ENTER_KEY = 13
return;
}
console.log("function: createOnEnter");
todoList.create({title: $('#new-todo').val()}); // triggers add event
console.log("passed to localstorage: " + $('#new-todo').val());
$('#new-todo').val(''); // cleans input box once enter is pressed
},
addOne: function(todo){
var view = new TodoView({model: todo});
console.log("show li");
$('#todo-list').append(view.render().el);
},
addAll: function(){
console.log("reset event triggered");
this.$('#todo-list').html(''); // clean the todo list
todoList.each(this.addOne, this);
},
addAll2: function(){
console.log("add event triggered");
this.$('#todo-list').html(''); // clean the todo list
todoList.each(this.addOne, this);
},
});
and for the index.html file part:
<script type="text/template" id="courses">
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<h4 align="center" style="color:white;">Courses</h4>
</div>
</div>
</div>
<div class="container">
Add Course
<input id="new-todo">
<section id="main">
<ul id="todo-list"></ul>
</section>
</div>
</script>
<script type="text/template" id="item-template">
<div class="view">
<label><%- title %></label>
</div>
</script>
Related
I am trying to build backbone.js application with twitter API.
the application that I am working on performs 3 tasks:1-returns the recent tweets on the user 's timeline, 2-returns the user profile 3- provides the ability to search in twitter. my problem is that I want the results of the search functionality to appear in the location of the tweets when the user clicks the search button. in my code there is view to display the tweets and another view to display the results of searching..how to put view in the place of another view on runtime. here is some of the code to explain the idea:
index.html :
<body>
<header role="banner">
<!—some code here-->
</header>
<div id="search" class="inner-search">
<form>
<label>Search for</label>
<input type="search" id="searchbox" style="width: 70%;"
autofocus="" placeholder="I'm looking for.."/>
<button id="searchbutton" style="width: 10%;">Go</button>
</form>
</div><!--search-view-->
<div class="inner-content">
<nav role="navigation">
<ul class="chapter-list">
………
</ul>
</nav>
<div role="main" class="main-content metrouicss">
<div id='timeline' class='timeline-view'>
<h3>Tweets </h3>
</div>
</div><!--main-->
<div role="right" class="right-content">
<h3>My Profile </h3>
<div id="profile" class="profile-view">
<!-- This would be the template -->
</div></div><!--right-->
</div> <!-- /.content-wrapper -->
<footer role="contentinfo">
<div class="inner-footer">
<p class="copyright">© Eva Hriekes, 2015. All rights
reserved.</p>
</div> <!-- /.inner-footer -->
</footer>
<!-- Template for profile -->
<script type="text/x-handlebars-template" id="profile-template">
<div class='tiles clearfix'>
<div class="tile double bg-color-orangeDark">
<div class="tile-content">
<img src="{{user.profile_image_url}}" class="place-left">
<h3 style="margin-bottom: 5px;">{{user.name}}</h3>
<p style="float:left;">{{user.description}}</p>
<div class="brand">
<div class="badge">{{user.followers_count}} Followers</div>
</div>
</div>
</div>
</div>
</script>
<!-- Template for timeline -->
<script type="text/x-handlebars-template" id="timeline-template">
<ul class='listview fluid'>
{{#each tweet}}
<li >
<div class='icon'>
<img src='{{user.profile_image_url}}'></img>
</div>
<div class='data'>
<h4>{{user.name}}</h4>
<p>{{format text}}</p>
<p class="timestamp" style="text-decoration:underline;">
<i>{{friendlyDate}}</i></p>
<p style="font-weight:bold;">Rating:
<i class="fa fa-star-o"></i><i class="fa fa-star-
o"></i><i class="fa fa-star-o"></i><i class="fa fa-star-
o"></i><i class="fa fa-star-o"></i></p>
</div>
</li>
{{/each}}
</ul>
</script>
<!-- Template for search results -->
<script type="text/x-handlebars-template" id="search-template">
<ul class='listview fluid'>
{{#each tweet}}
<li >
<div class='icon'>
<img src='{{user.profile_image_url}}'></img>
</div>
<div class='data'>
<h4>{{user.name}}</h4>
<p>{{format text}}</p>
<p class="timestamp" style="text-decoration:underline;">
<i>{{friendlyDate}}</i></p>
</div>
</li>
{{/each}}
</ul>
</script>
<script data-main="js/main" src="js/vendor/require.js"></script>
</body>
</html>
timeline view:
define(['jquery', 'handlebars', 'backbone', 'app/collection
/Timeline','app/view/ProfilePopupView'],
function($, Handlebars, Backbone, Timeline,ProfilePopupView) {
var com = com || {};
com.apress = com.apress || {};
com.apress.view = com.apress.view || {};
com.apress.view.TimelineView = Backbone.View.extend({
el: '#timeline',
template: Handlebars.compile($("#timeline-template").html()),
timeline: null,
events: {
'click .profile': 'showDialog'
},
initialize: function(options){
var self = this;
//create a collection for this view to render
self.timeline = new Timeline();//new
com.apress.collection.Timeline();
//initial render
self.render();
//force the fetch to fire a reset event
self.timeline.fetch({reset:true
});
self.listenTo(self.timeline, 'reset', self.render);
},
render: function(){
var self = this;
if(self.timeline.models.length > 0){
var output = self.template({tweet: self.timeline.toJSON()});
self.$el.append(output);
}
return self;
},
showDialog: function(options){
var self =this,
$target = $(options.currentTarget),
username = $target.data('user');
/**
* Reuse the profile view
**/
var profileView = new ProfilePopupView({user: username});
}
});
// export stuff:
return com.apress.view.TimelineView;
});
search view:
define(['jquery', 'backbone'], function($, Backbone) {
var com = com || {};
com.apress = com.apress || {};
com.apress.view = com.apress.view || {};
com.apress.view.SearchView = Backbone.View.extend({
el: '#search',
model: null,
events: {
'click #searchbutton': 'runSearch'
},
initialize: function(options){
var self = this;
self.model = options.model;
},
runSearch: function(e){
var self = this;
query = $('#searchbox').val();
e.preventDefault();
console.log('Run search against ' + query);
//force a reset
self.model.set('query', '', {silent: true});
self.model.set('query', query);
}
});
return com.apress.view.SearchView;
});
results view:
define(['jquery', 'backbone', 'handlebars','dialog'], function($,
Backbone, Handlebars,Dialog) {
var com = com || {};
com.apress = com.apress || {};
com.apress.view = com.apress.view || {};
com.apress.view.ResultsView = Backbone.View.extend({
el: '#results', /* or should be el="#timeline"???*/
model: null,
template: Handlebars.compile($("#search-template").html()),
initialize: function(options){
var self = this;
self.model = options.model;
self.model.fetch({
error: function(e){
self.model.trigger("app:error", {message: 'Error
retrieving timeline information'});
},
success: function(e){
self.model.trigger("app:success", {message: 'success
retrieving timeline information'});
}
});
self.listenTo(self.model,'change', self.render);
self.render();
},
render: function(){
console.log('Display now');
var self = this,
output = self.template({tweet: self.model.get('statuses')});
/* I want to delete this code and display the results
in the place of tweets*/
$.Dialog({
'title' : 'Search Results',
'content' : output,
'draggable' : true,
'overlay' : true,
'closeButton' : true,
'buttonsAlign': 'center',
'keepOpened' : true,
'position' : {
'zone' : 'left'
},
'buttons' : {
'OK' : {
'action': function(){}
}
}
});
}
});
return com.apress.view.ResultsView;
});
can anyone help me in doing what I want?
I am converting this http://localtodos.com/ todo example into a address book but it turning out to be harder than I thought. I saving data to localstorage but I can't seem to get it to display the template. In fact it doesn't seem to want to hit the AddressBookView at all. When I hit index in a browser and add a new contact to saves it just wont show the new one or the previously save contacts. Code is below. Thanks for any help.
$(function(){
var AddressBook = Backbone.Model.extend({
defaults: function() {
return {
name: "No contact name provided",
email: "No email address provided"
};
},
});
// Todo Collection
// ---------------
var AddressBookList = Backbone.Collection.extend({
model: AddressBook,
// Save all of the todo items under the `"todos-backbone"` namespace.
// localStorage: new Backbone.LocalStorage("todos-backbone"),
localStorage: new Backbone.LocalStorage("address-book"),
// We keep the Todos in sequential order, despite being saved by unordered
// GUID in the database. This generates the next order number for new items.
nextOrder: function() {
if (!this.length) return 1;
return this.last().get('order') + 1;
},
// Todos are sorted by their original insertion order.
comparator: 'order'
});
// Create our global collection of **Todos**.
var AddressBookEntries = new AddressBookList;
// Todo Item View
// --------------
// The DOM element for a todo item...
var AddressBookView = Backbone.View.extend({
tagName: "li",
// Cache the template function for a single item.
template: _.template($('#item-template').html()),
// The DOM events specific to an item.
events: {
"click .toggle" : "toggleDone",
"dblclick .view" : "edit",
"click a.destroy" : "clear",
"keypress .edit" : "updateOnEnter",
"blur .edit" : "close"
},
// The TodoView listens for changes to its model, re-rendering. Since there's
// a one-to-one correspondence between a **Todo** and a **TodoView** in this
// app, we set a direct reference on the model for convenience.
initialize: function() {
alert("TESST");
this.listenTo(this.model, 'change', this.render);
this.listenTo(this.model, 'destroy', this.remove);
},
// Re-render the titles of the todo item.
render: function() {
console.log("TEST")
this.$el.html(this.template(this.model.toJSON()));
this.input = this.$('.edit');
return this;
},
// Toggle the `"done"` state of the model.
toggleDone: function() {
this.model.toggle();
},
// Switch this view into `"editing"` mode, displaying the input field.
edit: function() {
this.$el.addClass("editing");
this.input.focus();
},
// Close the `"editing"` mode, saving changes to the todo.
close: function() {
var value = this.input.val();
if (!value) {
this.clear();
} else {
this.model.save({email: value});
this.model.save({name: value});
this.$el.removeClass("editing");
}
},
// If you hit `enter`, we're through editing the item.
updateOnEnter: function(e) {
if (e.keyCode == 13) this.close();
},
// Remove the item, destroy the model.
clear: function() {
this.model.destroy();
}
});
// The Application
// ---------------
// Our overall **AppView** is the top-level piece of UI.
var AppView = Backbone.View.extend({
// Instead of generating a new element, bind to the existing skeleton of
// the App already present in the HTML.
el: $("#address-book"),
// Our template for the line of statistics at the bottom of the app.
//statsTemplate: _.template($('#stats-template').html()),
// Delegated events for creating new items, and clearing completed ones.
events: {
"click #save-entry": "createAddressBookEntry",
"click #clear-completed": "clearCompleted",
"click #toggle-all": "toggleAllComplete"
},
// At initialization we bind to the relevant events on the `Todos`
// collection, when items are added or changed. Kick things off by
// loading any preexisting todos that might be saved in *localStorage*.
initialize: function() {
this.input = this.$("#save-entry");
// this.allCheckbox = this.$("#toggle-all")[0];
this.listenTo(AddressBookEntries, 'add', $("#name").addOne);
this.listenTo(AddressBookEntries, 'reset', this.addAll);
this.listenTo(AddressBookEntries, 'all', this.render);
this.footer = this.$('footer');
this.main = $('#main');
AddressBookEntries.fetch();
},
// Re-rendering the App just means refreshing the statistics -- the rest
// of the app doesn't change.
render: function() {
// var done = AddressBookEntries.done().length;
// var remaining = AddressBookEntries.remaining().length;
if (AddressBookEntries.length) {
this.main.show();
this.footer.show();
// this.footer.html(this.statsTemplate({done: done, remaining: remaining}));
} else {
this.main.hide();
this.footer.hide();
}
// this.allCheckbox.checked = !remaining;
},
// Add a single todo item to the list by creating a view for it, and
// appending its element to the `<ul>`.
addOne: function(entry) {
console.log("Second This" + this);
var view = new AddressBookView({model: entry});
this.$("#contact-list").append(view.render().el);
},
// Add all items in the **Todos** collection at once.
addAll: function() {
AddressBookEntries.each(this.addOne, this);
},
// If you hit return in the main input field, create new **Todo** model,
// persisting it to *localStorage*.
createAddressBookEntry: function(e) {
AddressBookEntries.create({name: $("#name").val()});
AddressBookEntries.create({email: $("#email").val()});
$("#email").val('');
$("#name").val('');
},
});
// Finally, we kick things off by creating the **App**.
var App = new AppView;
});
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Backbone.js Todos</title>
<link rel="stylesheet" href="css/bootstrap.min.css" type="text/css" media="screen" title="no title" charset="utf-8">
<link rel="stylesheet" href="css/narrowJumbotron.css" type="text/css" media="screen" title="no title" charset="utf-8">
<link rel="stylesheet" href="css/app.css" type="text/css" media="screen" title="no title" charset="utf-8">
</head>
<body>
<div class="container">
<div class="header">
<ul class="nav nav-pills pull-right">
<li class="active">Home</li>
<li>About</li>
<li>Contact</li>
</ul>
<h3 class="text-muted">Address Book</h3>
</div>
<div class="jumbotron">
<h1>Jumbotron heading</h1>
<p class="lead"></p>
<p><a class="btn btn-lg btn-success" href="#">Sign up today</a></p>
</div>
<div class="row marketing">
<div class="col-lg-6">
<div id="address-book">
<header>
<input id="name" type="text" placeholder="Name">
<input id="email" type="text" placeholder="Email">
<a id="save-entry" class="btn btn-lg btn-success" href="#">Save</a>
</header>
<section id="main">
<ul id="contact-list"></ul>
</section>
</div>
</div>
</div>
</div>
<script src="js/jquery-1.10.1.min.js" type="text/javascript" charset="utf-8"></script>
<script src="js/underscore.js" type="text/javascript" charset="utf-8"></script>
<script src="js/backbone.js" type="text/javascript" charset="utf-8"></script>
<script src="js/backbone.localStorage-min.js" type="text/javascript" charset="utf-8"></script>
<script src="js/app.js" type="text/javascript" charset="utf-8"></script>
<!-- Templates -->
<script type="text/template" id="item-template">
<div class="view">
// <input class="toggle" type="checkbox" <%= done ? 'checked="checked"' : '' %> />
<label><%- name %></label>
<a class="destroy"></a>
</div>
<input class="edit" type="text" value="<%- name %>" />
</script>
</body>
</html>
You say
this.listenTo(AddressBookEntries, 'add', $("#name").addOne);
I think you meant
this.listenTo(AddressBookEntries, 'add', this.addOne);
You would also like to save model like this
this.model.save({email: value, name: value});
otherwise it will add unnecessary rendering and will fire change event twice.
I have a #root DOM object with many .element children:
<div id='root'>
<div class='element'>
<span>Element 1</span>
</div>
<div class='element'>
<span>Element 2</span>
</div>
<div class='element'>
<span>Element 3</span>
</div>
</div>
I want a Backbone View instance for every .element. This is what I have tried:
var ElementView = Backbone.View.extend({
initialize: function () {
_.bindAll(this, 'showText');
this.setElement('.element');
},
events: {
'click span': 'showText'
},
showText: function () {
console.log('You have clicked ' + $el.text());
}
});
I initialize my views as follows:
new ElementView({ model: modelInstance });
The problem is that .element is not precise enough, $el will be $('.element')[0] for every initialisation. How should I deal with the situation where many elements have the same CSS selector?
Initialize your views with their associated elements :
$(".element").each(function(ix,el) {
new ElementView({ model: modelInstance , el:el });
});
Dont' forget to remove this.setElement('.element'); in the initialize code.
I can't get this thing to add when i click the add button. I'm completely new to this.
JS code
function RecipeApp() {
var _Ingredient=Backbone.Model.extend(COOKBOOK.Domain.Ingredient),
_Ingredients = Backbone.Collection.extend({
model: _Ingredient
}),
_IngredientView = Backbone.View.extend({
tagName: "li",
initialize: function () {
this.model.bind("change", this.render, this);
},
render: function () {
var templateid = this.model.get('ViewID');
$(this.el).html(Mustache.to_html($("#"+templateid).html(),this));
}
}),
_AddView = Backbone.View.extend({
id:"divAddIngredient",
events: {
"click .btn": "create"
},
render: function () {
var tmpAddAnIngredient = $("#tmpMasterView").html(),
$submain = $("#submain");
$submain.html(Mustache.to_html(tmpAddAnIngredient, COOKBOOK.Domain));
},
initialize: function (ingredients) {
console.log("init enter");
this.render();
this._Ingredients = ingredients;
this._Ingredients.bind('add', this.add, this);
console.log("init leave");
},
//added functions
create: function () {
console.log("create");
var typename = this.$(".typeName").val(),
ingredient = _.detect(COOKBOOK.Domain.Ingredients, function (i) { i.TypeName === typename });
if (!!ingredient) {
this._Ingredients.create(ingredient);
}
},
add: function (ingredient) {
console.log('add');
var view = new _IngredientView(ingredient);
this.$("#divIngredients").append(view.render().el);
}
});
this.Ingredients = new _Ingredients();
this.AddView = new _AddView(this.Ingredients);
}
$(function () {
window.app = new RecipeApp();
//
});
And here is the mustache template
<script id="tmpTempDirectoryIngredient" type="text/html">
<div class="block-message">
<form>
<fieldset>
<legend>Create a Temporary Directory</legend>
<div class="clearfix">
<input type="text" name="DirectoryName" class="DirectoryName" />
</div>
</fieldset>
</form>
</div>
</script>
<script id="tmpMasterView" type="text/html">
<div class="block-message info" id="divAddIngredient">
<form>
<fieldset>
<legend>Add an Ingredient</legend>
<div class="clearfix">
<select class="typeName">
{{#Ingredients}}
<option value="{{TypeName}}">{{Name}}</option>
{{/Ingredients}}
</select>
</div>
<div class="clearfix">
<input type="button" class="btn primary" value="Add Ingredient" />
</div>
</fieldset>
</form>
</div>
<hr />
<div id="divIngredients">
</div>
</script>
it started working as soon as i explicitly set the el property of the _AddView to a tag that existed when the _AddView was created.
$(function(){
new Apps({el:$("body"),'records':[1,2,3,4,5]});
});
Here need to give el.
because of only after DOM is generating.....
The way you are passing Ingredients to the _AddView initialize, they will be accessible by this.options (see http://documentcloud.github.com/backbone/#View-constructor).
I think a better way is pass your ingredients collection into you _AddView like this:
this.AddView = new _AddView({collection: this.Ingredients});
Then within your definition of your view, always refer to this.collection instead of this._Ingredients. That is I think a more standard way to do it.
my backbone view looks like this below and is rendered with the jQuery tmpl library. I want to apply a style to one/all/any of the data items for which
active==1. Any ideas on how to do this?
// backbone view
window.CaseView = Backbone.View.extend({
el: $("#main"),
initialize: function() {
_.bindAll(this, 'render');
this.render();
},
iTemplate: $("#tmplCase").template(),
render: function() {
var that = this;
that.el.fadeOut('fast', function() {
$.tmpl(that.iTemplate, that.model.toJSON()).appendTo(that.el);
that.el.fadeIn('fast');
});
return this;
}
});
// html file
<div id="main"></div>
<script id="tmplCase" type="text/x-jquery-tmpl">
<div class="caseInActive">
<span class="title">${title}</span>
<span class="current_status">${active}</span>
</div>
</script>
you can add if statements to your template:
// html file
<script id="tmplCase" type="text/x-jquery-tmpl">
<div {{if active == 1}}class="caseInActive"{{/if}}>
<span class="title">${title}</span>
<span class="current_status">${active}</span>
</div>
</script>
http://api.jquery.com/template-tag-if/