Javascript Class and extend - javascript

I don't quite know how to phrase this so I'll just dive ahead and try to explain it as best as I can.
I don't quite know how to store class properties without using them, or something like that.
If we have a class of Message that contains a method called addMessage.
We want to use this method. inside of a class called Magic.
How exactly do we do this?
class Message {
constructor(messageContainer) {
this.message = messageContainer;
}
addMessage(msg) {
this.message.innerText = msg;
}
}
class Magic extends Message {
constructor(magic) {
super();
this.magic = magic;
}
something() {
this.addMessage('Some random message');
}
}
We do:
const message = new Message(document.getElementById('message-container'))
in the code but never use message anywhere.
Instead we use Magic like so:
const magic = new Magic(something)
and then:
element.addEventListener('click', () => magic.something())
This does not work, it does seem logical to why it doesn't, its because message never gets used, but how could we make use of the addMessage method inside of Magic though?
I've tried a few things but nothing seems to work except for doing document.getElementById inside of the super(), but that seems to me like it defeats the point of using classes if I can't call it somehow in the code and have it keep the reference to the message container...
Any ideas?

The Magic constructor needs to take a messageContainer parameter to pass along to the Message constructor.
class Magic extends Message {
constructor(messageContainer, magic) {
super(messageContainer);
this.magic = magic;
}
something() {
this.addMessage('Some random message');
}
}
const magic = new Magic(document.getElementById('message-container'), something)
If you have lots of parameters and don't want to have list them all when calling super(), use an object to hold them instead of using seperate parameters. Each class can use the object properties that pertain to it.
class Message {
constructor(options) {
this.message = options.messageContainer;
}
addMessage(msg) {
this.message.innerText = msg;
}
}
class Magic extends Message {
constructor(options) {
super(options);
this.magic = options.magic;
}
something() {
this.addMessage('Some random message');
}
}
magic = new Magic({
messageContainer: document.getElementById("message-container"),
magic: something
});

Instead of inheritance, your use case is composed(Behavioral design patterns). Combining 2 objects to work together. here is basic sample to solve this.
class MessageContainer {
constructor(messageContainer) {
this.message = messageContainer;
}
addMessage(msg) {
this.message.innerHTML = msg;
}
}
class Magic {
constructor(msgContainer) {
this.container = msgContainer;
}
something() {
this.container.addMessage("Some random message"+ new Date().getTime());
}
}
const container = new MessageContainer(
document.getElementById("message-container")
);
const magic = new Magic(container);
document.addEventListener("click", () => {
magic.something();
});
<div style="height: 100px; color: red;">
click here
<br/>
<span id="message-container"></span>
</div>

Related

How to update the values of all custom element instances in Javascript?

I began to use Web Components in Javascript. I have a class called VariantSelects and I want each instance to have the same value. So whenever something changes in instance A, instance B should receive the same value. How do I do that?
I minimized my code for presentation purpose. The actual amount of functions is more complex.
<!-- instance A -->
<variant-selects></variant-selects>
<!-- instance B -->
<variant-selects></variant-selects>
class VariantSelects extends HTMLElement {
constructor() {
super();
this.currentVariant = null;
this.addEventListener('change', this.onVariantChange);
this.init();
}
init() {
// do stuff
}
onVariantChange() {
// should be updated in all instances
this.currentVariant = 123;
}
}
customElements.define('variant-selects', VariantSelects);
Thanks in advance!
I think I figured it out, although this might not be the best solution...
const allVariantRadios = document.querySelectorAll('variant-radios');
Array.from(allVariantRadios).forEach(radio => {
radio.addEventListener('change', updateAllInstances)
});
function updateAllInstances(e) {
allVariantRadios.forEach(instance => {
instance.querySelector(`input[value="${e.target.value}"]`).checked = true;
instance.onVariantChange();
});
}

How to override a function in a class being returned from module in javascript

I have a logging class in a module which contains a static function to return an instance of itself, so a simplified version looks like this:
Class Logger {
static createFromConfig(key) {
return new Logger(key);
}
info(message) {
// do logging stuff
}
debug(message) {
// do logging stuff
}
error(message) {
// do logging stuff
}
}
This is used in many places throughout our codebase like:
import logging from 'logging';
const log = logging.Logger.createFromConfig('keyname');
log.info('App starting');
In one project I wish to add some extra functionality to the error function, without having to change code in any of the places calling it. So something like
log.error = () => {
// do my extra stuff here
log.error() // call the original error function
}
I've tried extending the class like below but I think the problem is the original base class just gets returned from the static function so my overridden function isn't called:
class dblogger extends logging.Logger {
error(...args) {
// do my extra stuff here
super.error();
}
}
const dblogging = {
Logger: dblogger
};
export default dblogging;
You need to call the original method from the Logger prototype.
function getdblogger(keyname) {
const log = logging.Logger.createFromConfig(keyname);
log.error = function(message) {
// do my extra stuff here
Logger.prototype.error.call(this, message) // call the original error function
}
return log;
}
Note that you have to use an ordinary function rather than an arrow function so it gets this.
If you can get changes made to the original Logger class, you could chage createFromConfig() to allow the class to be specified, then you could use your subclass.
Class Logger {
static createFromConfig(key, loggerClass = Logger) {
return new loggerClass(key);
}
info(message) {
// do logging stuff
}
debug(message) {
// do logging stuff
}
error(message) {
// do logging stuff
}
}
Then in your files:
const log = logging.Logger.createFromConfig('keyname', dblogger);
I believe you really want to use this.constructor.prototype.error within your method:
class Logger{
static createFromConfig(key){
return new Logger(key);
}
info(message){
// do logging stuff
}
debug(message){
// do logging stuff
}
error(message){
return message;
// do logging stuff
}
}
class logging extends Logger{
}
const log = logging.createFromConfig('test');
log.error = function(message){
console.log(this.constructor.prototype.error('Logger message here'));
console.log(message);
}
log.error('within log');

Enki JS Exercise

Function greet!(name)
{
this.name = name!;
this.superpowers! = {
hello!: function!()
{
console.log('hello there')
}
}
}
const flash = new Superhero('Flash')
Flash.superpowers.greet()
Everything with an explanation mark after the name is something I've chosen (e.g., greet!)
I'm trying to get acquainted with JS and this Enki app is a quick walkthrough/cheat sheet. This solution seems right to me (drag and drop words) but it's not correct. Please help, I've googled functions and naming conventions and can't see where my error is
Are you trying like this? See the below snippet
class Superhero{
constructor(name){
this.name = name
}
superPowers(){
return {
hello: () => {
console.log(this.name)
}
}
}
}
const flash = new Superhero("John Wick")
console.log(flash.superPowers().hello())

Javascript ES5: How to extend the Function object in order to set a property value on itself, every time the function is executed?

I currently have the following working code:
Function.prototype.GetLastCallerName = function () {
if (!this.arguments || !this.arguments.callee || !this.arguments.callee.caller) return null;
var result = /^function\s+([\w\$]+)\s*\(/.exec(this.arguments.callee.caller.toString());
this.LastCaller = result ? result[1] : 'Anonymous';
return this.LastCaller;
};
I picked up that code from another thread. As you can see, it extends the Function.prototype in order to add a method called GetLastCallerName, which picks the last calling function name and (1) sets it to LastCaller on Function.LastCaller and (2) returns it.
In order to make it work:
function MyFunction1() {
MyFunction1.GetLastCallerName();
console.log(MyFunction.LastCaller);
}
function MyFunction2() {
MyFunction1();
}
MyFunction2();
What I'd like to be able to do: Eliminate the need to use GetLastCallerName() every time and extend Function in order to perform that get every time any function is called.
I'm struggling to follow what you have tried so far with your example, but I think I get the idea of what you'd like to do. Why not leverage classes, and extend on them for your use case. Check out the following example...
class Base {
baseFn() {
console.log('from base');
}
}
class Thing extends Base {
fn1() {
this.baseFn();
}
}
let thingee = new Thing();
thingee.fn1();
So baseFn is now always called when fn1 is called.
JSFiddle Link - class demo
In some of your comments it looks like you are wanting to get the "last calling function's name." How about passing back the instance of the caller itself to the parent? This would surely give you even more flexibility because now you can sculpt your caller however you wish. Check out the following...
class Base {
baseFn(caller) {
console.log(caller.id); // 1
}
}
class Thing extends Base {
constructor(id) {
super();
this.id = id;
}
fn1() {
this.baseFn(this);
}
}
let thingee = new Thing('1');
thingee.fn1();
Now you can add whatever you'd like to your Thing instance, in this case, an object with an id of 1 which can be inspected when fn1 propagates up to baseFn
JSFiddle Link - caller demo

How can I fix 'warning Expected 'this' to be used by class method ' eslint error?

I am creating a PDF like this inside a react Component.
export class Test extends React.PureComponent {
savePDF() {
const source = document.getElementById('printContainer');
/* eslint new-cap: ["error", { "newIsCap": false }]*/
let pdf = new jspdf('p', 'pt', 'letter');
let margins = { top: 50,
left: 60,
width: 612
};
pdf.fromHTML(
source,
margins.left,
margins.top,
{
width: margins.width
},
() => {
pdf.save('worksheet.pdf');
}
);
}
and I am getting warning Expected 'this' to be used by class method 'savePDF' class-me
this is being called an click like this onClick={this.savePDF} see below
render() {
<Link
name="save-to-pdf"
onClick={this.savePDF}
button="secondary">
Save to PDF</Link>
<div id="printContainer" className="cf-app-segment--alt cf-hearings-worksheet">...
There are two different answers to this question, depending on how you want to handle it.
First, the reason you get this error is because of the ESLint rule https://eslint.org/docs/rules/class-methods-use-this. Specifically, this is because if something is a class method, e.g. if you are calling this.foo() to call a function, the whole reason to make it a method is because there are properties on this that you need to use.
While in many languages with class, most functions are methods, that is not the case in JS. If you have a class like
class Example {
constructor(){
this.data = 42;
}
someMethod() {
this.someHelper(this.data);
}
someHelper(value){
console.log(value);
}
}
the someHelper function would trigger the same error you are getting, because it never uses this, so you can just as easily do
class Example {
constructor(){
this.data = 42;
}
someMethod() {
someHelper(this.data);
}
}
function someHelper(value){
console.log(value);
}
In your case, you can do this. Your whole savePDF function could be moved outside of the class object.
That said, it is important to ask yourself why something like this isn't using this. In most cases, you'd expect any function that works with HTML to absolutely use this, because how else, in React, is it supposed to access the element's that React has created.
So the real answer to your question would be to drop the
const source = document.getElementById('printContainer');
line. If you need access to the HTML element being created by React, you should be using React's APIs to do so. That would be done with something like
class SavePDFButton extends React.Component {
constructor(props) {
super(props);
this.printContainer = null;
this.savePDF = this.savePDF.bind(this);
this.handlePrintContainerRef = this.handlePrintContainerRef.bind(this);
}
render() {
return (
<div>
<Link
name="save-to-pdf"
onClick={this.savePDF}
button="secondary"
>
Save to PDF
</Link>
<div
id="printContainer"
className="cf-app-segment--alt cf-hearings-worksheet"
ref={this.handlePrintContainerRef}
/>
</div>
);
}
handlePrintContainerRef(el) {
// When React renders the div, the "ref={this.handlePrintContainerRef}" will
// make it call this function, which will store a reference.
this.printContainer = el;
}
savePDF() {
// OLD: const source = document.getElementById('printContainer');
const source = this.printContainer;
// ...
}
}
I believe that's caused by the class-methods-use-this ESLint rule.
It's just letting you know that your function doesn't use this, so you can probably make it a static function.
turn it into static function
static savePDF() { ... }
Its happening because this function isnt using this meaning it dosnt need to be dynamic

Categories