Recently I decided to convert my code into typescript, which lead to an issue of substituting this from JavaScript to TypeScript.
here is the code I am struggling with.
TypeScript:
/// <reference path="../typings/tsd.d.ts" />
class FormAjaxSubmit {
form:JQuery;
opt:any;
constructor(element:string) {
this.form = $(element);
this.opt = {
boxID : "#info",
invData: {
tag : "[data-invalid]",
tagTrue: "[data-invalid='true']"
},
msg : {
success: "",
field : ""
}
};
// Even Listeners
this.form
.on('focusout', "[data-invalid]", e => {
console.log(this); // outputs form.ajax object instead of current field with data-invalid
this.formVisuals(); // this method needs to be accessible too along with function's this
e.preventDefault();
});
}
private formVisuals() {
}
}
$(() => {
// Finally, we kick things off by creating the **App**.
new FormAjaxSubmit("form.ajax");
});
I understand why it happens as this is assigned to main class by typescript. So it simply creates var _this = this on the top level and continues using throughout the code which prevents me from using old JavaScript style like following:
$("form.ajax").on("focusout", [data-invalid], function(e) {
console.log(this) // it outputs the current field with data-invalid tag instead of the form object itself.
e.preventDefault();
}
So the question is is the a way to substitute JavaScript this in typescript.
Function expressions exist in TypeScript too. They're not "old JavaScript style".
this.form.on("focusout", "[data-invalid]", function(e) {
console.log(this); // works
e.preventDefault();
});
Basically, don't use arrow functions in these cases.
If you want to use both the class and the element, then you could do this:
this.form.on("focusout", "[data-invalid]", (e) => {
this; // class
e.target; // element
});
Or if you want to keep using this as the element instead of the class you could do this:
let self = this;
this.form.on("focusout", "[data-invalid]", function(e) {
self; // class
this; // element
});
I would recommend keeping this as the class though and use an arrow method... it reduces confusion by keeping the meaning of this consistent.
Related
I am working on a task where I have an event listener window.addEventListener which is in javascript, based on event data I want to trigger a function of typescript and pass the data to that function. the point I am not getting is how to call typescript function in javascript. I have tried different things like a global variable, returning a value from js function but didn't work for me.
ngOnInit() {
(function ($) {
$(document).ready(function () {
window.addEventListener('message', function (event) {
console.log("Rece new Call event ibrahim " + JSON.stringify(event.data));
let obj: any = JSON.stringify(event.data)
obj = JSON.parse(obj);
console.log(obj)
if (obj.EventType === "handleContactIncoming") {
var num = obj.Number
// here i want to trigger Typescript function and pass this num to that function.
}
else if (event.data === "endCall") {
// return "No"
// var dbBtn = document.getElementById("db");
// dbBtn.click();
}
// $('.sidebar-menu').tree();
});
});
});
There is no difference when calling function from TS or JS. Finally there's always only JS in the browser, TS exists only in source code.
Also your code is a bit messy. There's no need to use jQuery inside angular (with an exception when you want to use some plugins or custom controls).
$(document).ready(function () {
is also redundant, if angular works the document is for sure ready.
Your code is quite messy and breaks separation of concerns. You should not use jQuery inside Angular. The injectable EventManager provides functionality for setting events on the window or document objects.
constructor(private readonly eventManager: EventManager) {}
ngOnInit(): void {
this.eventManager.addGlobalEventListener(target, event, () => {
this.hello();
});
}
hello(): void {
console.log('Hello World!');
}
How should I best go about overriding a JavaScript class method when it has been set up as per below. In this snippet, if I want to override the _other method from another JS file, loaded after this one, what is the correct way to go about it?
var review = {};
"use strict";
(function ($) {
review.list = {
_init: function () {
// The code I want to leave intact
},
_other: function () {
// The code I want to override
},
init: function () {
$(document).ready(function () {
review.list._init();
review.list._other();
});
}
};
review.list.init();
})(jQuery);
You can just assign to review.list._other. If you want to have access to the previous version, grab that first:
var oldOther = review.list._other;
review.list._other = function() {
// Your new code here, perhaps calling oldOther if you like
console.log("The new other code ran.");
};
Example:
// The original file
var review = {};
"use strict";
(function($) {
review.list = {
_init: function() {
// The code I want to leave intact
},
_other: function() {
// The code I want to override
},
init: function() {
$(document).ready(function() {
review.list._init();
review.list._other();
});
}
};
review.list.init();
})(jQuery);
// Your file after it
(function($) {
var oldOther = review.list._other;
review.list._other = function() {
// Your new code here, perhaps calling oldOther if you like
console.log("The new other code ran.");
};
})(jQuery);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
You're actually quite lucky it was written that way. It could easily have been written such that you couldn't override _other at all...
Slightly off-topic, but you've asked below:
Actually, does this class structure look reasonably sensible to you? Trying to dip toes into more OOP JS
I don't know your design constraints, so take anything that follows with a grain of salt... I should note that there's no "class" there at all (neither in the ES5 and earlier sense nor the ES2015 and later sense), just an object. (Which is fine.) But it looks like _init and _other are meant to be private; they could be genuinely private instead of pseudo-private without any cost — except then you wouldn't be able to override _other! :-) Separately, I would allow the overall controlling code to determine when the initialization happened instead of doing it on ready. (Separately, on a pure style note, I don't hold at all with this two-spaces-indentation nonsense so many of the l33t types seem to be promoting. If your code is so deeply nested that using only two spaces for an indent is necessary, it needs refactoring; four spaces is a good solid clear indent, in my view, without being so big it pushes your code off the right-hand side.)
So something like this if ES5 is required:
(function($) {
var list = {
init: function() {
_init();
_other();
}
};
function _init () {
// Can use `list` here to refer to the object
}
function _other() {
// Can use `list` here to refer to the object
}
review.list = list;
})(jQuery);
...but again, that makes it impossible (well, unreasonable) to override _other.
Or this if ES2015 and above is okay (for code this short, the differences are quite minor):
(function($) {
let list = {
init() {
_init();
_other();
}
};
function _init () {
// Can use `list` here to refer to the object
}
function _other() {
// Can use `list` here to refer to the object
}
review.list = list;
})(jQuery);
Just add your new override below... It will work...
var review = {};
"use strict";
(function($) {
review.list = {
_init: function() {
console.log('I\'m init')
},
_other: function() {
//This original will be overridden
console.log('Original')
},
init: function() {
$(document).ready(function() {
review.list._init();
review.list._other();
});
}
};
review.list.init();
})(jQuery);
review.list._other = function() {
console.log('overridden')
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I've got some code in JavaScript and I'm looking to trigger a ViewModel method using a keyboard shortcut. What is the correct syntax? Here's my code:
document.addEventListener('keydown', function(event) {
if (event.keyCode==27){
ViewModel.escapePressed();
}
}, true);
function ViewModel() {
this.escapePressed=function(){
// Code
};
}
If you are going to use that style of class, then you must first make an instance of it.
var a_view_model = new ViewModel();
a_view_model.escapePressed();
… but if you just want to have a static method, then you probably shouldn't be using a constructor function in the first place
var view_model = {
escapePressed: function () { };
}
and:
view_mode.escapePressed();
I created a Modal class like this:
export class Modal {
link: Link;
constructor (link: Link) {
this.link = link;
}
create() {
this.link.Modal.$Modal = $.modal({ });
}
addSubmit() {
this.link.Modal.$Form.submit(function () {
var a = this.link;
submitHandler(this.link);
return false;
});
}
}
Here is the code that I am using:
var modal = new Modal(link);
modal.create();
modal.addSubmit();
There is a problem here. I want to pass the link as a parameter to the submitHandler function. However it seems it's not there. Also when I check everthing on the line "var a = this.link" it seems that the variable "a", the "this" and the "link" all show as "any" when I hover over them.
You have discovered JavaScript scope - which can be confusing at first.
JavaScript is functionally scoped. This means that the variables belong to the function they are declared in.
When the variable is declared outside the function, JavaScript attempts to walk up the chain to find the variable.
This is confusing when the context you type the code in is not the context your code is called in. In your case, this in the context of the submit function is the element that is being submitted (a form element for example). It isn't the class where you write the submit function.
Here is an update that should work:
export class Modal {
link: Link;
constructor (link: Link) {
this.link = link;
this.link.Modal.$Modal = $.modal({ });
}
addSubmit() {
var _this = this;
this.link.Modal.$Form.submit(function () {
var a = _this.link;
submitHandler(_this.link);
return false;
});
}
}
In this example we put this into a variable with a name that won't be overridden so the JavaScript runtime won't stop looking until it finds it. (It will stop looking for this straight away as it will have it defined locally as the form element). As there is no local _this JavaScript will walk up the chain until it discovers our variable, which represents the class.
There is also a short-hand in TypeScript to do this, which results in the same JavaScript as we have hand-written above:
export class Modal {
constructor (public link: Link) {
this.link.Modal.$Modal = $.modal({ });
}
addSubmit() {
this.link.Modal.$Form.submit(() => {
var a = this.link;
submitHandler(this.link);
return false;
});
}
}
Lastly, you don't want calling code to have to rely on calling functions in a specific order for things to work. This is an example of exposing the internal workings of your class, so I have updated the usage of the class to be:
var modal = new Modal(link);
modal.addSubmit();
I am assuming that addSubmit is optional - otherwise the Modal class should do that too without the calling code having to know about it.
JSFiddle: http://jsfiddle.net/M2ALY/3/
My goal is to make a module that I can use and distribute. Therefore I must not pollute the global namespace. The module I'm making is also going to be used multiple times on one web page. That's why I chose to use OOP, but this introduced a problem.
I want my object to bind a function to be run when the user clicks an element in the DOM. In this simplified example I made, I want an alert box to pop up when the user clicks a paragraph. As an example, one of the things I need in the real project I'm working on is: The user clicks a canvas, the function figures out where the user clicked and saves it to this.clientX and this.clientY.
Instead of doing
this.bind = function() {
$("p1").bind('click', function() {
// code here
});
}
I figured it would work if I did:
this.bind = function() {obj.codeMovedToThisMethod()}
The problem is that this isn't a good design. Inside the "class" you shouldn't need to know the name of the object(s) that is going to be made of this "class". This doesn't get better when I'm making multiple objects of the "class"...
So I figured I could do
$("p1").bind('click', function(this) {
// code here
});
}
But it didn't work because sending this into the function didn't work as I thought.
How should I solve this problem?
Here is a simplified sample problem. (Same as JSFiddle.)
var test = function() {
this.alert = function() {
alert("Hi");
}
this.bind = function() {
$("#p1").bind('click', function() {
obj.alert();
});
}
}
window.obj = new test();
obj.bind();
// What if I want to do this:
var test2 = function() {
// Private vars
this.variable = "This secret is hidden.";
this.alert = function() {
alert(this.variable);
}
this.bind = function() {
$("#p2").bind('click', function(this) {
obj2.alert();
this.alert();
});
}
}
window.obj2 = new test2();
obj2.bind();
Thanks!
Read MDN's introduction to the this keyword. As it's a keyword, you can't use it as a parameter name.
Use either
this.bind = function() {
var that = this;
$("#p2").on('click', function(e) {
that.alert();
// "this" is the DOM element (event target)
});
}
or $.proxy, the jQuery cross-browser equivalent to the bind() function:
this.bind = function() {
$("#p2").on('click', $.proxy(function(e) {
this.alert();
}, this));
}