Extending views in backbonejs with requirejs - backbone.js

So I am using views that are sidebars that have tabs in them, so I created a parent class SidebarView from which I extend two new classes. LeftSidebarView and RightSidebarView. here is the parent class:
define([
'jquery',
'underscore',
'backbone',
'eventbus'
], function ($, _, Backbone, eventBus) {
'use strict';
var SidebarView = Backbone.View.extend({
tabs: [],
isHidden: true,
alignment: '',
el : '',
templateId: '',
milliseconds: 300,
Notice the "tabs: []" array. The two classes that extend from it have only initialize function in them that populates the tabs:
define([
'jquery',
'underscore',
'backbone',
'views/sidebar',
'eventbus',
'views/search'
], function ($, _, Backbone, SidebarView, eventBus, SearchView) {
'use strict';
var RightSidebarView = SidebarView.extend({
alignment: 'right',
el: "#rightSidebar",
templateId: '#sidebarTemplate',
initialize : function() {
this.tabs['search'] = new SearchView({model: this.model});
}
});
return RightSidebarView;
});
and the other one is:
/*global define*/
define([
'jquery',
'underscore',
'backbone',
'views/sidebar',
'views/listings',
'views/listingDetails',
'eventbus'
], function ($, _, Backbone, SidebarView, ListingsView, ListingDetailsView, eventBus) {
'use strict';
var LeftSidebarView = SidebarView.extend({
alignment: 'left',
el: "#leftSidebar",
templateId: '#sidebarTemplate',
initialize : function() {
this.tabs['listings'] = new ListingsView({collection: this.collection);
this.tabs['listingDetails'] = new ListingDetailsView();
}
});
return LeftSidebarView;
});
In my main.js file I do this:
var leftSidebarView = new LeftSidebarView({collection:listings});
var rightSidebarView = new RightSidebarView({model: searchModel});
What happens is that the leftSideBarView which is an instance of LeftSidebarView and rightSidebarView an instance of RightSidebarView both have 3 members inside of this.tabs. It looks like. I still consider myself a javascript noob so I have got to ask if this is an expected behavior? Why does this happen? I looked at the documentation of _.extend and it says it returns a copy not a reference. So I'm kinda surprised by this happening.
edit: formulated the introduction a bit better and cleaned code a little.

The tabs property are attached to the SidebarView.prototype which means that it is shared between RightSidebarView and LeftSidebarView instances. Everything you send as config options in extend method will override the base class prototype and is shared - like static properties in object oriented languages! That means that you will have to change the implementation to initialize tabs in initialize method like:
SidebarView:
initialize : function() {
this.tabs = [];
}
RightSidebarView:
initialize: function() {
RighitSidebarView.__super__.initialize.apply(this, arguments);
// additinal initialization stuff...
}

I am assuming that you meant to declare tabs as a hash not an array since you are using property assignment further down the line. You can use an array but you would push elements into it rather than assigning properties.
The tabs property does not exist on your extended objects. It is defined further up the prototype chain on SidebarView. You can see in this fiddle, when you call rightSidebar.hasOwnProperty('tabs') and it returns false.
http://jsfiddle.net/puleos/yAMR2/
var SidebarView = Backbone.View.extend({
tabs: {},
isHidden: true,
alignment: '',
el : '',
templateId: '',
milliseconds: 300
});
var RightSidebarView = SidebarView.extend({
alignment: 'right',
el: "#rightSidebar",
templateId: '#sidebarTemplate',
initialize : function() {
this.tabs['search'] = {type: "search"};
}
});
var LeftSidebarView = SidebarView.extend({
alignment: 'left',
el: "#leftSidebar",
templateId: '#sidebarTemplate',
initialize : function() {
this.tabs['listings'] = {type: "listings"};
this.tabs['listingDetails'] = {type: "listing details"};
}
});
var rightSidebar = new RightSidebarView();
var leftSidebar = new LeftSidebarView();
console.log(rightSidebar.hasOwnProperty('tabs')); // false
console.log(rightSidebar.tabs);

Related

Backbone view 1 pass value to another view 2

This is the first view content ,where the second view is loaded to first view as a child.
define([ 'jquery', 'underscore', 'backbone',
'text!../../../../school-admin/classManagement.html',
'views/schoolModule/stdManagementView'],
function($, _, Backbone, hrManagementTemplate,StdManagementView) {
var ClassManagementView = Backbone.View
.extend({
// target item.
el : $("#schoolContentOuterPnl"),
render : function() {
var data = {};
// template
var compiledTemplate = _.template(hrManagementTemplate, data);
// append the item to the view's target
this.$el.html(compiledTemplate);
},
// Event Handlers
events : {
"click #btnStdInClassManagement" : "loadStdInClassManagement",
},
loadStdInClassManagement : function(){
//Here i want to pass value to another view
new StdManagementView({
el : $("#classManagementContenTtabContent")
});
},
});
return new ClassManagementView;
});
This is my second view ,when the event on the first view is triggered.
define([ 'jquery', 'underscore', 'backbone', 'datatables',
'text!../../../../school-admin/stdManagement.html' ],
function($, _, Backbone, datatables, stdManagementTemplate) {
var StdManagementView = Backbone.View.extend({
initialize: function(){
this.render();
},
render : function() {
var data = {};
// template
var compiledTemplate = _.template(
stdManagementTemplate, data);
// append the item to the view's target
this.$el.html(compiledTemplate);
},
// Event Handlers
events : {},
});
return StdManagementView;
});
From the above code how can i pass a dynamic value from view 1 to view 2.
From your code it looks like you only want to pass in a value once when you create your second view. As such you can just pass it in to the constructor of your second view and it will be part of the options object passed in.
For example
//view 1
loadStdInClassManagement : function(){
//Here i want to pass value to another view
new StdManagementView({
el : $("#classManagementContenTtabContent"),
someValue: 'something'
});
}
//view 2
var StdManagementView = Backbone.View.extend({
initialize: function(options){
this.someValue = options.someValue;
this.render();
},

Using RequireJS inject additional Bacbone Models in View

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

Uncaught TypeError: Cannot read property 'navigate' of undefined

So I developed a simple CRUD program from the tutorial video of backbonejs.org and the code worked fine. Now I'm trying to implement the code in requirejs but it shows following error in the following code: -
define([
'jquery',
'underscore',
'backbone',
'router',
'models/Customers/Customer',
'helper/Serialize'
], function ($, _, Backbone, Router, Customer, Serialize) {
var CustomerEditView = Backbone.View.extend({
el: '.page',
events: {
'submit .edit-customer-form': 'saveCustomer',
'click .delete': 'deleteCustomer',
},
saveCustomer: function (ev) {
var customerDetails = $(ev.currentTarget).serializeObject();
var customer = new Customer();
customer.save(customerDetails, {
success: function (customer) {
this.router.navigate('', { trigger: true });
}
});
return false;
},
You can use :
customer.save(customerDetails, {
success: function (customer) {
Backbone.history.navigate('', { trigger: true });
}
if you want to use router object first you have to initialize it like
this.router = new router();
and you can say this.router.navigate('', { trigger: true });
it is not optimal to create a new instance in all the views and not suggested to make the object global. You can use Backbone.history.nvaigate which is alias to router.nvaigate

uncaught typeerror: object function has no method 'tojson'

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.

Handeling response from backbone.js collection using fetch

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());
})
}
});

Resources