I have a django + backbonejs application. The problem is that the data rendered by backbonejs after the api call is not rendered inside the data table on the live site. It works perfectly on my local machine.
My local machine shows the data
The live site shows empty row for each record
What can be the reason of this strange behavior?
var PatientPageView = Backbone.View.extend({
el: ".st-content",
initialize: function () {
this.$dataTable = this.createDataTable();
this.$inputTextField = this.$("#myInputTextField");
this.listenTo(this.collection, "add", this.addObject);
this.listenTo(this.collection, "reset", this.fetchAll);
this.listenTo(app.vent, "patient:create", this.save);
this.collection.fetch({reset: true});
},
events: {
'click #addBtn': 'openDialog',
'keyup #myInputTextField': 'searchTable',
'click .viewPatient': 'viewPatient',
'click .NotesIcon': 'viewNote',
'click .paperclipIcon': 'viewAttachment',
'click #export': 'export',
'click #print': 'print'
},
save: function (object) {
this.collection.add(object);
},
addObject: function (patient) {
var view = new PatientItemView({model: patient});
$('#patientsTable').DataTable().row.add(view.render().el).draw();
},
openDialog: function () {
var view = new PatientAddSubView({model: new Patient.Model});
view.open();
},
createDataTable: function () {
return $('#patientsTable').DataTable({
"aoColumns": [
null, null, null, null, null, null, null, null, null, {"bSortable": false}, {"bSortable": false}
],
"order": [[0, "asc"]]
});
},
searchTable: function () {
this.$dataTable.search(this.$inputTextField.val()).draw();
},
export: function (e) {
view = new ExportPageView.MainView({
content_type: 'patients'
});
view.open();
},
print: function (e) {
window.print();
},
viewPatient: function (e) {
var patient = this.collection.get(e.target.id)
var editPatientSubView = new PatientEditSubView({
model: patient, remover: PatientDeleteSubView,
eventString: 'patient:deleted'
});
editPatientSubView.open();
},
viewNote: function (e) {
var patient = this.collection.get(e.target.id)
view = new NotePageView.MainView({
collection: new Note.Collection,
relatedTo: patient,
content_type: 10
});
view.open();
},
viewAttachment: function (e) {
var patient = this.collection.get(e.target.id)
view = new AttachmentPageView.EditView({
collection: new Attachment.Collection,
relatedTo: patient,
contentType: 10
})
view.open();
},
fetchAll: function () {
this.collection.each(this.addObject);
}
});
The function 'addObject' renders this list.
The patient item view is simple
var PatientItemView = core.ItemView.extend({
tagName: "tr",
//template: _.template($('#patient-view-template').html()),
template: _.template(ViewPatientTmpl)
});
Related
I am new to backbone and marionette. Now i m trying to implement paging with compositeview of marionettejs. Below is my code, what happens here that when a new fetch is done through my custom pager, existing data is getting replaced by new set of data instead of appending. Please help me to overcome this! Thanks in advance.
define(['text!/Templates/projects/_GroupItem.html', 'collections/projects/groups'], function (ProjectGroupsTmpl, GroupCollection) {
var GroupItemView = Backbone.Marionette.ItemView.extend({
tagName: 'li',
template: _.template(ProjectGroupsTmpl)
});
var CompositeView = Backbone.Marionette.CompositeView.extend({
template: _.template("<ul id='ulgroups' ></ul>"),
itemView: GroupItemView,
itemViewContainer: '#ulgroups',
initialize: function (params) {
this.isLoading = false;
this.ProjectID = params.id;
this.collection = new GroupCollection();
this.getData();
var self = this;
$(window).scroll(function () {
self.checkScroll();
});
},
getData: function () {
var that = this;
this.isLoading = true;
this.collection.fetch({
data: { ProjectID: this.ProjectID },
success: function (collection, response, options) {
that.isLoading = false;
}
});
},
checkScroll: function () {
var triggerPoint = 100; // 100px from the bottom
if (!this.isLoading && $(window).scrollTop() + $(window).height() + triggerPoint > $(document).height()) {
this.collection.page += 1; // Load next page
this.getData();
}
},
appendHtml: function (collectionView, itemView, index) {
$(this.itemViewContainer).append(itemView.el);
}
});
return CompositeView;
});
I have used backbone.paginator to resolve above issue and it works well. Below are the new code used for that.
Collection:
define([
'jquery',
'underscore',
'backbone',
'helper',
'paginator'
], function ($, _, Backbone) {
var Groups = Backbone.PageableCollection.extend({
url: 'projects/_groups',
mode: "infinite",
state: {
pageSize: null
},
queryParams: {
totalPages: null,
totalRecords: null
}
});
return Groups;
});
Marionette CompositeView:
define(['text!/Templates/projects/_GroupItem.html', 'collections/projects/groups'], function (ProjectGroupsTmpl, GroupCollection) {
var GroupItemView = Backbone.Marionette.ItemView.extend({
tagName: 'li',
template: _.template(ProjectGroupsTmpl)
});
var CompositeView = Backbone.Marionette.CompositeView.extend({
template: _.template("<ul id='ulgroups' ></ul>"),
itemView: GroupItemView,
itemViewContainer: '#ulgroups',
initialize: function (params) {
this.isLoading = false;
this.ProjectID = params.id;
this.grpcollection = new GroupCollection([], {
queryParams: {
ProjectID: params.id
}
});
this.collection = this.grpcollection.fullCollection;
this.getData();
var self = this;
$(window).scroll(function () {
self.checkScroll();
});
},
getData: function () {
var that = this;
this.isLoading = true;
this.grpcollection.fetch({
success: function (collection, response, options) {
if (response.length > 0) {
that.isLoading = false;
}
}
});
},
getNextPage: function () {
var that = this;
this.isLoading = true;
this.grpcollection.getNextPage({
success: function (collection, response, options) {
if (response.length > 0) {
that.isLoading = false;
}
}
});
},
checkScroll: function () {
var triggerPoint = 100; // 100px from the bottom
if (!this.isLoading && $(window).scrollTop() + $(window).height() + triggerPoint > $(document).height()) {
this.getNextPage();
}
},
appendHtml: function (collectionView, itemView, index) {
$(this.itemViewContainer).append(itemView.el);
}
});
return CompositeView;
});
I solved a similar problem recently by creating a temporary collection to hold the models for each paginated request. My setup was slightly different to yours, however, in that I created a Marionette controller to negotiate between the data and the view. A "show" method on the controller handled the initial data request and a "showMore" method handled subsequent requests. Here is basically what I did:
(function ($, _, Backbone, Marionette) {
var carData = [
{
make: 'Audi',
model: 'A4',
year: '1994'
},
{
make: 'BMW',
model: '3 Series',
year: '1975'
},
{
make: 'Chevrolet',
model: 'Cruze',
year: '2008'
},
{
make: 'Daimler',
model: 'Six',
year: '1994'
},
{
make: 'Fiat',
model: '500X',
year: '2015'
},
{
make: 'Honda',
model: 'Civic',
year: '1972'
},
{
make: 'Kia',
model: 'Optima',
year: '2015'
},
{
make: 'Lada',
model: 'Priora',
year: '2007'
},
{
make: 'Mitusbishi',
model: 'Lancer',
year: '1973'
},
{
make: 'Nissan',
model: 'Pathfinder',
year: '1995'
}
];
var Car = Backbone.Model.extend({
defaults: {
make: '',
model: '',
year: ''
}
});
var Cars = Backbone.Collection.extend({
model: Car,
rows: 3,
page: 0
});
var CarView = Marionette.ItemView.extend({
tagName: 'tr',
template: '#row-template'
});
var CarsView = Marionette.CompositeView.extend({
childView: CarView,
childViewContainer: 'tbody',
template: '#table-template',
triggers: {
'click button': 'showMore'
}
});
var CarController = Marionette.Controller.extend({
initialize: function (options) {
this.collection = options.collection;
},
show: function () {
var cars = this.getData(this.collection.page);
var carsView = new CarsView({
collection: new Backbone.Collection(cars)
});
this.listenTo(carsView, 'showMore', this.showMore);
app.carsRegion.show(carsView);
},
showMore: function (options) {
var cars = this.getData(++this.collection.page);
options.collection.add(cars);
},
getData: function (page) {
var rows = this.collection.rows;
var start = page * rows;
var end = start + rows;
return this.collection.slice(start, end);
}
});
var app = new Marionette.Application();
var cars = new Cars(carData);
var carController = new CarController({
collection: cars
});
app.addRegions({
carsRegion: '#cars-region'
});
app.addInitializer(function () {
carController.show();
});
app.start();
}(jQuery, _, Backbone, Marionette));
This is also available as a JSFiddle.
MenuView.js
events: {
"click ul li": "MenuClick"
},
MenuClick: function (e) {
var elm = e.currentTarget;
return elm.firstChild.innerText;
},
ModuleView.js
ModuleView = Backbone.View.extend({
initialize: function () {
var moduleDt = new ModuleCol();
moduleDt.fetch({
data: JSON.stringify({ Code: "Pass to here" }),
dataType: "json",
type: 'POST',
contentType: "application/json; charset=UTF-8"
});
},
The code should work like, when clicking the ul li, I will get the value from the li and pass to the moduleView.js to proceed with backbone fetch, but I dont have any idea on how actually to make it, I had search through quite some link, but still can't figure out the correct, are it is correct to make it?
Update1:
View/MenuView.js
define(['underscore', 'backbone', '../Collection/MenuCol'], function (_, Backbone, MenuCol) {
var MenuView = Backbone.View.extend({
initialize: function () {
//var vent = _.extend({}, Backbone.Events);
var mnCol = new MenuCol();
mnCol.fetch({
type: 'POST',
contentType: "application/json; charset=UTF-8",
success: _.bind(this.AppendMenu, this),
});
},
AppendMenu: function (msg) {
var feed = JSON.parse(JSON.stringify(msg));
//var title = _.pluck(_.flatten(feed[0]), "Title");
_.each(_.flatten(feed[0]), function (data) {
this.$('ul').append('<li class="inactive"><p class="none">' + data.Code + '</p><a>' + data.Title + ' </a></li>');
});
},
events: {
"click ul li": "MenuClick"
},
MenuClick: function (e) {
var elm = e.currentTarget
console.log(elm.firstChild.innerText);
this.trigger('itemChoose', elm.firstChild.innerText);
},
});
return MenuView;
});
View/ModuleView.js
define(['underscore', 'backbone', 'datatables', '../Collection/ModuleCol', '../View/MenuView'], function (_, Backbone, dataTable, ModuleCol, MenuView) {
var ModuleView = Backbone.View.extend({
initialize: function () {
//MenuView.vent.on('itmChoose');
MenuView.on('itemChoose', function (item_value) { });
var mdCol = new ModuleCol();
mdCol.fetch({
data: JSON.stringify({ Code: "" }),
type: 'POST',
contentType: "application/json; charset=UTF-8",
success: _.bind(this.AppendModule, this),
});
},
AppendModule: function (msg) {
var feed = JSON.parse(JSON.stringify(msg));
$('table[id$=gvMenu]').dataTable({
"bAutoWidth": false,
"aoColumns": [
{ "sWidth": "10%" },
{ "sWidth": "90%" },
],
"bFilter": false,
"bInfo": false,
"bLengthChange": false,
"bSort": false,
"bPaginate": false,
"aLengthMenu": [
[25, 50, 100, 200, -1],
[25, 50, 100, 200, "All"]
],
"iDisplayLength": -1,
"aoColumns": [
{
"sTitle": "Code", "mDataProp": "Title",
},
{
"sTitle": "Description", "mDataProp": "Description",
}],
sAjaxSource: "",
sAjaxDataProp: "",
fnServerData: function (sSource, aoData, fnCallback) {
//console.log(feed.d);
//fnCallback(feed.d);
},
});
}
});
//MenuView.on('itmChoose', function (item_value) { });
return ModuleView;
});
master.js
require(['jquery', '../JScripts/View/MenuView', '../JScripts/View/ModuleView'], function ($, menu, module) {
$(document).ready(function () {
new menu({ el: $('#sidebar') });
new module({});
});
});
This is my code, but still, it is correct to code like this? I had spent a night to work it out, but it still say is undefined.
When li click, I get the value of the li, and pass to module.js to ajax data so that I can create datatable.
I continue to puzzle it out and see if I can make it today while waiting for your guide :) thanks
According your updates, you must supply instance of MenuView to ModuleView instance:
master.js:
require(['jquery', '../JScripts/View/MenuView', '../JScripts/View/ModuleView'], function ($, menu, module) {
$(document).ready(function () {
var menuInstance = new menu({ el: $('#sidebar') });
new module({menu: menuInstance});
});
});
ModuleView.js:
var ModuleView = Backbone.View.extend({
initialize: function (options) {
//MenuView.vent.on('itmChoose');
options.menu.on('itemChoose', function (item_value) { });
var mdCol = new ModuleCol();
mdCol.fetch({
data: JSON.stringify({ Code: "" }),
type: 'POST',
contentType: "application/json; charset=UTF-8",
success: _.bind(this.AppendModule, this),
});
},
If your ModuleView and MenuView are equal components of system - pick out in your code some central component (e.g. descendant Backbone.Router) and make communication through it.
If your views aren`t equal components (e.g. ModuleView aggregates MenuView), you can subscribe on some event from MenuView in ModuleView and pass value:
//in ModuleView
menuView.on('itemChoosen', function(item_value) { .... });
//in MenuView
MenuClick: function (e) {
var elm = e.currentTarget;
this.trigger('itemChoosen', elm.firstChild.innerText);
},
I took the base of this code from a gist. It initially worked perfectly when I first fetch()ed the collection and then in render() called tw-bootstap's .typeahead().
However, I have put in a keypress event to try and restrict the size of the data returned by fetch(). The collection data is returned and it is filtered through prepData() fine and arrives at render(). The typeahead is not working, however at that stage. It may be that the backbone event is overriding render at that point?
// typeahead on the numbers
var Bootstrap = {};
Bootstrap.Typeahead = Backbone.View.extend({
el: '#autocompleteN',
tagName: 'input',
attributes: {"data-provide": "typeahead"},
initialize: function(options){
if(!this.collection) {
return null;
}
//this.collection.on("reset", this.prepData, this);
},
events: {
"keypress": "setSearch"
},
setSearch: _.throttle(function(e) {
var that=this;
var d = e.currentTarget.value;
// strip spaces and remove non-numerics
d = d.replace(/ /g,'');
d = d.replace(/[^0-9]/g, '');
// if it's longer than 2, call a fetch;
if(d.length > 2) {
$.when( app.searchNums.fetch({url: 'api/index.php/search/num/'+d}) ).then(function() {
//console.dir("success");
that.prepData();
});
}
}, 1000),
prepData: function() {
//console.dir("prepData called");
var prepare = _.pluck(this.collection.models, 'attributes');
this.property = this.options.property || _.keys(prepare[0])[0];
this.items = this.options.items;
this.data = _.pluck(prepare, this.property);
this.render();
},
render: function() {
var that = this;
that.$el.typeahead({
source: that.data,
//source: ['PHP', 'MySQL', 'SQL', 'PostgreSQL', 'HTML', 'CSS', 'HTML5', 'CSS3', 'JSON'],
items: that.items,
onselect: function( data ) {
// render the results view here
}
});
return this;
}
});
var bui = new Bootstrap.Typeahead({
collection: app.searchNums,
items: 5
});
Why dont you just set minLength on the typeahead, it looks like that is what you are trying to do?
I've been working on a prototype Backbone application using Backbone.LayoutManager and I'm running into something I don't understand.
The scenario is that I have a form for adding "people" {firstname, lastname} to a list view, I save the model fine and the new item shows up in the list. I also have a remove function that works when after the page is refreshed, but if I try to delete the person I just created without a page refresh, the removeUser() function never gets called.
My code is below. Can someone help me out? I'm just trying to learn Backbone and if you have the answer to this question as well as any other criticisms, I'd be grateful. Thanks.
define([
// Global application context.
"app",
// Third-party libraries.
"backbone"
],
function (app, Backbone) {
var User = app.module();
User.Model = Backbone.Model.extend({
defaults : {
firstName: "",
lastName: ""
}
});
User.Collection = Backbone.Collection.extend({
model: User.Model,
cache: true,
url: "/rest/user"
});
User.Views.EmptyList = Backbone.View.extend({
template: "users/empty-list",
className: "table-data-no-content",
render: function (manage) {
return manage(this).render().then(function () {
this
.$el
.insertAfter(".table-data-header")
.hide()
.slideDown();
});
}
});
User.Views.Item = Backbone.View.extend({
template: "users/user",
tagName: "ul",
className: "table-data-row"
events: {
"click .remove": "removeUser"
},
removeUser: function () {
console.log(this.model);
this.model.destroy();
this.collection.remove(this.model);
this.$el.slideUp();
if (this.collection.length === 0) {
this.insertView(new User.Views.EmptyList).render();
}
}
});
User.Views.List = Backbone.View.extend({
initialize: function () {
this.collection.on("change", this.render, this);
},
render: function (manage) {
if (this.collection.length > 0) {
jQuery(".table-data-no-content").slideUp("fast", function() {
$(this).remove();
});
this.collection.each(function(model) {
this.insertView(new User.Views.Item({
model: model,
collection: this.collection,
serialize: model.toJSON()
}));
}, this);
} else {
this.insertView(new User.Views.EmptyList());
}
// You still must return this view to render, works identical to
// existing functionality.
return manage(this).render();
}
});
User.Views.AddUser = Backbone.View.extend({
template: "users/add-user",
events: {
"click input#saveUser": "saveUser"
},
render: function (manage) {
return manage(this).render().then(function () {
$("input[type='text']")
.clearField()
.eq(0)
.focus();
});
},
saveUser: function () {
var user = new User.Model({
firstName: $(".first-name").val(),
lastName: $(".last-name").val()
});
this.collection.create(user);
this
.$("input[type='text']")
.val("")
.clearField("refresh")
.removeAttr("style")
.eq(0)
.focus();
}
});
return User;
});
The problem turned out to be an incorrect response from the server. Once the server sent back the correct JSON object, everything worked correctly.
I am putting together a backbone example in which models are created edited and deleted. I am able to save new models and edits to local storage, but am having a problem getting localstorage to properly display on refresh. It seems to be loading as a single object, and therefore gives me one model regardless of how many were added.
var Thing = Backbone.Model.extend({
defaults: {
title: 'blank'
}
});
var ThingView = Backbone.View.extend({
template: _.template('<b><button id="remove">X</button> <b><button id="edit">Edit</button> <%= title %></b>'),
editTemplate: _.template('<input class="name" value="<%= name %>" /><button id="save">Save</button>'),
events: {
"click #remove": "deleteItem",
"click #edit": "editItem",
"click #save": "saveItem",
},
deleteItem: function () {
console.log('deleted');
this.model.destroy();
this.remove();
},
editItem: function () {
console.log('editing');
this.$el.html(this.editTemplate(this.model.toJSON()));
},
saveItem: function () {
console.log('saved');
editTitle = $('input.name').val();
console.log(editTitle);
this.model.save({
title: editTitle
});
this.$el.html(this.template(this.model.toJSON()));
},
render: function () {
var attributes = this.model.toJSON();
this.$el.append(this.template(attributes));
return this;
}
});
var ThingsList = Backbone.Collection.extend({
model: Thing,
localStorage: new Store("store-name"),
});
var storeVar = localStorage.getItem("store-name");
console.log(storeVar);
var thingsList = new ThingsList;
thingsList.reset(storeVar);
var ThingsListView = Backbone.View.extend({
el: $('body'),
events: {
'click #add': 'insertItem',
},
initialize: function () {
this.render();
this.collection.on("add", this.renderThing, this);
},
insertItem: function (e) {
newTitle = $('input').val();
newThing = new Thing({
title: newTitle
});
this.collection.add(newThing);
newThing.save();
console.log(this.collection.length);
},
render: function () {
_.each(this.collection.models, function (items) {
this.renderThing(items);
}, this);
},
renderThing: function (items) {
var thingView = new ThingView({
model: items
});
this.$el.append(thingView.render().el);
}
});
var thingsListView = new ThingsListView({
collection: thingsList
});
You need to add the items to your collection, and then to read it in you need to call fetch. You also have a couple of extra trailing commas in your objects.
Here's a slightly modified version of your code which seems to work.
var Thing = Backbone.Model.extend({
defaults:{
title:'blank'
}
});
var ThingView = Backbone.View.extend({
//el: $('body'),
template: _.template('<b><button id="remove">X</button> <b><button id="edit">Edit</button> <%= title %></b>'),
editTemplate: _.template('<input class="name" value="<%= name %>" /><button id="save">Save</button>'),
events: {
"click #remove": "deleteItem",
"click #edit": "editItem",
"click #save": "saveItem",
},
deleteItem: function(){
console.log('deleted');
this.model.destroy();
//remove view from page
this.remove();
},
editItem: function(){
console.log('editing');
this.$el.html(this.editTemplate(this.model.toJSON()));
},
saveItem: function(){
console.log('saved');
editTitle = $('input.name').val();
console.log(editTitle);
this.model.save({title: editTitle});
this.$el.html(this.template(this.model.toJSON()));
},
render: function(){
var attributes = this.model.toJSON();
this.$el.append(this.template(attributes));
return this;
}
});
var storeVar = localStorage.getItem("store-name");
var ThingsList = Backbone.Collection.extend({
model: Thing,
localStorage: new Store("store-name")
});
var things = [
{ title: "Macbook Air", price: 799 },
{ title: "Macbook Pro", price: 999 },
{ title: "The new iPad", price: 399 },
{ title: "Magic Mouse", price: 50 },
{ title: "Cinema Display", price: 799 }
];
console.log(things);
var thingsList = new ThingsList;
var ThingsListView = Backbone.View.extend({
el: $('body'),
events: {
'click #add': 'insertItem'
},
initialize: function () {
this.render();
this.collection.on("add", this.renderThing, this);
},
insertItem: function(e){
newTitle = $('input').val();
newThing = new Thing({ title: newTitle });
this.collection.add(newThing);
newThing.save();
//this.model.saveItem;
console.log(this.collection.length);
},
render: function(){
_.each(this.collection.models, function (items) {
this.renderThing(items);
}, this);
},
renderThing: function(items) {
//console.log('added something');
var thingView = new ThingView({ model: items });
items.save();
this.$el.append(thingView.render().el);
}
});
var thingsListView = new ThingsListView( {collection: thingsList} );
thingsList.fetch();
console.log(thingsList.toJSON());
thingsList.reset(things);
Edit: I see you are trying to read in the value stored in local storage under "store-name", the way backbone-localStorage works is that it uses the name of the store (in your case "Store-name") to store the ids of the rest of the models and then saves each model under a combination of the store name and the id, so say you had three models, you would end up with 4 entries in local storage,
localStorage["store-name"] //id1, id2, id3"
localStorage["store-name-id1"] //model with id1
localStorage["store-name-id2"] // model with id2
localStorage["store-name-id3"] // model with id3
EDIT 2
Here's a link to a jsfiddle of your code, to start I'm leaving the line thingsList.fetch(); commented out, uncomment that line and comment out thingsList.add(things); and run it a second time and it should pull the models from local Storage (I left an alert in there).
var Thing = Backbone.Model.extend({
defaults: {
title: 'blank'
}
});
var ThingView = Backbone.View.extend({
template: _.template('<b><button id="remove">X</button> <b><button id="edit">Edit</button> <%= title %></b>'),
editTemplate: _.template('<input class="name" value="<%= name %>" /><button id="save">Save</button>'),
events: {
"click #remove": "deleteItem",
"click #edit": "editItem",
"click #save": "saveItem",
},
deleteItem: function () {
console.log('deleted');
this.model.destroy();
this.remove();
},
editItem: function () {
console.log('editing');
this.$el.html(this.editTemplate(this.model.toJSON()));
},
saveItem: function () {
console.log('saved');
editTitle = $('input.name').val();
console.log(editTitle);
this.model.save({
title: editTitle
});
this.$el.html(this.template(this.model.toJSON()));
},
render: function () {
var attributes = this.model.toJSON();
this.$el.append(this.template(attributes));
return this;
}
});
var ThingsList = Backbone.Collection.extend({
model: Thing,
localStorage: new Store("store-name"),
});
var storeVar = localStorage["store-name-7ee7d1e3-bbb7-b3e4-1fe8-124f76c2b64d"];
console.log(storeVar);
var thingsList = new ThingsList;
//thingsList.reset(storeVar);
var ThingsListView = Backbone.View.extend({
el: $('body'),
events: {
'click #add': 'insertItem',
},
initialize: function () {
thingsList.fetch();
thingsList.toJSON();
this.render();
this.collection.on("add", this.renderThing, this);
},
insertItem: function (e) {
newTitle = $('input').val();
newThing = new Thing({
title: newTitle
});
this.collection.add(newThing);
newThing.save();
console.log(this.collection.length);
},
render: function () {
_.each(this.collection.models, function (items) {
this.renderThing(items);
}, this);
},
renderThing: function (items) {
var thingView = new ThingView({
model: items
});
this.$el.append(thingView.render().el);
}
});
var thingsListView = new ThingsListView({
collection: thingsList
});