undefined variables in javascript class - javascript

I'm having trouble running and understanding classes in javascript. I am experimenting this with a simple app that I made. The end goal of it is just to grab names from a textarea, store that in an array, and then perform actions on it such as displaying them to the screen or shuffling them. To do this, I made 2 classes. One for storing the names to an array and then another that extends from this class to manipulate the array - just for the sake of organization. Here's my code:
Store names to array
import $ from 'jquery';
class SaveInput {
// dom selection usually and firing events when a page loads.
constructor(){
this.mainText = $('.main-text');
this.names = [];
this.events();
}
//events to watch for such as click
events() {
// save names to array, no submit button
this.mainText.blur(this.saveNameIndex.bind(this));
}
// methods to be called from events
// save names without submitting
saveNameIndex() {
let namesResult = this.names = this.mainText.val().split('\n');
return namesResult;
}
}
export default SaveInput;
Maniuplate Array
import $ from 'jquery';
import SaveInput from './SaveInput';
class Display extends SaveInput {
// dom selection usually and firing events when a page loads.
constructor(names){
super(names);
this.h1= $('h1');
this.events();
}
//events to watch for such as click
events(){
this.h1.click(this.log.bind(this));
}
// methods to be called from events
//display images with names
log () {
console.log('test');
}
}
export default Display;
It doesn't matter what I do, as you can see from my 2nd class that I was just trying to test by click an h1 tag to console log some text. I always get the same error when the app loads:
App.js:10522 Uncaught TypeError: Cannot read property 'click' of undefined
at Display.events (App.js:10522)
at Display.SaveInput (App.js:96)
at new Display (App.js:10509)
at Object.defineProperty.value (App.js:10412)
at __webpack_require__ (App.js:20)
at Object.defineProperty.value (App.js:63)
at App.js:66
The only thing I want from the saveInput class is the names array so I can pass it on and there seems to be some conflict when I extend that class. What am I missing?

When you create a Display object, it first calls the base object's constructor. That SaveInput constructor calls this.events() which you have overridden in the derived Display class. That overridden implementation is expecting this.h1 to be set, but it isn't set yet, thus the error you see.
I don't really understand what you're trying to do here to know what fix to suggest, but this is a timing issue based on what happens in the constructor and what the derived class is ready for. Probably you should take some stuff out of the constructor so the object can get fully formed before you call this.events().
Also, if you expect the SaveInput version of events() to get called, you need to add super.events() to the Display implementation of events() so the base object's version gets called too.

Related

is there a way to put the method functions of a class, in separate files (javascript)?

if there is a way to split and make the code of every method in a separate file?
so I can have clean code and be simple and easier to maintain.
for now, I have a structure like this (minimal reproducible example):
// there is also a parentClass
// and I want to make sure that the method be inside parent
// so I can modify all the childs
// with the same method
class Parent {
// myMethod.js
myMethod() {
// my long code
}
//secondMethod.js
secondMethod() {
// my long code
}
// thirdMethod.js
thirdMethod() {
// my long code
}
}
class Child extends Parent {
constructor() {
this.someData = "someData";
}
}
// this need to work after putting the file externally for example
new Child.myMethod();
but I want that the methods be in separate files
something like this:
the problem is how can a method know that is been part of a specific Class and not another Class for example.
for example If we want to separate the Child from parentClass, this is easy:
thanks to import, export, extends
import ParentClass from './ParentClass.js';
// here since we have "extends" the child class know that is been part of Parent.
export default class Child extends ParentClass {
// my child things and it will work
}
// but methods don't have extends or similar thing?
// and if yes how we can export them?
// export as a function?
// (but they aren't function, they are methods?)
so what do I want?
method -> file
file -> method can be exported
method -> parentClass can import it
import -> method be part of parentClass
method ParentClass -> get extended to all childs
method extended -> read (and change) correctly the values of this. (like it was before inside with code splitting)
Some languages support partial classes, which is similar to what you are requesting. They don't get used a great deal as they make it hard to understand a class when it's internals are scattered around your project.
A more OOP way to solve your problem is to look at the parts you label // my long code.
You can usually break up that code and place it in other classes that you delegate the request to. For example, if your long code does many things:
myMethod(input) {
// Map the input into a different shape
// Part of the long code
// Perform some form of calculation on some values
// Another part of the long code
// Create a response object and return it
// This part probably isn't too long
return response;
}
You might be able to split these up by creating a class that does mapping and a class that does the calculation. This means the mapping and calculation are easily used from other places and reduces the length of code in your method:
myMethod(input) {
const mapped = this.specificMapper.map(input);
const response = this.specificCalculator.calculate(mapped);
return response;
}

Code control couldn't go inside the child class constructor in javascript

I have been learning squish to automate a desktop application and I'm using Javascript at language to write my tests.
So I have these three following classes in my code : Actions, Button and AppElements. And AppElement class creates an instance of Button elements which is inheriting Actions class.
import * as names from 'names.js';
class Actions{
constructor(appObject){
this.object=(appObject);
}
doubleclick(){
doubleClick(waitForObjectExists(this.object));
}
}
class Button extends Actions{
constructor(anElement){
this.object=anElement;
super(anElement);
}
}
class AppElements {
get dashboardButton() { return new Button(names.contentButtonDashboardWcPushButton);}
}
export
{
Actions,
AppElements,
Button
};
import * as names from 'names.js';
function main() {
// attach the desktop application
let dash= new test.AppElements();
dash.dashboardButton.doubleclick();
}
But when the property dashboardButton() is being called from the test script(from the main function) then it first goes to line constructor(appObject){ but does not enter in the constructor block and gives following error
error Detail Attempted to access uninitialized this value. Forgot to call super() in derived class?
And because of that the Actions class can never be called and I cannot access any methods of the Actions class.
Would it be great if i could get some help to fix the problem
Here it says...
When used in a constructor, the super keyword appears alone and must be used before the this keyword is used. The super keyword can also be used to call functions on a parent object.
So try:
class Button extends Actions {
constructor(anElement) {
super(anElement);
this.object = anElement;
}
}
Related information:
JavaScript extensions in Squish 6.6
Super

Observable / Subject loosing subscribers in Angular

I'm using a Static variable in my Class to store an initialised BehaviourSubject, so that I can provide a default, while I load the user's settings from the server.
(have put a cut down example version below)
#Injectable
export class AppSettings {
// Using a static to globalize our variable to get
// around different instances making lots of requests.
static readonly currency: Subject<string> = new BehaviorSubject('USD');
// Return a property for general consumption, but using
// a global/static variable to ensure we only call once.
get currency(): Observable<string> { return AppSettings.currency; }
loadFromServer():any {
// Broadcast the currency once we get back
// our settings data from the server.
this.someService.getSettings().subscribe(settings => {
// this is called lastly, but AppSettings.currency.observers
// seems to show as an empty array in the Inspector??
AppSettings.currency.next(settings.currency);
});
}
}
When I subscribe to it later in my code, it will run through it once (since it's a BehaviorSubject), but it won't fire after that.
export class myComponent {
public currency: string;
constructor(settings: AppSettings) {
// Called once with the default 'USD'
settings.currency.subscribe(currency => {
// only gets here once, before loadFromServer
console.log(currency);
this.currency = currency;
});
// Load from the server and have our subscription
// update our Currency property.
settings.loadFromServer();
}
}
The loadFromServer() is working exactly as expected, and the AppSettings.currency.next(settings.currency) line is being called, and after the first event. What is interesting however, is at this point, the AppSettings.currency.observables[] is empty, when it was previously filled in.
My thoughts we're initially an issue of different instances, but I'm using a static variable (have even tried a global one) to avoid different instances.
This is the current workflow...
myComponent.constructor subscribes
that subscription fires, giving the default 'USD'
the server data is loaded, and AppSettings.currency.next(settings.currency) is called
...then...nothing....
I'm expecting that at part 4 the Observer that subscribed in part 1 would be fired again, but it isn't, making my glorified Observer a constant. :(
Am I missing something?
Well I feel sheepish....
Figured out the issue was due to my import statement having the (wrong) file suffix on the file reference. So, in the myComponent file I had...
import { AppSettings } from './settings.js';
While everywhere else I have been using (the correct)
import { AppSettings } from './settings';
which was causing WebPack to compiling two versions of the class, the TypeScript and the (compiled) Javascript version, thus creating two different instances. I managed to see an AppSettings_1 somewhere, that lead me down the rabbit hole to finally gave it away.

Most efficient Aurelia binding way for a singleton object

Here's a pretty fundamental JavaScript and Aurelia question.
Let's say that I have a singleton object, for example User and it would often get updates from the server, which returns a whole new User object.
Now, to push the update to the views, I have two options (that I know of):
Update every property of the existing User to that of the new User's manually (this would also require mapping every property).
Replace the object reference and push an EventAggregator notification for all listeners to re-query the User object.
I have currently gone for option number 1, which has raised some issues, but nothing blocking.
Which one would be more efficient and/or provide more benefits over the other?
Here's my opinion. You don't have to use EventAggregator, and you also don't have to struggle updating every property. You could create helper class (AppState or something) to hold your User object. In your elements, inject the AppState class and create a getter function to return the User object (use #computedFrom or aurelia-computed to avoid dirty-checking). For example:
JS
import { AppState, MyClass } from './app-state';
import { computedFrom } from 'aurelia-framework';
export class MyElement {
static inject = [AppState];
constructor(appState) {
this.appState = appState;
}
#computedFrom('appState.obj')
get obj() {
return this.appState.obj;
}
updateObject() {
this.appState.obj = new MyClass();
}
}
HTML
<template>
<h1>Element 1</h1>
<button click.delegate="updateObject()">Update Object</button>
<br><br>
${obj.p1}
<br><br><br>
${obj.p2}
</template>
Running example https://gist.run/?id=f2ed9769343513b0819115863ff64c34

Preventing Circular Dependencies with Exported Singleton Class

I have a question regarding a scenario I keep running into building HTML5 games resulting in difficult to manage circular dependencies.
I understand completely why the circular dependency is occuring and where it is occurring. However, I can't seem to figure out a convenient way to get around it, so I assume my logic / approach is fundamentally flawed.
Here's a little bit of context.
I have a game that has a single point of entry (compiled with Webpack) called Game.js. I have a basic event manager that allows for two functions on(key, callback) and fire(key, parameters).
The event manager simply creates an object, sets the supplied key of on as a property with an array value populated with any callback functions registered to that key. When the fire method is called that property is retrieved and all of the fuctions defined in it's array value are invoked.
What I'm trying to do
I want to be able to instance the event manager on Game.js and export an instance of Game that other classes can import and subsequently register callbacks to the Game instances event manager.
class Game {
constructor() {
this.events = new EventManager();
window.addEventListener('resize', this.resize.bind(this));
}
resize(event) {
if(window.innerWidth < window.innerHeight) {
this.events.fire('orientation-change', 'vertical');
} else {
this.events.fire('orientation-change', 'horizontal');
}
}
}
export default new Game();
Then for example a Button class may need to respond to an orientation change event fired by the Game. Please note the above is simply an example of a circumstance in which the event manager may fire an event, but this condition could be anything.
import Game from '../core/Game';
class Button {
constructor() {
Game.events.on('orientation-change', this.reorient.bind(this));
}
reorient() {
// ...
}
}
export default Button;
The above class is a UI component called Button that needs to know when the orientation-change event is fired, again please note this event could be anything.
What's the problem?
Nothing looks particularly wrong with the above, however, because Game.js is the entry point, at some point an instance of Button is created whether it be directly in Game.js or through another class which is subsequently instanced via Game.js which of course causes a circular dependency because even if not directly, Game imports Button and Button imports Game.
What I've tried
There are two main solutions that I have found that work (to some degree). The first being simply waiting for the export to be available using an interval check of the value of Game in the constructor of Button, like this:
import Game from '../core/Game';
class Button {
constructor() {
let check = setInterval(() => {
if(Game !== undefined) {
Game.events.on('orientation-change', this.reorient.bind(this));
clearInterval(check);
}
}, 100);
}
reorient() {
// ...
}
}
export default Button;
This will typically resolve in a single iteration.
The second solution being to use dependency injection and pass reference of Game to Button when it's instanced, which again works great, but the prospect of having to repeatedly do this per class seems unintuitive. The interval check works fine too, but seems hacky.
I'm feel like I'm completely missing something and that the solution isn't a difficult as I'm making it.
Thanks for any help regarding this.

Categories

Resources