Cannot read property 'replace' of undefined BackboneJS - backbone.js

I am trying to render a View(customerEditView) using modular BackboneJS but it gives me the uncaught type error.
this is my router.js file:
define([
'jquery',
'underscore',
'backbone',
'views/Customers/CustomerEditView',
'views/Customers/CustomerListView'
], function($, _, Backbone, CustomerEditView, CustomerListView) {
var Router = Backbone.Router.extend({
routes: {
"customers": "customerhome",
"editcustomer/:id": "editcustomer",
"newcustomer": "editcustomer",
},
customerhome: function () {
var customerListView = new CustomerListView();
customerListView.render();
},
editcustomer: function (id) {
var customerEditView = new CustomerEditView();
customerEditView.render({ id: id });
}
});
var initialize = function () {
var router = new Router;
Backbone.history.start();
};
return {
initialize: initialize
};
});
and this is my customerEditView file:
define([
'jquery',
'underscore',
'backbone',
'router',
'models/Customers/Customer',
'helper/Serialize',
'text!template/Customer/CustomerEditTemplate.html'
], function ($, _, Backbone, router, Customer, Router, serializeObject, CustomerEditTemplate) {
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) {
Backbone.history.navigate('', { trigger: true });
}
});
return false;
},
deleteCustomer: function (ev) {
this.customer.destroy({
success: function () {
console.log('destroyed');
Backbone.history.navigate('', { trigger: true });
}
});
return false;
},
render: function (options) {
var that = this;
if (options.id) {
that.customer = new Customer({ id: options.id });
that.customer.fetch({
success: function (customer) {
var template = _.template(CustomerEditTemplate);
that.$el.html(template({ customer: customer }));
}
});
} else {
var template = _.template(CustomerEditTemplate);
that.$el.html(template({ customer: null }));
}
}
});
return CustomerEditView;
});

Related

How to navigate between Routes basing on Page URL?

I am working on a Backbone Application that contains multiple routes. I would like to modify my changePage function and make it take a hash as a parameter and when it is called it changes the page basing on that URL.
This is my router
define(["jquery","backbone",'views/header'], function( $, Backbone, Header) {
var ApplicationRouter = Backbone.Router.extend({
_header: null,
routes: {
"": "login",
"home": "home",
},
initialize: function() {
this.firstPage = true;
this.fromBack = false;
i18n.currentLocal = localStorage.getItem("currentLocal") || 'en';
Backbone.history.start();
},
login: function() {
var self = this;
require(['views/loginPageView'], function(loginView){
self.changePage(new loginView(),true);
});
},
home: function() {
var self = this;
require(['views/homePageView'], function(homeView){
self.changePage(new homeView(),false);
});
},
changePage: function (page,noPanel) {
var deferred = $.Deferred();
$page = $(page.el);
$page.attr('data-role', 'page');
console.log(this.firstPage);
if(this.firstPage){
$('#pageContent').append($page);
page.render();
if(!noPanel){
self._header = new Header({parent: $("#header")});
}
}
else{
$('#pageContent').html($page);
$("#header").empty();
page.render();
}
if (this.firstPage) {
this.firstPage = false;
}
deferred.resolve();
return deferred;
},
});
return ApplicationRouter;
})
This is the First View :
define([ "jquery", "backbone","text!../../pages/login.html"], function($, Backbone,loginTpl) {
var loginPageView = Backbone.View.extend({
events :{
"click #login" : "login",
"change input[type=radio]":"changeLanguage"
},
initialize : function() {
//Some Code
},
render: function() {
this.template = _.template(loginTpl);
$(this.el).html(this.template);
return this;
},
login:function(){
console.log("Login Clicked");
router.navigate('home', {trigger: false, replace: false});
},
changeLanguage:function(){
//console.log("change lang");
if(i18n.currentLocal == 'en'){
i18n.currentLocal='fr';
this.render();
$("#month2").attr('checked',true);
}else
{
if(i18n.currentLocal == 'fr'){
i18n.currentLocal='en';
this.render();
}
}
}
});
return loginPageView;
});
So instead of using router.navigate which caused some problems due to bugs in the router I think, I would like to use router.changePage('home.html');
and based on the URL I load to respective View.
Thank You

How can we do paging with backbone marionette composite view?

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.

Calling view function from another view from autocomplete

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)

Backbone.history.start() in ie8 leads to page reload every 20 seconds

Simple application-pilot with Backbone + requireJs.
In ie8 string Backbone.history.start({pushState: true}); leads to page reload every 20 seconds. Without it application doesnt start. What is the problem?
Below content of router.js :
define(
[
'jquery', 'underscore',
'backbone'
],
function ($, _, Backbone) {
var MainRouter = Backbone.Router.extend({
initialize: function () {
var re = new RegExp("(\/)+$", "g");
this.route(/(.*)\/+$/, "trailFix", function (id) {
// remove all trailing slashes if more than one
id = id.replace(re, '');
this.navigate(id, true);
});
},
routes: {
'home': 'showMainPage'
},
showMainPage: function (param) {
require([ 'views/global/main'], function (MainView) {
$(".navigation_item[data-type=home]").addClass("selected").on('click', function () {
return false;
})
$(".p_map, .p_feed").show();
new MainView();
});
}
});
var initialize = function () {
window.mainRouter = new MainRouter();
Backbone.history.start({pushState: true});
};
return {
initialize: initialize
};
});
This is fix for IE8
Backbone.history.loadUrl(window.location.pathname);

backbone-extend.js doesn't seem to load my method

I added this to my backbone-extend.js file which resides in the same folder as backbone-min.js...
_.extend(Backbone.View.prototype, {
getFormData: function(form) {
var unindexed_array = form.serializeArray();
var indexed_array = {};
$.map(unindexed_array, function(n, i){
indexed_array[n['name']] = n['value'];
});
return indexed_array;
}
});
However, when I call this.getFormData in my view code, I get a method not defined error. What am I missing? Thanks for your help!
Edit: Here is my view. I have to uncomment the getFormData method to make it work. It can't see the getFormData otherwise...
define([
'jquery',
'underscore',
'backbone',
'models/Member',
'text!templates/memberEditTemplate.html'
], function($, _, Backbone, Member, memberEditTemplate) {
var MemberEditView = Backbone.View.extend({
el: $("#page"),
model: 'member',
initialize: function(args) {
this.member = new Member({ id: args.id });
this.member.on('error', this.eventSyncError, this);
this.member.on('sync', this.eventSyncModelLoaded, this);
this.member.fetch();
},
events: {
"click #bttnMemberSave": "bttnClickMemberSave"
},
eventSyncError: function(model,response,options) {
console.log('Sync error='+response.statusText);
$('#server-message').css({'color':'red', 'font-weight':'bold'}).text(response.statusText);
//$('#server-message').text(response.statusText);
},
eventSyncModelLoaded: function(model,response,options) {
this.render();
},
eventSyncModelSaved: function(model,response,options) {
console.log("Member saved!");
$('#server-message').css({'color':'green', 'font-weight':'bold'}).text("Member saved!");
//$('#server-message').text('Member saved!');
var to = setTimeout(function() { Backbone.history.navigate('members', true); }, 2000);
},
bttnClickMemberSave: function() {
var data = this.getFormData($('#member-form').find('form'));
this.member.save(data, { success: this.eventSyncModelSaved });
},
// getFormData: function(form) {
// var unindexed_array = form.serializeArray();
// var indexed_array = {};
// $.map(unindexed_array, function(n, i){
// indexed_array[n['name']] = n['value'];
// });
// return indexed_array;
// },
render: function() {
this.member.toJSON();
var compiledTemplate = _.template( memberEditTemplate, { member: this.member } );
this.$el.html( compiledTemplate );
return this;
}
});
return MemberEditView;
});
Ok, I added backbone-extend.js to the RequireJS required files array in my app.js, now it's working.

Resources