I am writing a script for a project, it has a function to collapse or open the sidebar on click on hamdurger icon but when I click it gives me error
TypeError: this.CollapseSidebar is not a function
The following is my code:
(function($) {
'use strict';
var Prtm = {
Constants: {
LEFTMARGIN:'315px',
COLLAPSELEFTMARGIN: '63px',
},
PrtmEle:{
BODY: $('body'),
SIDEBAR: $('.prtm-sidebar'),
SIDENAV: $('.sidebar-nav'),
MAIN: $('.prtm-main'),
HEADER: $('.prtm-header'),
CONTENTWRAP: $('.prtm-content-wrapper'),
CONTENT: $('.prtm-content'),
PRTMBLOCK: $('.prtm-block'),
FOOTER: $('.prtm-footer'),
HAMBURGER: $('.prtm-bars'),
},
Init:function(){
this.BindEvents();
},
BindEvents:function(){
this.PrtmEle.BODY.on('click',this.PrtmEle.HAMBURGER,function(){
this.CollapseSidebar();
});
},
CollapseSidebar: function(){
this.PrtmEle.HAMBURGER.toggleClass("prtm-sidebar-closed is-active");
this.PrtmEle.BODY.toggleClass("prtm-sidebar-closed is-active");
this.PrtmEle.SIDEBAR.toggleClass('collapse');
},
};
Prtm.Init();
})(jQuery);
When I change this.CollapseSidebar to Prtm.CollapseSidebar it works properly. What I am doing wrong here and how it can be resolved?
Why it do not work? Because a function() binds its own this.
One thing you can do is to use Arrow function which do not bind its own this - like this:
BindEvents:function(){
this.PrtmEle.BODY.on('click',this.PrtmEle.HAMBURGER,() => {
this.CollapseSidebar();
});
}
Inside you click function this will refer to the window - so either you can create a temporary variable like the below or use the arrow function:
BindEvents:function() {
let $this = this;
this.PrtmEle.BODY.on('click',this.PrtmEle.HAMBURGER,function() {
$this.CollapseSidebar();
});
}
Related
When user clicks on .step, the jquery will execute the hello function which works fine. The hello function will call hi function => I got error here: Hi is not a function
jQuery(document).ready(function( $ ) {
const form = {
init: function() {
$('.step').on('click', this.hello)
},
hello: function() {
console.log('Index: ', $(this).index()); // I can get the right index
this.hi(); // ERROR: Hi is not a function!
},
hi: function() {
console.log('HI')
},
}
form.init()
})
When I put the code like this, it works fine
jQuery(document).ready(function( $ ) {
const form = {
init: function() {
$('.step').on('click', this.hi) // Look here
},
hi: function() {
console.log('HI')
},
}
form.init()
})
Can you guys explain why?
Hi is not a function
Can any one explain why?
In this code
init: function() {
$('.step').on('click', this.hello)
},
hello: function() {
console.log('Index: ', $(this).index()); // I can get the right index
this.hi(); // ERROR: Hi is not a function!
},
you setup form.hello as an event handler for the click event. Within event handlers, this is the element that was clicked, as in:
$("button").click(function() { console.log(this.id); }
the button will (given this code) not have a .hi method.
How can we avoid the binding using Es6 or arrow function?
If you don't need to know which button was clicked, you can use an arrow function when defining the event handler:
init: function() {
$('.step').on('click', () => this.hello())
},
Example: https://jsfiddle.net/mcuh5awt/2/
Ideally, you'll want to access both the button and the form class, this is possible in two ways, the first is to change how you define your class, but keeping with how you've done it, we can add a new property self to store a reference to its ...well... self...
jQuery(document).ready(function( $ ) {
const form = {
self : {},
init: function() {
self = this;
$('.step').on('click', self.hello)
},
hello: function() {
console.log('Index: ', $(this).index());
self.hi();
},
hi: function() {
console.log('HI')
},
}
form.init()
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<button class='step' type='button'>
Step 1
</button>
<button class='step' type='button'>
Step 2
</button>
</div>
As this becomes a public property of your class, you may prefer a different name in this case eg form to match the class name.
I'm am using module js syntax and I have some code like this:
var myModule = {
settings: {
myage: 25
},
init: function() {
//init code here
},
someFunction1: function(param1) {
//function code here
},
someFunction2: function() {
myModule.someFunction1(myparam);
}
}
The above will work fine but if I tried:
someFunction2: function() {
this.someFunction1(myparam);
}
It will not find the function.
Can't I use this.someFunction1... ?
this is who called the function.
myModule.someFunction2() will have this=myModule
someFunction2 = myModule.someFunction2; someFunction2() will have this=window
(you didn't show how you're calling it though)
I've just developed this JavaScript/Backbone module as a part of a web page I am developing. I would like to create a Jasmine test for it, but I am brand new to Jasmine, therefore I am not sure what should I be testing in this class. What should be the "skeleton" of the test? In order to avoid redundancy in tests, what parts will you test?
editdestinationview.js:
define([
'common/jqueryex',
'backbone',
'marionette',
'handlebars',
'text!education/eet/templates/editdestination.hb',
'text!common/templates/validationerror.hb',
'lang/languageinclude',
'common/i18nhelper'
], function ($, Backbone, Marionette, Handlebars, templateSource, errorTemplateSource, i18n) {
'use strict';
var errorTemplate = Handlebars.compile(errorTemplateSource),
EditDestinationView = Marionette.ItemView.extend({
initialize: function (options) {
this._destinationTypes = options.destinationTypes;
},
onRender: function () {
this.stickit();
this._bindValidation();
},
_bindValidation: function () {
Backbone.Validation.bind(this, {
valid: this._validAttributeCallback,
invalid: this._invalidAttributeCallback,
forceUpdate: true
});
},
_validAttributeCallback: function (view, attr) {
view.$('#error-message-' + attr).remove();
},
_invalidAttributeCallback: function (view, attr, error) {
view.$('#error-message-' + attr).remove();
view.$('#destinationTypes').parent('div').append(errorTemplate({
attr: attr,
error: error
}));
},
template: Handlebars.compile(templateSource),
ui: {
saveAnchor: '#ed_eetSaveDestinationAnchor',
deleteAnchor: '#ed_eetDeleteDestinationIcon'
},
triggers: {
'click #ui.saveAnchor': 'click:saveDestination',
'click #ui.deleteAnchor': 'click:deleteDestination'
},
bindings: {
'select#destinationTypes': {
observe: 'destinationTypeId',
selectOptions: {
collection: function () {
return this._destinationTypes;
},
labelPath: 'description',
valuePath: 'destinationTypeId',
defaultOption: {label: i18n.EDUCATION_EET_SELECT_INTENDED_DESTINATION, value: null}
}
}
}
});
return EditDestinationView;
});
Thanks everyone!
UPDATE:
After thinking a lot about it, I think that I should try these aspects:
-Triggers: Check if they can be clicked.
-"_validAttributeCallback" and "_invalidAttributeCallback": Check if they behave accordingly to the code.
-Template: Spy on it to check if it is performing it's mission. (Optional test)
So, the test skeleton will be:
define([
'education/eet/views/editdestinationview'
], function (EditDestinationView) {
describe('description...', function () {
beforeEach(function () {
//EditDestinationView.triggers
});
describe('blablabla', function () {
beforeEach(function () {
// ...
});
it('blablabla', function () {
// blablabla
});
});
});
});
Any help on how to test this please?
One common pattern is to use two describe statements, one for the class and one for the method being tested, and then an it statement for each thing you want to test about that method. The rspec people have a convention (which I use in my JS tests) of using a '#' on the method describe for an instance method, and a "." for a describe of a static method.
Now, if you adopt all of the above, and you want to test (for instance) that your View's click-handling method triggers a certain event on the View's Model, it would look something like this:
define([
'education/eet/views/editdestinationview'
], function (EditDestinationView) {
describe('EditDestinationView', function () {
var view;
beforeEach(function () {
// do setup work that applies to all EditDestinationView tests
view = new EditDestinationView({model: new Backbone.Model()});
});
describe('#handleClick', function () {
beforeEach(function () {
// do setup work that applies only to handleClick tests
});
it('triggers a foo event', function () {
var wasTriggered;
view.model.on('foo', function() {
wasTriggered = true;
});
view.handleClick();
expect(wasTriggered).toBe(true);
});
});
});
});
P.S. Instead of creating a fake "foo" handler like I did, most people use a mocking library like Sinon. Using that library our "it" statement could instead be:
it('triggers a foo event', function () {
var triggerStub = sinon.stub(view.model, 'trigger');
view.handleClick();
expect(triggerStub.calledOnce).toBe(true);
expect(triggerStub.args[0][0]).toBe('foo');
//NOTE: args[0][0] == first arg of first call
});
I have two items that should be visible only when a given observable evaluates to false, and one item that should be visible when the same item evaluates to true. It works fine if I only have two items whose visibility depends on the observable. However, when a third item is introduced, it stops working.
ViewModel:
var viewModel = {
editable: ko.observable(false),
edit: function () {
this.editable(true);
},
delete: function () {
this.editable(true);
},
cancel: function () {
this.editable(false);
},
save: function () {
this.editable(false);
}
};
ko.applyBindings(viewModel);
A fiddle is available here: http://jsfiddle.net/mupersan82/Y6eKS/10/
The fiddle is working. Uncomment one of the "button" items to see the problem.
Can anyone identify what the problem is?
Regards, Anders
delete is a Javascript operator and therefore a reserved word (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete). Renaming the function fixes it:
var viewModel = {
editable: ko.observable(false),
edit: function () {
this.editable(true);
},
remove: function () {
this.editable(true);
},
cancel: function () {
this.editable(false);
},
save: function () {
this.editable(false);
}
};
ko.applyBindings(viewModel);
(fiddle: http://jsfiddle.net/Y6eKS/11/)
I have a function inside a toolbar, let's call it:
Ext.define('MyArchive.Toolbar', {
search: function() {
console.log('searching');
}
}
Now I'd like to call this function when clicking a button. So I'm adding some click handlers in the afterRender on the toolbar setup:
afterRender: function() {
Ext.getCmp('search-button').on('click', this.search);
}
However, this doesn't work and I ultimately need to go the full route of:
afterRender: function() {
Ext.getCmp('search-button').on('click', function() {
quick_search();
)};
}
Any particular reason why my first attempt doesn't apply the click handler as I expect?
Thanks for any explanations or refactorings! Additional patterns/idioms welcome...
Next try:
var panelOverall = new Ext.form.FormPanel({
html: 'bla',
search: function() {
console.log('searching');
},
buttons: [
{
text: 'Moo',
id: 'button1',
handler: function(){
//window.destroy();
}
}
],
afterRender: function() {
Ext.getCmp('button1').on('click', this.search);
}
});
is working for me.. am I missing something?