I have followed a tutorial (source code) and everything works great but I have one issue, the sorting works but how do I add another sorting option?
For example I have this
server_api: {
'per_page': function() { return this.perPage },
'page': function() { return this.currentPage },
'year': function() {
if (this.sortField === undefined)
return '2016';
return this.sortField;
}
},
So I can sort my API using year, but my API can also accept another parameter like sort_by.
So I added this under year:
'sort_by': function() {
if(this.sortField === undefined)
return 'title.desc';
return this.sortField;
}
Now every time I click the 'year button', it sorts based on year example:
sort_by=title.desc&year=2016
sort_by=title.desc&year=2011
but if click on sort_by button, it's changing the value of year, instead of sort_by example:
sort_by=title.desc&year=popularity.asc
My full code:
<script type="text/html" id="sortingTemplate">
<div class="form-group">
<div class="col-sm-3">
<div class="btn-group">
<button data-toggle="dropdown" class="btn btn-default dropdown-toggle">Year <strong><span id="sortByYear">2016</span></strong> <span class="caret"></span></button>
<ul class="dropdown-menu" id="year">
<li>2016</li>
<li>2015</li>
<li>2014</li>
</ul>
</div>
</div>
<div class="col-sm-3">
<div class="btn-group">
<button data-toggle="dropdown" class="btn btn-default dropdown-toggle">Sort by <strong><span id="sortBy">2016</span></strong> <span class="caret"></span></button>
<ul class="dropdown-menu" id="sort_by">
<li>Popularity Descending</li>
<li>Popularity Ascending</li>
<li>Rating Descending</li>
<li>Rating Ascending</li>
<li>Release Date Descending</li>
<li>Release Date Ascending</li>
<li>Title (A-Z)</li>
<li>Title (Z-A)</li>
</ul>
</div>
</div>
</div>
</script>
<script>
window.myapp = {};
myapp.collections = {};
myapp.models = {};
myapp.views = {};
myapp.serverURL = '{{url("/")}}';
myapp.models.Item = Backbone.Model.extend({});
myapp.collections.PaginatedCollection = Backbone.Paginator.requestPager.extend({
model: myapp.models.Item,
paginator_core: {
dataType: 'json',
url: '{{ route('api.discover.movie') }}'
},
paginator_ui: {
firstPage: 1,
currentPage: 1,
perPage: 20,
totalPages: 10
},
server_api: {
'per_page': function() { return this.perPage },
'page': function() { return this.currentPage },
'year': function() {
if(this.sortField === undefined)
return '2016';
return this.sortField;
},
'sort_by': function(){
if (this.sortField2 === undefined)
return 'title.desc';
return this.sortField2;
}
},
parse: function (response) {
$('#movies-area').spin(false);
this.totalRecords = response.total;
this.totalPages = Math.ceil(response.total / this.perPage);
return response.data;
}
});
myapp.views.ItemView = Backbone.View.extend({
tagName: 'div',
className: 'col-lg-2',
template: _.template($('#MovieItemTemplate').html()),
initialize: function() {
this.model.bind('change', this.render, this);
this.model.bind('remove', this.remove, this);
},
render : function () {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
myapp.views.SortedView = Backbone.View.extend({
events: {
'click #year a': 'updateYear',
'click #sort_by': 'updateSortBy'
},
template: _.template($('#sortingTemplate').html()),
initialize: function () {
this.collection.on('reset', this.render, this);
this.collection.on('sync', this.render, this);
this.$el.appendTo('#discover');
},
render: function () {
var html = this.template(this.collection.info());
this.$el.html(html);
if (this.collection.sortField == undefined){
var sortYearText = this.$el.find('#sortByYear').text();
}else{
var sortYearText = this.collection.sortField;
}
$('#sortByYear').text(sortYearText);
if (this.collection.sortField2 == undefined){
var sortByText = this.$el.find('#sortBy').text();
}else{
var sortByText = this.collection.sortField2;
}
$('#sortBy').text(sortByText);
},
updateYear: function (e) {
e.preventDefault();
var currentYear = $(e.target).attr('href');
this.collection.updateOrder(currentYear);
$('#movies-area').spin();
},
updateSortBy: function (e) {
e.preventDefault();
var currentSort = $(e.target).attr('href');
this.collection.updateOrder(currentSort);
$('#movies-area').spin();
}
});
myapp.views.PaginatedView = Backbone.View.extend({
events: {
'click button.prev': 'gotoPrev',
'click button.next': 'gotoNext',
'click a.page': 'gotoPage'
},
template: _.template($('#paginationTemplate').html()),
initialize: function () {
this.collection.on('reset', this.render, this);
this.collection.on('sync', this.render, this);
this.$el.appendTo('#pagination');
},
render: function () {
var html = this.template(this.collection.info());
this.$el.html(html);
},
gotoPrev: function (e) {
e.preventDefault();
$('#movies-area').spin();
this.collection.requestPreviousPage();
},
gotoNext: function (e) {
e.preventDefault();
$('#movies-area').spin();
this.collection.requestNextPage();
},
gotoPage: function (e) {
e.preventDefault();
$('#movies-area').spin();
var page = $(e.target).text();
this.collection.goTo(page);
}
});
myapp.views.AppView = Backbone.View.extend({
el : '#paginated-content',
initialize : function () {
$('#movies-area').spin();
var items = this.collection;
items.on('add', this.addOne, this);
items.on('all', this.render, this);
items.pager();
},
addOne : function ( item ) {
var view = new myapp.views.ItemView({model:item});
$('#paginated-content').append(view.render().el);
}
});
$(function(){
myapp.collections.paginatedItems = new myapp.collections.PaginatedCollection();
myapp.views.app = new myapp.views.AppView({collection: myapp.collections.paginatedItems});
myapp.views.pagination = new myapp.views.PaginatedView({collection:myapp.collections.paginatedItems});
myapp.views.sorting = new myapp.views.SortedView({collection:myapp.collections.paginatedItems});
});
</script>
Quickfix
Change the values directly in the view's events callbacks:
updateYear: function(e) {
e.preventDefault();
var currentYear = $(e.target).attr('href');
this.collection.sortField = currentYear;
$('#movies-area').spin();
},
updateSortBy: function(e) {
e.preventDefault();
var currentSort = $(e.target).attr('href');
this.collection.sortField2 = currentSort;
$('#movies-area').spin();
}
Better way
Name things with what they represent and encapsulate the logic.
In the collection, offer clearly named setters.
server_api: {
/* ...snip... */
'year': function() {
return this.year || '2016';
},
'sort_by': function() {
return this.sortField || 'title.desc';
}
},
setYearFilter: function(value) {
if (value !== undefined) {
this.year = value;
return this.pager(options);
}
return reject();
},
And use them in the view:
updateYear: function(e) {
e.preventDefault();
var currentYear = $(e.target).attr('href');
this.collection.setYearFilter(currentYear)
$('#movies-area').spin();
},
updateSortBy: function(e) {
e.preventDefault();
var currentSort = $(e.target).attr('href');
this.collection.updateOrder(currentSort);
$('#movies-area').spin();
}
Best way
Update to the latest version of backbone.paginator, not that it will solve the problem directly, but it'll be easier to find help and documentation. Also, additional features!
Related
How can I make a To-Do List, and at 5 elements checked to show a button?
I have already done the To-Do list (in HTML and JavaScript - my code below ), but I don't know how can I write a code to show a button at 5 elements checked.
My JSFiddle:
https://jsfiddle.net/sd355qxp
My code (in HTML and JavaScript) :
<html>
<head>
<link rel="stylesheet" href="tomo.css">
<title>TOMO</title>
</head>
<body>
<h1>TOMO</h1>
<center>
<div id="todo-app">
<label class="todo-label" for="new-todo">What do you have to do today?</label>
<input type="text" id="new-todo" class="todo-input" placeholder="english homework">
<ul id="todo-list" class="count-this"></ul>
<div id="todo-stats"></div>
</div>
</center>
<script type="text/x-template" id="todo-item-template">
<div class="todo-view">
<input type="checkbox" class="todo-checkbox" {checked}>
<span class="todo-content" tabindex="0">{text}</span>
</div>
<div class="todo-edit">
<input type="text" class="todo-input" value="{text}">
</div>
<a href="#" class="todo-remove" title="Remove this task">
<span class="todo-remove-icon"></span>
</a>
</script>
<script type="text/x-template" id="todo-stats-template">
<span class="todo-count">
<span class="todo-remaining">{numRemaining}</span>
<span class="todo-remaining-label">{remainingLabel}</span> left.
</span>
<a href="#" class="todo-clear">
Clear <span class="todo-done">{numDone}</span>
completed <span class="todo-done-label">{doneLabel}</span>
</a>
</script>
<script src="http://yui.yahooapis.com/3.18.1/build/yui/yui-min.js"></script>
<script>
YUI().use('event-focus', 'json', 'model', 'model-list', 'view', function (Y) {
var TodoAppView, TodoList, TodoModel, TodoView;
TodoModel = Y.TodoModel = Y.Base.create('todoModel', Y.Model, [], {
sync: LocalStorageSync('todo'),
toggleDone: function () {
this.set('done', !this.get('done')).save();
}
}, {
ATTRS: {
done: {value: false},
text: {value: ''}
}
});
TodoList = Y.TodoList = Y.Base.create('todoList', Y.ModelList, [], {
model: TodoModel,
sync: LocalStorageSync('todo'),
done: function () {
return this.filter(function (model) {
return model.get('done');
});
},
remaining: function () {
return this.filter(function (model) {
return !model.get('done');
});
}
});
TodoAppView = Y.TodoAppView = Y.Base.create('todoAppView', Y.View, [], {
events: {
'#new-todo': {keypress: 'createTodo'},
'.todo-clear': {click: 'clearDone'},
'.todo-item': {
mouseover: 'hoverOn',
mouseout : 'hoverOff'
}
},
template: Y.one('#todo-stats-template').getHTML(),
initializer: function () {
var list = this.todoList = new TodoList();
list.after('add', this.add, this);
list.after('reset', this.reset, this);
list.after(['add', 'reset', 'remove', 'todoModel:doneChange'],
this.render, this);
list.load();
},
render: function () {
var todoList = this.todoList,
stats = this.get('container').one('#todo-stats'),
numRemaining, numDone;
if (todoList.isEmpty()) {
stats.empty();
return this;
}
numDone = todoList.done().length;
numRemaining = todoList.remaining().length;
stats.setHTML(Y.Lang.sub(this.template, {
numDone : numDone,
numRemaining : numRemaining,
doneLabel : numDone === 1 ? 'task' : 'tasks',
remainingLabel: numRemaining === 1 ? 'task' : 'tasks'
}));
if (!numDone) {
stats.one('.todo-clear').remove();
}
return this;
},
add: function (e) {
var view = new TodoView({model: e.model});
this.get('container').one('#todo-list').append(
view.render().get('container')
);
},
clearDone: function (e) {
var done = this.todoList.done();
e.preventDefault();
this.todoList.remove(done, {silent: true});
Y.Array.each(done, function (todo) {
todo.destroy({remove: true});
});
this.render();
},
createTodo: function (e) {
var inputNode, value;
if (e.keyCode === 13) { // enter key
inputNode = this.get('inputNode');
value = Y.Lang.trim(inputNode.get('value'));
if (!value) { return; }
this.todoList.create({text: value});
inputNode.set('value', '');
}
},
hoverOff: function (e) {
e.currentTarget.removeClass('todo-hover');
},
hoverOn: function (e) {
e.currentTarget.addClass('todo-hover');
},
reset: function (e) {
var fragment = Y.one(Y.config.doc.createDocumentFragment());
Y.Array.each(e.models, function (model) {
var view = new TodoView({model: model});
fragment.append(view.render().get('container'));
});
this.get('container').one('#todo-list').setHTML(fragment);
}
}, {
ATTRS: {
container: {
valueFn: function () {
return '#todo-app';
}
},
inputNode: {
valueFn: function () {
return Y.one('#new-todo');
}
}
}
});
TodoView = Y.TodoView = Y.Base.create('todoView', Y.View, [], {
containerTemplate: '<li class="todo-item"/>',
events: {
'.todo-checkbox': {click: 'toggleDone'},
'.todo-content': {
click: 'edit',
focus: 'edit'
},
'.todo-input' : {
blur : 'save',
keypress: 'enter'
},
'.todo-remove': {click: 'remove'}
},
template: Y.one('#todo-item-template').getHTML(),
initializer: function () {
var model = this.get('model');
model.after('change', this.render, this);
model.after('destroy', function () {
this.destroy({remove: true});
}, this);
},
render: function () {
var container = this.get('container'),
model = this.get('model'),
done = model.get('done');
container.setHTML(Y.Lang.sub(this.template, {
checked: done ? 'checked' : '',
text : model.getAsHTML('text')
}));
container[done ? 'addClass' : 'removeClass']('todo-done');
this.set('inputNode', container.one('.todo-input'));
return this;
},
edit: function () {
this.get('container').addClass('editing');
this.get('inputNode').focus();
},
enter: function (e) {
if (e.keyCode === 13) {
Y.one('#new-todo').focus();
}
},
remove: function (e) {
e.preventDefault();
this.constructor.superclass.remove.call(this);
this.get('model').destroy({'delete': true});
},
save: function () {
this.get('container').removeClass('editing');
this.get('model').set('text', this.get('inputNode').get('value')).save();
},
toggleDone: function () {
this.get('model').toggleDone();
}
});
function LocalStorageSync(key) {
var localStorage;
if (!key) {
Y.error('No storage key specified.');
}
if (Y.config.win.localStorage) {
localStorage = Y.config.win.localStorage;
}
var data = Y.JSON.parse((localStorage && localStorage.getItem(key)) || '{}');
function destroy(id) {
var modelHash;
if ((modelHash = data[id])) {
delete data[id];
save();
}
return modelHash;
}
function generateId() {
var id = '',
i = 4;
while (i--) {
id += (((1 + Math.random()) * 0x10000) | 0)
.toString(16).substring(1);
}
return id;
}
function get(id) {
return id ? data[id] : Y.Object.values(data);
}
function save() {
localStorage && localStorage.setItem(key, Y.JSON.stringify(data));
}
function set(model) {
var hash = model.toJSON(),
idAttribute = model.idAttribute;
if (!Y.Lang.isValue(hash[idAttribute])) {
hash[idAttribute] = generateId();
}
data[hash[idAttribute]] = hash;
save();
return hash;
}
return function (action, options, callback) {
var isModel = Y.Model && this instanceof Y.Model;
switch (action) {
case 'create': // intentional fallthru
case 'update':
callback(null, set(this));
return;
case 'read':
callback(null, get(isModel && this.get('id')));
return;
case 'delete':
callback(null, destroy(isModel && this.get('id')));
return;
}
};
}
new TodoAppView();
});
</script>
</body>
</html>
Can you use jQuery?
$(".todo-checkbox").change(function(){
if($(".todo-checkbox:checked").length > 4){
$("#yourButton").show();
}
});
UPDATE: I've updated my views to show how I resolved this question using information from the accepted answer.
I'd like to update/increment an attribute ('video_views') of my Backbone JS model via a click event from my view. But, as a Backbone rookie, I'm not sure how to accomplish this exactly.
I'd like the 'video_views' attribute to increment by one with the playVideo event (click).
Thanks for the help!
Here is the structure of my JSON from my API:
{
"id": 8,
"name": "Bike to work day",
"slug": "bike-work-day",
"tagline": "A brief tagline about the video.",
"description": "This is a test.",
"created": "2015-02-06T15:22:26.342658Z",
"website": "http://thevariable.com/",
"logo": "http://dev.thevariable.com/media/brands/logos/test_logo.jpeg",
"video": "http://dev.thevariable.com/media/brands/videos/3D463BC3-38B8-4A6F-BE93-3F53E918EC3B-3533-00000118880074BA_1.1.mp4",
"video_thumbnail": "http://dev.thevariable.com/media/brands/video_thumbnails/3D463BC3-38B8-4A6F-BE93-3F53E918EC3B-3533-00000118880074BA_1.1.mp4.jpg",
"links": {
"self": "http://dev.thevariable.com/api/brands/bike-work-day"
},
"status_display": "published",
"video_views": 0
}
Here are my Backbone views:
var TemplateView = Backbone.View.extend({
templateName: '',
initialize: function () {
this.template = _.template($(this.templateName).html());
},
render: function () {
var context = this.getContext(), html = this.template(context);
this.$el.html(html);
},
getContext: function () {
return {};
}
});
var HomePageView = TemplateView.extend({
templateName: '#home-template',
events: {
'click video': 'updateCounter',
'click .video video': 'playVideo',
'click .sound': 'muteVideo',
'click .js-open-card': 'openCard'
},
initialize: function (options) {
var self = this;
TemplateView.prototype.initialize.apply(this, arguments);
app.collections.ready.done(function () {
app.brands.fetch({success: $.proxy(self.render, self)});
});
},
getContext: function () {
return {brands: app.brands || null};
},
updateCounter: function (e) {
var id = $(e.currentTarget).data('id');
var item = self.app.brands.get(id);
var views = item.get('video_views');
var video = this.$('.video video');
// Only update the counter if the video is in play state
if (video.prop('paused')) {
item.save({video_views: views + 1}, {patch: true});
this.render();
}
},
playVideo: function () {
var video = this.$('.video video');
if (video.prop('paused')) {
video[0].play();
} else {
video.get(0).pause();
}
},
muteVideo: function (e) {
e.preventDefault();
var video = this.$el.parent().find('video');
video.prop('muted', !video.prop('muted'));
this.$('.sound').toggleClass('is-muted');
},
openCard: function (e) {
e.preventDefault();
this.$el.toggleClass('is-open');
this.$el.closest('.card-container').toggleClass('is-open');
}
});
And my Backbone models:
var BaseModel = Backbone.Model.extend({
url: function () {
var links = this.get('links'),
url = links && links.self;
if (!url) {
url = Backbone.Model.prototype.url.call(this);
}
return url;
}
});
app.models.Brand = BaseModel.extend({
idAttributemodel: 'slug'
});
var BaseCollection = Backbone.Collection.extend({
parse: function (response) {
this._next = response.next;
this._previous = response.previous;
this._count = response.count;
return response.results || [];
},
getOrFetch: function (id) {
var result = new $.Deferred(),
model = this.get(id);
if (!model) {
model = this.push({id: id});
model.fetch({
success: function (model, response, options) {
result.resolve(model);
},
error: function (model, response, options) {
result.reject(model, response);
}
});
} else {
result.resolve(model);
}
return result;
}
});
app.collections.ready = $.getJSON(app.apiRoot);
app.collections.ready.done(function (data) {
app.collections.Brands = BaseCollection.extend({
model: app.models.Brand,
url: data.brands
});
app.brands = new app.collections.Brands();
});
Just increment that attribute on the model and save it.
var views = model.get('video_views');
model.set({video_views: views + 1});
model.save();
I have this bb app that I'm trying to search and return the results of the search, then when cleared, so all results again. I was able to get everything to show before adding the search feature, but now nothing showing up. I think the collection isn't available at the time it's trying to populate, but can't seem to get it to wait. I've tried moving the fetch around to no avail. Any help would be greatly appreciate. For the sake of ease, I've put everything in a fiddle that can be found here...
//Campaign Model w defaults
app.model.Campaign = Backbone.Model.extend({
default: {
title: '',
img: '',
id: '',
slug: '',
image_types: 'small',
tagline: ''
}
});
//Campaign Collection from model
app.collection.Campaign = Backbone.Collection.extend({
//our URL we're fetching from
url: 'https://api.indiegogo.com/1/campaigns.json?api_token=e377270bf1e9121da34cb6dff0e8af52a03296766a8e955c19f62f593651b346',
parse: function(response) {
console.log('parsing...');
return response.response; //get array from obj to add to collection based on model
},
currentStatus: function(status){
return _(this.filter(function(data){
console.log('currentStats', status);
return data.get('_pending') == status;
}));
},
search: function(searchVal) {
console.log('search...');
if (searchVal == '') {
return this;
}
var pattern = new RegExp(searchVal, 'gi');
return _(this.filter(function(data) {
return pattern.test(data.get('title'));
}));
}
});
app.collection.campaigns = new app.collection.Campaign();
app.collection.campaigns.fetch({
success: function(){
console.log('Success...');
var sHeight = window.screen.availHeight - 200 + 'px';
$('#container ul').css('height', sHeight);
},
error: function() {
console.log('error ',arguments);
}
});
//List view for all the campaigns
app.view.CampaignList = Backbone.View.extend({
events: {
'keyup #searchBox': 'search'
},
render: function(data) {
console.log('campaignList',$(this.el).html(this.template));
$(this.el).html(this.template);
return this;
},
renderAll: function(campaigns) {
console.log('campList renderAll', campaigns, $('#campaignList'));
$('#campaignList').html('');
campaigns.each(function(campaign){
var view = new app.view.CampaignItem({
model: campaign,
collection: this.collection
});
console.log(view);
$('#campaignList').append(view.render().el);
});
return this;
},
initialize: function() {
console.log('init campList',app);
this.template = _.template($('#campaignList-tmp').html());
this.collection.bind('reset', this.render, this);
},
search: function(e) {
console.log('listView search');
var searchVal = $('#searchBox').val();
this.renderAll(this.collection.search(searchVal));
},
sorts: function() {
var status = $('#campSorting').find('option:selected').val();
if(status == '') {
status = false;
};
this.renderAll(this.collection.currentStatus(status));
}
});
//Item view for single campaign
app.view.CampaignItem = Backbone.View.extend({
events: {},
render: function(data){
console.log('campItem render...', data);
this.$el.html(this.template(this.model.toJSON()));
return this;
},
initialize: function(){
console.log('campItem init');
this.template = _.template( $('#campaignItem-tmp').html());
}
});
//Router
app.router.Campaign = Backbone.Router.extend({
routes: {
'': 'campaigns'
},
campaigns: function(){
this.campListView = new app.view.CampaignList({
collection: app.collection.campaigns
});
$('#container').append(this.campListView.render().el);
this.campListView.sorts();
}
});
app.router.campaigns = new app.router.Campaign();
Backbone.history.start();
http://jsfiddle.net/skipzero/xqvrpyx8/
Now Solved - See bottom....
I've got a Backbone list view with a button on it that should show the edit elements.
Neither the jQuery hide() call in the 'showAddEntry' function or the view rendering for 'versionEditView' are doing anything at all. I've stepped right through and I'm not getting any errors. I've even tried manually running methods in the console to see what's going on with hide, but I'm not getting anywhere.
Here's the main view...
define(['ministry', 'jquery', 'models/m-version-info', 'views/about/v-edit-version-info-entry', 'text!templates/version-info/version-info.html'],
function(Ministry, $, VersionInfo, VersionInfoEditView, TemplateSource) {
var versionInfoEntriesView = Ministry.View.extend({
el: '#mainAppArea',
template: Handlebars.compile(TemplateSource),
versionInfoEditView: null,
initialize: function () {
this.$addEntryArea = $('#addVersionInfoEntryArea');
this.$addEntryButton = $('#addVersionInfoEntryButton');
},
events: {
'click #addVersionInfoEntryButton': 'showAddEntry'
},
render: function () {
var that = this;
var entries = new VersionInfo.Collection();
entries.fetch({
success: function (data) {
that.$el.html(that.template({ items: data.toJSON() }));
}
});
return this;
},
showAddEntry: function() {
if (this.versionInfoEditView != null) {
this.versionInfoEditView.trash();
}
this.versionInfoEditView = new VersionInfoEditView({ el: this.$addEntryArea });
this.$addEntryButton.hide();
this.versionInfoEditView.render();
return false;
}
});
return versionInfoEntriesView;
});
And here's the child view...
define(['ministry', 'models/m-version-info', 'text!templates/version-info/edit-version-info- entry.html', 'jquery.custom'],
function (Ministry, VersionInfo, TemplateSource) {
var editVersionInfoView = Ministry.View.extend({
template: Handlebars.compile(TemplateSource),
initialize: function () {
this.$dbVersionInput = this.$('#dbVersion');
this.$tagInput = this.$('#tag');
},
render: function () {
this.$el.html(this.template());
return this;
},
events: {
'submit .edit-version-info-form': 'saveEntry'
},
saveEntry: function() {
var entry = new VersionInfo.Model({ dbVersion: this.$dbVersionInput.val(), tag: this.$tagInput.val() });
entry.save({
success: function() {
alert('Your item has been saved');
}
});
return false;
}
});
return editVersionInfoView;
});
And the main template...
<h2>Version Info</h2>
<div id="info">
<a id="addVersionInfoEntryButton" href="#/versioninfo">Add manual entry</a>
<div id="addVersionInfoEntryArea">
</div>
<ul id="items">
{{#each items}}
<li>{{dbVersion}} | {{tag}}</li>
{{/each}}
</ul>
</div>
And the edit template...
<form class="edit-version-info-form">
<h3>Create a new entry</h3>
<label for="dbVersion">DB Version</label>
<input type="text" id="dbVersion" maxlength="10" />
<label for="tag">Tag</label>
<input type="text" id="tag" />
<button type="submit" id="newEntryButton">Create</button>
</form>
I'm fairly new to backbone so I may well be doing something totally wrong, but I can't see anything wrong with the approach so far and it's not throwing any errors.
OK - Fix as follows after some facepalming...
define(['ministry', 'jquery', 'models/m-version-info', 'views/about/v-edit-version-info-entry', 'text!templates/version-info/version-info.html'],
function(Ministry, $, VersionInfo, VersionInfoEditView, TemplateSource) {
var versionInfoEntriesView = Ministry.View.extend({
el: '#mainAppArea',
template: Handlebars.compile(TemplateSource),
versionInfoEditView: null,
$addEntryArea: undefined,
$addEntryButton: undefined,
initialize: function () {
},
events: {
'click #addVersionInfoEntryButton': 'showAddEntry'
},
render: function () {
var that = this;
var entries = new VersionInfo.Collection();
entries.fetch({
success: function (data) {
that.$el.html(that.template({ items: data.toJSON() }));
that.$addEntryArea = that.$('#addVersionInfoEntryArea');
that.$addEntryButton = that.$('#addVersionInfoEntryButton');
}
});
return this;
},
showAddEntry: function (e) {
e.preventDefault();
if (this.versionInfoEditView != null) {
this.versionInfoEditView.trash();
}
this.versionInfoEditView = new VersionInfoEditView({ el: this.$addEntryArea });
this.$addEntryButton.hide();
this.$addEntryArea.append('Do I want to put it here?');
this.versionInfoEditView.render();
}
});
return versionInfoEntriesView;
});
The issue was due to the fact that I was setting the internal element variables within the view before the completion of the render, so the elements were linked up to nothing. I resolved this by extracting the element initiation to the end of the render success callback.
Here's the fix again...
define(['ministry', 'jquery', 'models/m-version-info', 'views/about/v-edit-version-info-entry', 'text!templates/version-info/version-info.html'],
function(Ministry, $, VersionInfo, VersionInfoEditView, TemplateSource) {
var versionInfoEntriesView = Ministry.View.extend({
el: '#mainAppArea',
template: Handlebars.compile(TemplateSource),
versionInfoEditView: null,
$addEntryArea: undefined,
$addEntryButton: undefined,
initialize: function () {
},
events: {
'click #addVersionInfoEntryButton': 'showAddEntry'
},
render: function () {
var that = this;
var entries = new VersionInfo.Collection();
entries.fetch({
success: function (data) {
that.$el.html(that.template({ items: data.toJSON() }));
that.$addEntryArea = that.$('#addVersionInfoEntryArea');
that.$addEntryButton = that.$('#addVersionInfoEntryButton');
}
});
return this;
},
showAddEntry: function (e) {
e.preventDefault();
if (this.versionInfoEditView != null) {
this.versionInfoEditView.trash();
}
this.versionInfoEditView = new VersionInfoEditView({ el: this.$addEntryArea });
this.$addEntryButton.hide();
this.$addEntryArea.append('Do I want to put it here?');
this.versionInfoEditView.render();
}
});
return versionInfoEntriesView;
});
The issue was due to the fact that I was setting the internal element variables within the view before the completion of the render, so the elements were linked up to nothing. I resolved this by extracting the element initiation to the end of the render success callback.
Just beginning with backbone and after few hours can't seem to get even a view render working correctly. I've included all appropriate JavaScript files in HTML. Here is my script:
(function($) {
// MODELS
var Paper = Backbone.Model.extend ({
defaults : {
title : null,
author: null,
}
});
// COLLECTIONS
var PaperCollection = Backbone.Collection.extend({
model : Paper,
initialize : function() {
console.log("We've created our collection");
}
});
// VIEWS
var PaperView = Backbone.View.extend({
tagName:'li',
className: 'resultTable',
events: {
'click .ptitle':'handleClick'
},
initialize: function() {
_.bindAll(this, 'render', 'handleClick');
},
render: function() {
$(this.el).html('<td>'+this.model.get('title')+'</td>');
return this; // for chainable calls
},
handleClick: function() {
alert('Been clicked');
}
});
var ListView = Backbone.View.extend({
events: {
//"keypress #new-todo": "createOnEnter",
},
initialize : function() {
console.log('Created my app view');
_.bindAll(this, 'render', 'addOne', 'appendOne');
this.collection = new PaperCollection();
this.collection.bind('add', this.appendOne); // collection event binder
this.counter = 0;
this.render();
},
render : function() {
console.log('Render app view');
$(this.el).append("<button id='add'>Add list item</button>");
$(this.el).append("<p>More text</p>");
// $(this.el).append("<ul></ul>");
/*
_(this.collection.models).each(function(item){ // in case collection is not empty
appendOne(item);
}, this); */
},
addOne: function() {
this.counter++;
var p = new Paper();
p.set({
title: "My title: " + this.counter // modify item defaults
});
this.collection.add(p);
},
appendOne: function(p) {
var paperView = new PaperView({
model: p
});
$('ul', this.el).append(paperView.render().el);
}
});
var App = new ListView({el: $('paper_list') });
// App.addOne();
})(jQuery);
Note not getting any errors in console on FF - but still not displaying any of the render outputs in AppView). Appreciate any help. Simple HTML:
<body>
<div class="container_16">
<div class="grid_16">
<div id="paper_list">
Text...
<ul class="thelist"></ul>
</div>
</div>
<div class="clear"></div>
</div>
</body>
This will at least get you rendering the ListView...
// MODELS
var Paper = Backbone.Model.extend ({
defaults : {
title : null,
author: null,
}
});
// COLLECTIONS
var PaperCollection = Backbone.Collection.extend({
model : Paper,
initialize : function() {
console.log("We've created our collection");
}
});
// VIEWS
var PaperView = Backbone.View.extend({
tagName:'li',
className: 'resultTable',
events: {
'click .ptitle':'handleClick'
},
initialize: function() {
_.bindAll(this, 'render', 'handleClick');
},
render: function() {
$(this.el).html('<td>'+this.model.get('title')+'</td>');
return this; // for chainable calls
},
handleClick: function() {
alert('Been clicked');
}
});
var ListView = Backbone.View.extend({
el: '#paper_list',
events: {
"click #add": "createOnEnter",
},
initialize : function() {
console.log('Created my app view');
_.bindAll(this, 'render', 'addOne', 'appendOne');
this.collection = new PaperCollection();
this.collection.bind('add', this.appendOne); // collection event binder
this.counter = 0;
this.render();
},
render : function() {
console.log(this);
$(this.el).append("<button id='add'>Add list item</button>");
$(this.el).append("<p>More text</p>");
// $(this.el).append("<ul></ul>");
/*
_(this.collection.models).each(function(item){ // in case collection is not empty
appendOne(item);
}, this); */
},
addOne: function() {
this.counter++;
var p = new Paper();
p.set({
title: "My title: " + this.counter // modify item defaults
});
this.collection.add(p);
},
appendOne: function(p) {
var paperView = new PaperView({
model: p
});
$('ul', this.el).append(paperView.render().el);
}
});
$(function(){
var App = new ListView();
});
A couple of things...First, I initialized your ListView inside of a document.ready to make sure that the DOM was ready to go, second, I made the el in the listview simply #paper_list then you can do $(this.el) later.
I at least got the button and "more text" to show up...Let me know if that helps!