Imported Classes relationship from one to other - javascript

O.K..so I kind of figure it out. but I want to make sure its not possible.
lets say I have 3 files:
index.js main file,player.js player class file,game.js game class file.
the main file (index) is importing all the classes. but, I need to have the option to create Game object in both files
example:
index.js:
var game = require("./game.js");
var player = require("./player.js");
new player("player");
new game("game");
player.js:
module.exports = class Player {
constructor(arg){
console.log(arg);
var asd=new game("ASD");/// <- the problem
}
};
game.js:
module.exports = class Game {
constructor(arg){
console.log(arg);
}
};
how can i call to Game in player class without require game both in the index.js and the player.js
I know this will solve it but is there another way without a duplicate require?
(2 require, one in index.js and one in player.js)

index.js:
var player = require("./player.js");
var game = player.Game;
new player("player");
new game("game");
player.js:
var game = require("./game.js");
module.exports = class Player {
constructor(arg){
console.log(arg);
var asd=new game("ASD");/// <- the problem
}
};
modules.exports.Game = Game;
game.js:
module.exports = class Game {
constructor(arg){
console.log(arg);
}
};

Related

How can multiple Imported classes call each others methods?

I have this index.js file:
import GameController from './Controllers/GameController'
import LandController from './Controllers/LandController'
const GAME = new GameController();
const LAND = new LandController();
And in this class, I want to call a function every second using setInterval:
export default class GameController {
tickCount = 0;
tick = setInterval( () => {
this.Tick()
}, 1000);
Tick () {
LAND.updateLand();
this.tickCount++;
}
}
But I get this error:
Uncaught ReferenceError: GAME is not defined
I'm new to webpack, I had this working in vanilla javascript but I'm still learning, so any help would be greatly appreciated! Thank you
EDIT:
Updated GameController class based on bravo's answer, however now I get the error:
Uncaught ReferenceError: LAND is not defined
How can I make it so multiple classes can call each others methods?
You started using modules and classes, so you are moving to OOP (Object Oriented Programming). This means that you should forget about scripting, where you declare globals, everything manipulates everything, and start reasoning in a structured, hierarchical, and isolated fashion (where you have objects responsible for specific tasks).
You started properly by understanding what isolation is. You created an autonomous class to control the land. Then you need a game controller, which controls the land (and anything else, but we will come back to this lather).
Follow the reasoning; Game controls Land, in OOP this would translate in either Game receives Land or Game creates Land. Which of two to choose depends on the specific use case.
Game creates Land
// ./Controllers/GameController.js
import LandController from './Controllers/LandController'
export default class GameController {
tickCount = 0;
land = new LandController();
intercal = setInterval(this.onTick.bind(this), 1000);
onTick () {
this.land.updateLand();
this.tickCount++;
}
}
// index.js
import GameController from './Controllers/GameController'
const GAME = new GameController();
Here you can see how Game creates Land, this is useful but reduces your access to the Land constructor (assuming you do not want to change Game to configure land)
Game receives Land
// ./Controllers/GameController.js
export default class GameController {
tickCount = 0;
land;
constructor(land) {
this.land = land;
}
intercal = setInterval(this.onTick.bind(this), 1000);
onTick () {
this.land.updateLand();
this.tickCount++;
}
}
// index.js
import GameController from './Controllers/GameController'
import LandController from './Controllers/LandController'
const LAND = new LandController();
const GAME = new GameController(LAND);
Here you have access to both constructors and your code is more maintainable. Game is not concerned about Land (what is, what it has in or where it came from) as long as it has a method updateLand() that has to be called at an interval.
More than land
The second solution allows you to implement a more versatile Game. Assume your Land has a method called update() rather than updateLand().
// ./Controllers/GameController.js
export default class GameController {
tickCount = 0;
children;
constructor(children) {
this.children = children;
}
intercal = setInterval(this.onTick.bind(this), 1000);
onTick () {
this.children.forEach(c => c.update());
this.tickCount++;
}
}
And then you can go beyond Land, maybe have a Character or more Land, or anything else.
// index.js
import GameController from './Controllers/GameController'
import CharController from './Controllers/CharController'
import LandController from './Controllers/LandController'
const LAND1 = new LandController();
const LAND2 = new LandController();
const CHAR = new CharController();
const GAME = new GameController([ LAND1, LAND2, CHAR ]);
This is how you can start reasoning to structure your OOP software properly; objects that are concerned about tasks and other objects.
When an object is not concerned about another object it should not have access to it (so no globals)
When an object is responsible for another object it should have direct access (receive them from the constructor or a setter) or directly instantiate them (create them).
If you really want to make the instances global, which I would not recommend because there are normally are better methods then using global variables, you should setup the variables in the window object
window.yourNamespace = {};
window.yourNamespace.game = new GameController();
window.yourNamespace.land = new LandController();
Then you can access both objects everywhere you like with
window.yourNamespace.game
window.yourNamespace.land
Answering your question about a non global solution:
I can only guess what your application will do but it sounds like a game. So GameController should be the main class. From this class you should instantiate all other classes like the LandController(). In that case you can access the LandController of course from the GameController. If you need an object in a class you should give the object as an parameter. E.g.
Class GameController()
{
landController = new LandController();
… here all needed methods for the class …
// Example if you need LandController in an other Class
exampleMethod()
{
Let otherClass = new OtherClass(this.landController);
}
}
You can do it this way:
We may use a constructor for that.
export default class GameController {
tickCount = 0;
tick = null;
constructor() {
this.tick = setInterval(() => {
this.Tick()
}, 1000);
}
Tick () {
LAND.updateLand();
this.tickCount++;
}
}

Passing a variable to a (globally?) exported class

I apologize if this question doesn't really make sense - I'm probably lacking a bit of an understanding on how exactly module.export works and scoping between classes, but I will try my best to explain. I currently have a discord bot with a music player that has a MusicPlayer class - this class is exported in this manner:
const ytdl = require('ytdl-core-discord');
const MusicQueue = require('./musicqueue');
const discordUtils = require ('../../../utils/discord-utils');
class MusicPlayer {
constructor() {
this.repeat_playlist = false;
this.queue = new MusicQueue(); // this is the persistent queue for the server
this.dispatcher = null;
this.volume = 1;
this.stopped = true;
this.paused = false;
this.repeat_current_song = false;
this.auto_delete = false;
this.manual_index = false;
this.autodcEnabled = false;
}
....
}
module.exports = new MusicPlayer();
I have various music-related commands that import this music player like so:
const { Command } = require('discord.js-commando');
const discordUtils = require ('../../utils/discord-utils');
let musicplayer = require(`./modules/musicplayer`);
module.exports = class TestCommand extends Command {
constructor(client) {
....
}
...
}
My question is, how would I pass a local variable to the musicplayer constructor since I'm exporting it via module.exports = new MusicPlayer();?
It's my understanding that using module.exports = new MusicPlayer(); would allow me to use the same musicplayer object throughout the entire project (which is what I want); but - and this is where I'm not sure I understand how module.exports works - there's no guarantee when (if at all) the musicplayer will be instantiated.
If you never use a music-related command the musicplayer will never be created because let musicplayer = require('./modules/musicplayer'); will never be run, right?
Either way, now I need to pass something to the musicplayer constructor from within one of the commands, how can I do so in a way that I don't have to repeatedly do something like: let musicplayer = require('./modules/musicplayer')(variable); in every file that requires the music player? Is there a way for me to declare let musicplayer = require('./modules/musicplayer')(variable); one time and have that musicplayer be used throughout the entire codebase?
Let's list out a few requirements:
You want to share a single music player among a bunch of code.
You want the music player to be created upon demand, only when someone actually needs it
You want to initialize the music player with some variable you have in your code
To make this work, you have to centralize the creation of the music player and that has to be coordinated with whoever has the initialization variable that you want to pass to the constructor. There are many ways to do that. I will illustrate two of them.
Let's assume that it's your application initialization code that has this variable that you want to use when creating the music player. So, rather than having everyone load the music player module themselves, we will have them all ask the app for the music player.
// app.js
const MusicPlayer = require(`./modules/musicplayer`); // load module to get constructor
let musicPlayerInitVariable; // your code sets this during startup
const sharedPlayer;
function getMusicPlayer() {
if (!sharedPlayer) {
sharedPlayer = new MusicPlayer(musicPlayerInitVariable);
}
return sharedPlayer;
}
// one of many possible exports from your app
module.exports = { getMusicPlayer };
Then, in any of your application files that want access to the shared music player, then just do this:
const musicPlayer = require('./app.js').getMusicPlayer();
This requires that anyone using the shared musicPlayer knows how to reference app.js.
Another way to do it is to have your app, initialize the music player module itself with some initial state that it can then use when it creates the music player upon demand.
// musicplayer.js
const ytdl = require('ytdl-core-discord');
const MusicQueue = require('./musicqueue');
const discordUtils = require ('../../../utils/discord-utils');
class MusicPlayer {
constructor() {
this.repeat_playlist = false;
this.queue = new MusicQueue(); // this is the persistent queue for the server
this.dispatcher = null;
this.volume = 1;
this.stopped = true;
this.paused = false;
this.repeat_current_song = false;
this.auto_delete = false;
this.manual_index = false;
this.autodcEnabled = false;
}
....
}
// this is the constructor variable
// You can either give it a default value so things will work if the player
// is not configured or if you don't give it a default value, then the code
// will require that configurePlayer() is called first
let musicPlayerInitVariable;
// call this before anyone calls getMusicPlayer()
function configurePlayer(data) {
// save this for later
musicPlayerInitVariable = data;
}
let sharedPlayer;
function getMusicPlayer() {
if (!sharedPlayer) {
if (!musicPlayerInitVariable) {
throw new Error('Music player must be configured with configurePlayer() first')
}
sharedPlayer = new MusicPlayer(musicPlayerInitVariable);
}
return sharedPlayer;
}
module.exports = { getMusicPlayer, configurePlayer };
Then, in your app-startup code somewhere, you do this:
require('./modules/musicplayer').configurePlayer(passMagicVariableHere);
From then on, anyone can just do:
const musicPlayer = require('./modules/musicplayer').getMusicPlayer();

Protractor element(..) returning undefined from a separate file

I'm writing a Protractor test and in my test.step.js file I have
element(by.css('...')).getText().then(function (text) {
expect(text).to.equal('expectedText');
});
This works as expected and passes.
Instead I created a test.page.js file and in there put this.field = element(by.css('...')); and then in my step file had
"use strict"
module.exports = function exampleTest() {
var TestPage = require("...");
var testPage = new TestPage;
...
test.Then(..., function (next) {
testPage.field.getText().then(function (text) {
expect(text).to.equal('expectedText');
});
});
}
then field is undefined. I have also tried adding getText() in the page file, but again get undefined or get told that I can't call 'then' on undefined.
In my mind, this should do exactly the same thing as the first example, but I'm far from an expert with Angular or JavaScript.
test.page.js looks like:
"use strict";
module.exports = (function () {
function TestPage() {
this.field = element(by.css('...'));
}
return TestPage;
});
Hoping someone can shine some light on why this is happening and what I should do instead to be able to put the CSS selector inside a page file for re-use.
Thanks
Your code new TestPage; returns the constructor TestPage, but it's never called.
You could return the class :
function TestPage() {
this.field = element(by.css('...'));
}
module.exports = TestPage;
var TestPage = require("...");
var testPage = new TestPage;
testPage.field.getText().then(...
Or an instance of the class:
function TestPage() {
this.field = element(by.css('...'));
}
module.exports = new TestPage();
var testPage = require("...");
testPage.field.getText().then(...
The way you defined re-usable element locators looks different. I am following some thing like below
Step 1: Define a .js file which should contain the Locator objects and re-usable methods
var Login = {
PageElements: {
emailInput: element(by.css('#email')),
passwordInput: element(by.css('#password')),
loginForm: element(by.css('#form')),
},
doLogin: function doLogin() {
this.PageElements.emailInput.sendKeys('blahblah#email.com');
this.PageElements.passwordInput.sendKeys('blahblah');
this.PageElements.loginForm.submit();
},
};
module.exports = Login;
Step 2: Call these page objects in your test classes.
var LoginPage = require('../pageobjects/LoginPage.js');
it('Scenario1_Login',function(){
LoginPage.PageElements.emailInput.sendKeys('blahblah');
});
More details here

Node.js Module - attach required objects methods to main App object

Sorry if the title is a little vague, I don't really know what to call this concept.
Basically I have a main.js file that contains the following code:
var util = require('./folder/util');
var summoner = require('./folder/player');
var App = {
timeout: 500,
apiKey: 'asdnanfasofafasqrqrsa',
region: 'can'
}
App.init = function(apiKey, region) {
this.apiKey = apiKey;
this.region = region;
}
App.getPlayerByName = player.getPlayerByName;
module.exports = App;
I also have a player.js file that contains those methods (player.getPlayerByName). My goal is to create an NPM module with that main.js entry file and access it like so:
var main = require('main.js');
main.getPlayerByName('Jake');
Now the only way I've found to 'attach' those methods to that main App object so I can access it as above, is by adding the following line of code to main.js:
App.getPlayerByName = player.getPlayerByName;
It works great as is, but after I add a lot more methods to the player.js file I will have a lot of extra bloat in the main.js file that I wouldn't mind avoiding. Is there a better way of going about this?
Thank you in advance!
You could iterate the player object and copy all methods over to your App object:
var App = {
timeout: 500,
apiKey: 'asdnanfasofafasqrqrsa',
region: 'can'
}
App.init = function(apiKey, region) {
this.apiKey = apiKey;
this.region = region;
}
// copy all methods from player to App
for (var method in player) {
if (player.hasOwnProperty(method) && typeof player[method] === "function") {
App[method] = player[method];
}
}
This will work as long as player is just a namespace object, where the methods don't use the actual object instance.
Or, you could just use the existing player object and assign your own properties to that existing object:
Object.assign(player, {
timeout: 500,
apiKey: 'asdnanfasofafasqrqrsa',
region: 'can',
init: function(apiKey, region) {
this.apiKey = apiKey;
this.region = region;
}
});
module.exports = player;
In both these cases, you have to make sure that there are no method/property name conflicts between the two uses of the object.

NodeJS required module in module is always undefined

I am pretty much new to backend-code. For now I try learning the architecture of a nodeJS-server.
My Problem for now is pretty simple I believe, but I can't run through -.-
server.js (main):
var io = require("socket.io"),
express = require("express"),
expressHbs = require('express3-handlebars'),
Player = require("./models/Player").Player,
Room = require("./models/Room").Room,
Game = require("./models/Game").Game,
dataSet = require('./data/data.json');
//...
Then I have 3 modules:
Player
Room
Game
Each module is the same structure:
Player.js
// importing another module
var Room = require("./Room").Room;
if (!Player.players) {
Player.players = {};
}
function Player(foo) {
this.foo = foo;
Player.players[foo] = this;
}
Player.getAllPlayersAsObject = function () {
return Player.players;
};
// ...
Player.prototype.removePlayer = function () {
if (Player.players.hasOwnProperty(this.id)) {
delete Player.players[this.id];
}
};
// ...
/**
* node export
* #type {Player}
*/
exports.Player = Player;
Room.js
var Player = require("./Player").Player;
if (!Room.rooms) {
Room.rooms = {};
}
function Room(foo) {
this.foo = foo
}
Room.getAllRoomsAsObject = function () {
return Room.rooms;
};
Room.prototype.toString = function () {
return JSON.stringify(this);
};
/**
* node export
* #type {Room}
*/
exports.Room = Room;
My main problem is, that I can use Room, Player, Game from server.js - constructor, prototype and all other functions normally.
In Player.js I can use the imported Room-module normally, too!
Room.js tells me:
path/path/path/gameserver/models/Room.js:222
var currentPlayer = Player.getPlayer(this.players[player].id);
^
TypeError: Cannot read property 'getPlayer' of undefined
I am going crazy. What am I doing wrong? When I do a console.log (debug) of that variable "Player" at the top of the file, I am always getting undefined.
I walked around on google and here at stackoverflow, not finding any solution for my problem.
Thx and best regards,
Michael
You have a cyclic dependency between your modules.
You start by requiring Player. The first thing it does is require Room. Room then requires Player. At this point, Player has not yet completed its loading. Node prevents an infinite loop occurring by returning an incomplete version of Player. At this point, all bets are off as to what will happen.
You need to structure your modules to avoid this direct loop.
Background information of cyclic dependencies here: https://nodejs.org/api/modules.html#modules_cycles

Categories