I am getting a JS error "Uncaught ReferenceError: model is not defined" when rendering a view from within another view.
I have a list view:
define([
'jquery',
'backbone',
'underscore',
'views/action',
'collections/actionlist',
'text!templates/actionlist.html'],
function($, Backbone, _, actionView, actionList, template){
var someactions = [
{ the_action: "Contact 1", due: "1, a street, a town, a city, AB12 3CD", list: "0123456789" },
{ the_action: "Contact 2", due: "1, a street, a town, a city, AB12 3CD", list: "0123456789" },
{ the_action: "Contact 3", due: "1, a street, a town, a city, AB12 3CD", list: "0123456789" }
];
var actionlistView = Backbone.View.extend({
el: '#main',
template: _.template(template),
initialize: function () {
this.collection = new actionList(someactions);
this.collection.on("add", this.renderAction, this);
},
events: {
"click #add": "addAction"
},
render: function () {
var $el = $('#main')
$el.html(this.template);
// Get Actions
_.each(this.collection.models, function (action) {
this.renderAction(action);
}, this);
},
renderAction: function (action) {
var theAction = new actionView({ model: action });
$('#actionlist').append(theAction.render().el);
},
addAction: function(e){
e.preventDefault();
var formData = {};
$('#addAction').children("input").each(function(i, el){
if ($(el).val() !== "") {
formData[el.id] = $(el).val();
}
});
this.collection.create(formData);
}
});
return actionlistView;
});
The actionView that this calls in the renderAction function is:
define([
'jquery',
'backbone',
'underscore',
'models/action',
'text!templates/action.html'],
function($, Backbone, _, actionModel, template){
var actionView = Backbone.View.extend({
tagname: 'li',
template: _.template(template),
render: function () {
this.$el.html(this.template(this.model)); // ERROR OCCURS ON THIS LINE
return this;
}
});
return actionView;
});
I get the error on the line "this.$el.html(this.template(this.model));" when trying to render the first actionView.
I am stumped!! What am I missing?
ActionView Template as requested:
<b class="name"><%=model.get("the_action")%></b> - <%=model.get("due")%> -
<em>from <%=model.get("list")%></em>
You are better off calling model.toJSON() in the template and referencing the json in the template.
From this:
this.$el.html(this.template(this.model));
To this:
this.$el.html(this.template(this.model.toJSON()));
Then reference 'due' and 'list' in the template directly:
<b class="name"><%=the_action%></b> - <%=due%> -
<em>from <%=list%></em>
Related
I'm learning Backbone with RequireJS and I have got a problem when trying to instantiate additional model in my view. I have couple of events which are calling different methods. Different methods are using more or less different models and subviews The example above drops on new model instance
TypeError: GridsModel is not a constructor
var gridModel = new GridsModel;
when fireing grid method
My code looks like
/*global define*/
define([
'jquery',
'underscore',
'backbone',
'templates',
'jqueryui',
'models/grids',
'views/grids',
'views/modal'
], function ($, _, Backbone, JST, GridsModel, GridsView, ModalView) {
'use strict';
var EditorView = Backbone.View.extend({
template: JST['app/scripts/templates/editor.ejs'],
tagName: 'div',
el: '.container',
id: '',
className: '',
events: {
"click button.expand" : "controlToggle",
"click .row-edit" : "edit",
"click .grid" : "grid",
"click .delete" : "delete",
"click .components" : "components",
},
initialize: function () {
var gridModel = new GridsModel;
var body = $('body')
var rows = body.find('.row')
console.log(this.model)
$.each(rows, function(e , v){
if(v.length > 0)
console.log(v)
//$(this).parent().addClass('editor-row')
else
//console.log($(this))
$(this).addClass('editor-row empty-row')
})
$('.ui-sortable').sortable({ handle: 'button.row-handle' })
this.listenTo(this.model, 'change', this.render);
this.listenTo(this.model, 'change', this.render);
},
render: function () {
this.$el.html(this.template(this.model.toJSON()));
},
controlToggle: function(e){
var controlls = $(e.currentTarget).closest('.editor-controls')
$(controlls).find('.active').removeClass('active')
$(e.currentTarget).parent().addClass('active')
},
edit: function(){
},
delete: function() {
confirm('Press OK to delete section, Cancel to leave')
},
grid: function() {
this.model = new GridsModel({
'title': 'Edit Grids'
})
var gridView = new GridsView({
model: this.model
})
var grids = new ModalView({
model : this.model,
subview: gridView
}).render()
},
components: function() {
this.model = new Fefe.Models.Components({
'title': 'Add Component'
})
var componentsView = new Fefe.Views.Components({
model: this.model
})
var components= new Fefe.Views.Modal({
model : this.model,
className: 'modal large',
subview: componentsView
}).render()
}
});
return EditorView;
});
What do I do wrong here
I have the following views in my application.
sets.js
define([
'jquery',
'underscore',
'backbone',
'bootstrap',
'jqueryui',
'numberformat',
'text!templates/sets/sets.html'
], function ($, _, Backbone, bootstrap, jqueryui, numberformat, setsTemplate) {
var setsView = Backbone.View.extend({
el: $("#contenido"),
events: {
'keyup table tbody input': 'afterRender'
},
initialize: function (options) {
_.bindAll(this, 'render', 'afterRender');
this.options = options || {};
},
render: function () {
var data = {sets: this.options.sets};
var compiledTemplate = _.template(setsTemplate, data);
this.$el.html(compiledTemplate);
$(".alert").addClass('alert-' + this.options.clase);
var tabs = $("#tabs").tabs();
tabs.find(".ui-tabs-nav").sortable({
axis: "x",
stop: function () {
tabs.tabs("refresh");
}
});
$('#emisor').hide();
this.afterRender();
},
afterRender: function () {
console.log('afterRender');
var ventasView = new VentasView();
ventasView.render();
}
})
return setsView;
});
ventas.js
define([
'jquery',
'underscore',
'backbone',
'bootstrap',
'jqueryui',
'text!templates/cliente/cliente.html',
'models/cliente',
'views/sets/sets'
], function ($, _, Backbone, bootstrap, jqueryui, clienteTemplate, SetsView) {
var clienteView = Backbone.View.extend({
el: $("#cliente"),
initialize: function (options) {
this.options = options || {};
},
render: function () {
$("#cliente").html(clienteTemplate);
$('#rut').autocomplete(
{
source: '/clientes/buscar/',
minLength: 1,
dataType: 'json',
cache: false,
select: function (event, ui) {
$("#rut").val(ui.item.Cliente.rut);
$("#id").val(ui.item.Cliente.id);
$("#razon").val(ui.item.Cliente.razon);
$("#giro").val(ui.item.Cliente.giro);
$("#direccion").val(ui.item.Cliente.direccion);
$("#comuna").val(ui.item.Cliente.comuna);
$("#ciudad").val(ui.item.Cliente.ciudad);
var sets = new SetsView();
sets.afterRender();
return false;
}
}).data("autocomplete")._renderItem = function (ul, item) {
return $("<li></li>").data("item.autocomplete", item).append("<a><strong>" + item.Cliente.rut + "</strong></a>").appendTo(ul);
};
}
});
return clienteView;
});
error
Uncaught TypeError: Object [object Object] has no method 'afterRender'
You are missing a parameter in your function :
function ($, _, Backbone, bootstrap, jqueryui, clienteTemplate, SetsView)
function ($, _, Backbone, bootstrap, jqueryui, clienteTemplate, clienteModel, SetsView)
I have created a model like this
define(['backbone', 'text_dictionary'], function(Backbone, Text_Dict) {
var IndexPageModel = Backbone.Model.extend({
defaults:{
val_btn_gotohomepage : Text_Dict.val_btn_gotohomepage,
val_btn_gotologinpage : Text_Dict.val_btn_gotologinpage,
val_btn_gotolistpage : Text_Dict.val_btn_gotolistpage
}
});
return IndexPageModel;
});
and instantiated this model with 'new' in my page code like this
define([ 'page_layout',
'panel_itemview',
'header_itemview',
'content_itemview',
'footer_itemview',
'templates',
'text_dictionary',
'indexpage_model',
'indexpage_logic'],
function( Page,
Panel,
Header,
Content,
Footer,
Templates,
Text_Dict,
IndexPageModel,
IndexPage_BusnLogic) {
console.log("Success..Inside Index Page.");
var Page_Index = {};
Page_Index.page = (function(){
var _pageName = Text_Dict.indexpage_name;
var _pageModel = new IndexPageModel();
return _pageLayout = Page.pageLayout({
name:_pageName,
panelView: Panel.panelView({name:_pageName, pagetemplate: Templates.simple_panel}),
headerView: Header.headerView({name:_pageName, title: Text_Dict.indexpage_header, pagetemplate: Templates.header_with_buttons}),
contentView: Content.contentView({name:_pageName, page_model:_pageModel, pagetemplate:Templates.content_index, busn_logic:IndexPage_BusnLogic.HandleEvents}),
footerView: Footer.footerView({name:_pageName, title: Text_Dict.indexpage_footer, pagetemplate: Templates.simple_footer})
});
})();
return Page_Index;
});
my page gets created using the page layout
define([ 'underscore', 'marionette' ], function( _, Marionette ) {
console.log("Success..Inside Index View.");
var Page = {};
var _ReplaceWithRegion = Marionette.Region.extend({
open: function(view){
//Need this to keep Panel/Header/Content/Footer at the same level for panel to work properly
this.$el.replaceWith(view.el);
}
});
Page.pageLayout = function (opts) {
var _opts = _.extend ({ name: 'noname',
panelView: null,
headerView: null,
contentView: null,
footerView: null,
}, opts);
return new ( Marionette.Layout.extend({
tagName: 'section',
attributes: function() {
return {
'id': 'page_' + _opts.name,
'data-url': 'page_' + _opts.name,
'data-role': 'page',
'data-theme': 'a'
};
},
template: function () {
return "<div region_id='panel'/><div region_id='header'/><div region_id='content'/><div region_id='footer'/>";
},
regions: {
panel: {selector: "[region_id=panel]", regionType: _ReplaceWithRegion},
header: {selector: "[region_id=header]", regionType: _ReplaceWithRegion},
content: {selector: "[region_id=content]", regionType: _ReplaceWithRegion},
footer: {selector: "[region_id=footer]", regionType: _ReplaceWithRegion},
},
initialize: function(){
$('body').append(this.$el);
this.render();
},
onRender: function() {
if (this.options.panelView) {
this.panel.show (this.options.panelView);
};
if (this.options.headerView) {
this.header.show (this.options.headerView);
};
if (this.options.contentView) {
this.content.show(this.options.contentView);
};
if (this.options.footerView) {
this.footer.show (this.options.footerView);
};
},
}))(_opts);
};
return Page;
});
but in my itemview when i am passing model reference like this
define([ 'underscore', 'marionette', 'event_dictionary', 'app' ], function(_,
Marionette, Event_Dict, App) {
console.log("Success..Inside Content Index View.");
var Content = {};
Content.contentView = function(opts) {
return new (Marionette.ItemView.extend({
tagName : 'div',
attributes : function() {
console.log('options name==' + opts.name);
console.log("page model=="+opts.page_model);
return {
'region_id' : 'content',
'id' : 'content_' + opts.name,
'data-role' : 'content'
};
},
initialize : function() {
_.bindAll(this, "template");
},
template : function() {
return opts.pagetemplate;
},
model : function() {
return opts.page_model;
}
}))(opts);
};
return Content;
});
It's giving me error
Uncaught TypeError: Object function () {
return opts.page_model;
} has no method 'toJSON'
The model property of a view cannot be a function. Backbone allows this for some things like url (by way of the _.result helper function), but not in this case. Change your view code to not have a model function and just do this in initialize:
initialize: function (options) {
this.model = this.page_model = options.page_model;
}
UPDATE since you won't just take my word for it, here is the Marionette source that is almost certainly the top of your exception stack trace. Once again: view.model has to be a model object not a function. Fix that and the error will go away.
The accepted answer is correct, but it took a bit of messing about to find out why I had that error coming up, so I'm offering what the solution for my personal use-case was in case it helps anyone else stumbling upon this page in the future.
I had this:
app.module 'Widget.Meta', (Meta, app, Backbone, Marionette, $, _) ->
Meta.metaView = Backbone.Marionette.ItemView.extend
model: app.Entities.Models.meta
template: '#meta-template'
... when I should have had this:
app.module 'Widget.Meta', (Meta, app, Backbone, Marionette, $, _) ->
Meta.metaView = Backbone.Marionette.ItemView.extend
model: new app.Entities.Models.meta()
template: '#meta-template'
It's just a matter of instantiating the function definition.
Im trying to add a fuzzy search feature to filter objects in a collection. The console is showing that the Fuse is working correctly and returning the correct objects. Now the question is how do I pass the filtered collection to my view to be rendered.
Here is the collection:
define(["jquery", "backbone", "models/MachineModel"],
function($, Backbone, Model) {
var MachineCollection = Backbone.Collection.extend({
model: Model,
url: '/api/machines',
searchablefields: ['name', 'type', 'ips', 'dataset', 'cpus', 'datacenter', 'state'],
rebuildIndex: function(options) {
var _ref;
if (options == null) {
options = {
keys: this.searchablefields
};
}
return this._fuse = new Fuse(_.pluck(this.models, 'attributes'), options);
},
search: function(query) {
this.rebuildIndex();
var result = this._fuse.search(query);
console.log(result);
this.trigger('reset');
}
});
return MachineCollection;
});
and here is my view
define(["jquery", "backbone", "views/cloud/machines/SingleMachineView", "text!templates/cloud/machines/allMachines.html"],
function($, Backbone, SingleMachineView, template){
var AllMachinesView = Backbone.View.extend({
el: "#magic",
initialize: function() {
// Calls the view's render method
this.collection.on('add', this.addMachine, this);
this.collection.on('reset', this.onCollectionReset, this);
this.render();
},
// View Event Handlers
events: {
'keyup #filter': 'fuzzySearch'
},
// SUBVIEWS
// ========
onCollectionReset: function(collection) {
console.log('collection reset');
var that = this;
$(collection).each(function (model) {
that.addMachine(model);
});
},
addMachine: function(model) {
var machineHTML = (new SingleMachineView({ model: model })).render().el;
$(machineHTML).prependTo('#machine-container');
},
// FUZZY SEARCH
// ============
fuzzySearch: function(e) {
var query = $(e.target).val();
this.collection.search(query);
},
// RENDER
// ======
render: function() {
this.template = _.template(template);
this.$el.html(this.template);
return this;
}
});
return AllMachinesView;
});
any insight would be greatly appreciated.
am pretty new to backbone.js and managed recently to finish my first application. I made a collection that is responsible for fetching data through a API but am not able to loop through the result and use it.
Here is my model file
define([
'jquery',
'underscore',
'backbone'
], function($, _, Backbone){
var VehicleLookupModel = Backbone.Model.extend({
//data will contain one of the items returned from the collection's 'parse' function.
parse: function(data){
return data;
}
})
return VehicleLookupModel;
});
collection file
define([
'jquery',
'underscore',
'backbone',
'l/models/VehicleLookupModel'
], function($, _, Backbone, VehicleLookupModel){
var VehicleLookupModelSet = Backbone.Collection.extend({
model : VehicleLookupModel,
url : function() {
return '/en/car/api/model-lookup-model.json/'+this.make+'/';
},
parse : function(response) {
return response;
},
initialize: function(options) {
options || (options = {});
this.make = options.make;
}
})
return VehicleLookupModelSet;
});
and finally the view file
define([
'jquery',
'underscore',
'backbone',
'l/collections/VehicleLookupMakeSet',
'l/collections/VehicleLookupModelSet',
'l/collections/VehicleLookupTrimSet'
], function($, _, Backbone, VehicleLookupMakeSet, VehicleLookupModelSet, VehicleLookupTrimSet){
var BrowseVehicleView = Backbone.View.extend({
el: $('#vehicle-browse-form'),
initialize: function(){
// Extend JQuery example
// This would extend JQuery function for resetting elements on the form
$.fn.extend({
resetElement: function(){
$(this).attr('disabled', 'disabled');
$(this).html('');
return $(this);
}
});
// define array of elements to be used in DOM manipulations
this.elements = {
"make" : $('#id_make', this.el),
"model" : $('#id_model', this.el),
"trim" : $('#id_trim', this.el),
"year" : $('#id_year', this.el)
}
},
events: {
"change #id_make" : "onMakeChange",
"change #id_model" : "onModelChange",
"change #id_trim" : "onTrimChange"
},
render: function(){
// Using Underscore we can compile our template with data
},
onMakeChange: function(event) {
this.elements.model.resetElement();
this.elements.trim.resetElement();
this.collection = new VehicleLookupModelSet({make: this.elements.make.val()})
this.collection.fetch();
console.log(this.collection);
},
onModelChange: function(event) {
var VehicleLookupTrimInstance = new VehicleLookupTrimSet({make: this.elements.make.val(), model: this.elements.model.val()})
VehicleLookupTrimInstance.fetch();
},
onTrimChange: function(event) {
},
renderItem: function(object, item) {
console.log(item);
}
});
// Our module now returns our view
return new BrowseVehicleView;
});
The above is console.log(this.collection) is returning an object with many property which am not sure how to use. But, I noticed that there is a method "models" and inside models there is many number of objects, each represent the value of the json.
Any ideas how i can loop through the object?
this.collection.fetch({
success: function(collection, response) {
_.each(collection.models, function(model) {
console.log(model.toJSON());
})
}
});