Parent/Child class hierachy in nodejs - node.js

child.js
class Child {
constructor(){
this.helloWorld = "Hello World";
}
run() {
}
}
export default new Child();
parent.js
import child from './child.js';
class Parent {
constructor() {
this.child = child;
}
}
export default new Parent();
index.js
import parent from './parent.js'
console.log(parent.child.helloWorld); <-- does not throws an error, displays "Hello World"
console.log(parent.child.run); <-- throws an error (Cannot read property run from undefined)
console.log(parent.child.run()); <-- throws an error (Cannot read property run from undefined)
If I do console.log(parent.child) in index.js, run does not show up, however the property helloWorld does..
How can I have the functions exposed as well? I was hoping to be able to do this to help keep my code a bit more organized, so was going to separate it out into separate classes to help minimize the amount of code in each file.

To make one thing clear from the start: The error you seem to get has nothing to do with run not appearing in the console.log output.
If your code really throws that error then that means that the value of parent.child is undefined. Hence when you call console.log(parent.child), you should see undefined, not an object. However, I don't see why you'd get that error.
Anyways, run is defined on the prototype of parent.child, not on itself. console.log most likely shows an object's own properties (the console API is not standardized, so results can vary between environments). That's normal.
Simple example to reproduce:
var foo = {
x: 42
};
var bar = Object.create(foo);
bar.y = 21;
console.log(bar, bar.x, bar.y);
// Open the browser console to see output
bar.x is accessible even though console.log doesn't show it (in Chrome at least).

Well I'm not sure if helps you to solve the problem, but whenever I want to add inheritance, I use extends and super here is an example:
Base Class:
class BaseDataModel {
constructor() {
}
getModel() {
return 'model';
}
module.exports.BaseDataModel = BaseDataModel;
Class extending Base Class:
"use strict"
// Imports
const BaseDataModel = require('../baseDataModel').BaseDataModel; // use the proper location
class UserMembershipModel extends BaseDataModel {
constructor() {
super(); // this is optional, I use this to inherit the constructors
}
getChildModel() {
return super.getModel(); // This is how you access the function from your extended class
}
module.exports.UserMembershipModel = UserMembershipModel;
Again, not sure if it solves your problem, since your actually adding a property with a Child class. My example is actually extending (or UserMembershipModel inherits from BaseDataModel).
Hope this helps you a bit.

Related

nodejs with tap: Class extends value undefined is not a constructor or null

I'm pretty new to node development and have the issue mentioned in the headline. I figured out, that this is probably a circular dependency issue. But I really don't get why.
Maybe you can help?
Here is the first part of my application that works.
I have a simple abstract class like this:
export abstract class AbstractApplication {
abstract beforeStart(): void
abstract afterStart(): void
}
Then I have a second class that extends from that AbstractApplication:
export class App extends AbstractApplication {
//implement the specific methods from AbstractApplication here and do things
async startApp(): void {
//do things here
}
}
Inside this same file, I create an instance of the App class and export it into the module like this:
const app: App = new App()
module.exports = app
Then, inside another file, the server.js, I require this app and start it like this:
const app = require("./src/App")
app.startApp()
And if I run npm run start which calls node server.js everything works and the App runs.
Now I have a seperate file that contains a third class that extends from the App class like this:
export class TestSuite extends App {
async startApp(): Promise<void> {
console.log("READY")
}
}
So as you can see, I take the app class but I override the startApp function.
Inside this same file I do the same thing I did in the App file before:
const testSuite = new TestSuite()
module.exports = testSuite
Then in a last file, I'm doing this:
const testSuite = require("./TestSuite")
testSuite.startApp()
If I now change my npm start to node theOtherFileWithTestSuiteStart.js and run npm run start, everything works also fine.
Now, the last file which contains the testSuite.startApp()should contain test functions that depend on that application, just something simple as
const testSuite = require("./TestSuite")
testSuite.startApp()
test('requests the "/" route', function () {
//test something here
})
And when I run this with tap, then I get the error above: TypeError: Class extends value undefined is not a constructor or null
The interesting thing is: If I extend the TestSuite class from AbstractApplication, I at least get an error that the test is unfinished (which may be okay because of dummy implementation). If I extend it from the App class, this error occures.
Any ideas?

NodeJS + TypeScript Error TS2339: Property 'timeOrigin' does not exist on type 'Performance'

I have a NodeJS/TypeScript app that uses Selenium WebDriver, which has approximately this snippet:
import { WebDriver } from "selenium-webdriver";
export class MyClass {
public async getTimeOrigin(driver: WebDriver): Promise<number> {
return await driver.executeScript(function () {
// This should be the DOM's timeOrigin (same as if a person types "performance.timeOrigin"
// into the Chrome DevTools console)
return performance.timeOrigin;
});
}
}
When I attempt to build the above, I get the following error message:
error TS2339: Property 'timeOrigin' does not exist on type 'Performance'
But it does certainly exist, since I can looks up the references on the Performance object and see timeOrigin inside. Can anyone please advise on how to fix this and correctly access the timeOrigin property?
performance is a global variable which is only available to the web browser you test. While you're working in NodeJS environment, it doesn't have any global performance, that's why Typescript cannot detect it and throw the error when compiling.
To fix this, you only need a simple global variable declaration:
declare var performance: any;
If you're using a lot of web interfaces in your Node environment, I recommend you import the lib lib.dom to typescript to avoid these types of errors:
{
"compilerOptions": {
"lib": [
"dom"
]
}
}
As mentioned by mcernak, another way is to use string as input to executeScript function.
To avoid the compilation issues, you can provide the script that you want to execute in context of the browser as a string(instead of a function):
public async getTimeOrigin(driver: WebDriver): Promise<number> {
return await driver.executeScript("return performance.timeOrigin;");
}
From the documentation of WebDriver.executeScript:
Executes a snippet of JavaScript in the context of the currently
selected frame or window. The script fragment will be executed as the
body of an anonymous function. If the script is provided as a function
object, that function will be converted to a string for injection into
the target window.
Maybe you can try to cast it as Performance.
import { WebDriver } from "selenium-webdriver";
export class MyClass {
public async getTimeOrigin(driver: WebDriver): Promise<number> {
return await driver.executeScript(function () {
// This should be the DOM's timeOrigin (same as if a person types "performance.timeOrigin"
// into the Chrome DevTools console)
const perf = <Performance>performance; // or "performance as Performance"
return perf.timeOrigin;
});
}
}

TS/Node.js: Getting the absolute path of the class instance rather than the class itself

Is there a way to get the path (__dirname) of the file where an instance of a class was made without passing that into the constructor?
For example,
// src/classes/A.ts
export class A {
private instanceDirname: string;
constructor() {
this.instanceDirname = ??
}
}
// src/index.ts
import { A } from "./classes/A"
const a = new A();
// a.instanceDirname === __dirname ✓
I tried callsite, it worked but I had to do some regex that I'm not happy with to get what I need, I also tried a module called caller-callsite, but that ended up returning the module path, not the path of the file where the instance was made.
Is there a workaround for this?
I would have callers pass in the location information. Sniffing this stuff seems like a code smell to me (pardon the pun). ;-)
But you can do it, by using regular expressions on the V8 call stack from an Error instance, but it still involves doing regular expressions (which you didn't like with callsite), though it's doing them on V8's own stacks, which aren't likely to change in a breaking way (and certainly won't except when you do upgrades of Node.js, so it's easy to test). See comments:
// A regular expression to look for lines in this file (A.ts / A.js)
const rexThisFile = /\bA\.[tj]s:/i;
// Your class
export class A {
constructor() {
// Get a stack trace, break into lines -- this is V8, we can rely on the format
const stackLines = (new Error().stack).split(/\r\n|\r|\n/);
// Find the first line that doesn't reference this file
const line = stackLines.find((line, index) => index > 0 && !rexThisFile.test(line));
if (line) {
// Found it, extract the directory from it
const instanceOfDirName = line.replace(/^\s*at\s*/, "")
.replace(/\w+\.[tj]s[:\d]+$/, "")
.replace(/^file:\/\//, "");
console.log(`instanceOfDirName = "${instanceOfDirName}"`);
}
}
}
Those three replaces can be combined:
const instanceOfDirName = line.replace(/(?:^\s*at\s*(?:file:\/\/)?)|(?:\w+\.[tj]s[:\d]+$)/g, "");
...but I left them separate for clarity; not going to make any performance difference to care about.

Phaser 3 ScaleManager exception

I am trying to enable fullscreen in my game written in Phaser 3.
I am doing it from Scene class via
this.game.scale.startFullScreen();
but getting error in f12 browser console
Uncaught TypeError: this.game.scale.startFullScreen is not a function
at TitleScene.<anonymous> (TitleScene.js:23)
at InputPlugin.emit (phaser.js:2025)
at InputPlugin.processDownEvents (phaser.js:167273)
...
In docs ScaleManager class has startFullScreen method.
Why console tells me it doesn't?
This is the full code of TitleScene.js:
export class TitleScene extends Phaser.Scene {
constructor ()
{
const config =
{
key: 'TitleScene'
}
super(config);
}
preload ()
{
this.load.image('Title', 'assets/Title.png');
}
create ()
{
this.background = this.add.image(960, 540, 'Title');
this.input.manager.enabled = true;
this.input.once('pointerdown', function () {
this.scene.start('MainScene');
this.game.scale.startFullScreen(); // here is the error
}, this);
}
}
There are two problems prevented me from resolving this problem:
I followed examples from here
https://www.phaser.io/examples/v2
But I am using the third version Phaser. And everyone who uses the same must follow examples from here
https://www.phaser.io/examples/v3
You must pay attention to url while using their site with examples. Both pages are the same from the first look. But urls are different. Also there are warning after each example using the second (old) version of engine.
And finally this function name is not startFullScreen but startFullscreen :)

Using EasyRTC in dart: Class 'Proxy' has no instance getter 'iterator'

As an experiment, I'm trying some stuff out with dart and easyrtc. I started at porting this (it is normally served through a nodejs server, found here) to a dart version and this is what I made from it
EDIT: I found out which part of the code is causing the error. It is the data object proxy which the for loop is unable to run through. Normally, the setRoomOccupantListener function gives as parameters the name of the room and an object with all the peers connected to the room. I have made a screenshot of the object layout in normal javascript as how it looks when I debug in chrome, found here.
function connect() {
easyrtc.setRoomOccupantListener(convertListToButtons);
}
function convertListToButtons (roomName, data, isPrimary) {
clearConnectList();
var otherClientDiv = document.getElementById("otherClients");
for(var easyrtcid in data) {
var button = document.createElement("button");
button.onclick = function(easyrtcid) {
return function() {
performCall(easyrtcid);
};
}(easyrtcid);
var label = document.createTextNode(easyrtc.idToName(easyrtcid));
button.appendChild(label);
otherClientDiv.appendChild(button);
}
}
And here is the screenshot when i debug the dart code in chromium
void connect() {
easyrtc.setRoomOccupantListener(convertListToButtons);
}
void convertListToButtons(roomName, data, isPrimary) {
clearConnectList();
var otherClientDiv = querySelector("#otherClients");
for (var easyrtcid in data) {
var button = document.createElement("button");
button.onClick.listen((event) {
performCall(easyrtcid);
});
button.appendText(easyrtc.idToName(easyrtcid));
otherClientDiv.append(button);
}
}
This is the error I get:
Class 'Proxy' has no instance getter 'iterator'.
NoSuchMethodError: method not found: 'iterator' Receiver: Instance of 'Proxy' Arguments: []
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:45)
#1 P...<omitted>...7)
Am I missing something simple here or is this some kind of incompatibility? Thank you.
I see you can use import package:js/js.dart'; too. I don't know how to use it
You could try
import 'dart:js' as js;
https://www.dartlang.org/articles/js-dart-interop/
This looks weird too
easyrtc = js.context.easyrtc; // <== here you have context 'easyrtc'
easyrtc.easyApp('easyrtc.audioVideo', 'selfVideo', new js.JsObject.jsify(['callerVideo']), loginSuccess, loginFailure);
// and here again 'easyrtc.audioVideo', I guess this is one to much
try
easyrtc.easyApp.callMethod('audioVideo', ['selfVideo', js.JsObject.jsify(['callerVideo']), loginSuccess, loginFailure]);
where 'audioVideo' is the called method and the rest are arguments
easyrtc.callMethod('easyApp', ['audioVideo', 'selfVideo', js.JsObject.jsify(['callerVideo']), loginSuccess, loginFailure]);
where 'easyApp' is the called method and the rest are arguments.
If you can add how the code would look in JavaScript I could create better examples.
Like dart:js package:js doesn't handle directly Dart List. So the following line :
easyrtc.easyApp('easyrtc.audioVideo', 'selfVideo',
['callerVideo'], loginSuccess, loginFailure);
should be :
easyrtc.easyApp('easyrtc.audioVideo', 'selfVideo',
js.array(['callerVideo']), loginSuccess, loginFailure);
See also What is a difference between dart:js and js package?

Resources