user2290820 user2290820 - 6 months ago 53
jQuery Question

Backbone.js TypeError: Cannot read property of undefined

I am trying out Backbone.js and pretty new to it but cannot pin down on binding this.listenTo properly in the View initialize function in the following code.

This backbone code is a separate js file.

'use strict';

let Preloadcart = function ( $, Backbone, _ ) {
// console.log(Backbone);
console.log(this);
let
// fnSelf = this,
// Model-each unit comprises of this
Service = Backbone.Model.extend({
defaults:{
title:'',
price: 0,
checked: false
},

type: 'Service',

toggleCheck: () => {
this.set('checked', !this.get('checked'));
}
}),

// Collection- of such Service instances
ServiceList = Backbone.Collection.extend({

// collection based on this model
model: Service,

type: 'ServiceList',

// get model instances with attribute checked=true
getChecked: () => {
console.log(`checking ${this.type}`);
return this.where({checked:true});
},
}),

services = new ServiceList([
new Service({ title: 'web development', price: 200}),
new Service({ title: 'web design', price: 250}),
new Service({ title: 'photography', price: 100}),
new Service({ title: 'coffee drinking', price: 10})
// Add more here
]),

// View of each Service model unit
ServiceView = Backbone.View.extend({
tagName: 'li',

type: 'ServiceView',

events: {
'click': 'toggleService'
},

initialize: () => {
console.log(`initializing ${this.type}`);
_.bindAll(this, 'toggleService', 'render');

this.listenTo(this.model, 'change', this.render);
},

toggleService: () => {
this.model.toggle();
},

render: () => {
let
$frag = $(document.createDocumentFragment()),
$htmlCheckbox = $('<input>', {
type:'checkbox',
value: 1,
name: this.model.get('title'),
}),
$htmlSpan = $('<span>', {
value: `\$${this.model.get('price')}`,
});

$htmlCheckbox.append(this.model.get('title'));
$htmlCheckbox.append($htmlSpan);
this.$htmlCheckbox.prop('checked', this.model.get('checked'));

$frag.append($htmlCheckbox);
this.$el.append($frag);

return this;
}
}),

App = Backbone.View.extend({
el: $('#main'),

type: 'App',

initialize: function () {
// listen on the collection instance of ServiceList, services
console.log(`initializing App ${this.type}`);
let
view, that= this;
this.total = 0,
this.collection = services;

_.bindAll(this, 'render');

this.listenTo(this.collection, 'change', this.render);

_(this.collection.models).each( (model) => {
view = new ServiceView();
view.set({model: model});
that.$('#services').append(view.render().el);
}, this);
},

render: () => {

_.each(this.collection.getChecked(), (each) => {
this.total += each.get('price');
});

this.$('#total-value').text(`\$${this.total}`);
}
}),

AppView = new App();

// new App();
// return App;

};


export default Preloadcart;


This is called in the main js file something like this:

$(() => {
Preloadcart($, Backbone, _, that);
});


but throws the error:

preloadCart.js:57 Uncaught TypeError: Cannot read property 'type' of undefined


The problem as I trace in the error log seems here:

_.bindAll(this, 'toggleService', 'render');


and

console.log(`initializing ${this.type}`);


under ServiceView
intialize
function;

this
is undefined when I output. what am i missing?

Let me know if there's any confusion about the code, I will try to make this simpler.

Answer
initialize: () => {
        console.log(`initializing ${this.type}`);
        _.bindAll(this, 'toggleService', 'render');

        this.listenTo(this.model, 'change', this.render);
      },

The Arrow Operator is called when the initialize function is defined. just when the Preloadcart is called. So in function initialize,'this' is not the viewobject,but the 'this' when Preloadcart is called. use normal function.

initialize: function() {
            console.log(`initializing ${this.type}`);
            _.bindAll(this, 'toggleService', 'render');

            this.listenTo(this.model, 'change', this.render);
          },