Nodejs reference to class object from other file - javascript

I'm learning nodejs now and Im wondering how can I reference to the object created in a other file
for example:
I have a file with my class user.js which I'm exporting
module.exports = class Username {
constructor(name, lastProjects) {
this.current.name = name;
this.current.lastProjects = lastProjects;
}
};
name.handler.js I can not export it this to oder files
const Alexa = require('alexa-sdk');
const User = require('../models/user.model');
module.exports = Alexa.CreateStateHandler(StatesConst.NAME, {
'NewSession': function () {
this.emit('NewSession'); // Uses the handler in newSessionHandlers
},
'MyNameIsIntent': function() {
var user = new User.Username("Anna", ["project1", "project2"]);
this.emit(':ask', "Hi "+User.Username.name);
}
}
user.handler.js I tottaly dont know how can I write a reference to my new created object Username
const Alexa = require('alexa-sdk');
const User = require('../models/user.model');
module.exports = Alexa.CreateStateHandler(StatesConst.NEWSTATE, {
'NewSession': function () {
this.emit('NewSession'); // Uses the handler in newSessionHandlers
},
'MyUserIntent': function() {
this.emit(':ask', "My username is "+User.Username.name);
}
}
How can I reference to new user object in other files in my programm ? I want that everytime my user starts a program I will get a new user object and I could reference and change attributes in every other file. I would be very appreciate for a help :)

module.exports = Username;
const Alexa = require('alexa-sdk'); // ok?
const User = // in linux, to go 1 dir up is just '.'
console.log(require('../models/user.model'))
see if you get a message = '[Function: Username]'".
If not you will see a message telling you something. Maybe no found file.

Since you are exporting Username by assigning it to module.exports, you need to use it as new User in your handlers. For example, name.handler.js would become:
const Alexa = require('alexa-sdk');
const User = require('../models/user.model');
module.exports = Alexa.CreateStateHandler(StatesConst.NAME, {
'NewSession': function () {
this.emit('NewSession'); // Uses the handler in newSessionHandlers
},
'MyNameIsIntent': function() {
var user = new User("Anna", ["project1", "project2"]);
this.emit(':ask', "Hi "+user.name);
}
}
Note that in the this.emit line, you should use the instance of User, not the class itself. (so user instead of User).
Moreover, in your user.handler.js file, you don't instantiate any User. You will get a Cannot read property 'name' of undefined
Finally if your Username class is in user.js, your require statement should look like require('../models/user'), not require('../models/user-model')

Related

Passing socket.io instance to JS object constructor

I read this question and attempted to do the solution, however I am trying to pass my instance of io to an object constructor instead of a class. I originally attempted to do something like...
//index.js
const {CONNECTION, CREATE_ROOM} = require('./SignalTypes')
const app = require('express')()
const server = require('http').Server(app)
const io = require('socket.io')(server)
const Lobbies = require('./lobby')
let lobbies = new Lobbies(io)
io.of('/menu').on(CONNECTION, (socket) => {
console.log(`User connected to main menu`)
socket.on(CREATE_ROOM, () => {
const roomKey = lobbies.createLobby()
socket.emit(ROOM_CREATED, roomKey)
})
...
})
And my Lobbies file looks like...
//lobby.js
const shortid = require('shortid')
function Lobbies(io) {
this.io = io;
this.lobbies = {}
}
Lobbies.prototype.createLobby = () => {
let roomKey = shortid.generate()
//create namespace for new lobby
const lobbyNamespace = this.io.of(`/${roomKey}`) // issue
this.lobbies[roomKey] = new Lobby(roomKey, lobbyNamespace)
return roomKey
}
//Lobby object constructor defined later
...
module.exports = Lobbies
However I keep running into errors in which it says io is undefined at the line
//lobby.js
const lobbyNamespace = this.io.of(`/${roomKey}`)
//TypeError: Cannot read property 'of' of undefined
I was wondering if there's a way to pass my io object to my object constructor without having to change it into an ES6 class or something. Any suggestions?
You are losing this reference when using arrow function syntax. I don't know why do you want to use old, hard-to-read syntax, but if you want that instead of class you should do:
Lobbies.prototype.createLobby = function() {
let roomKey = shortid.generate()
//create namespace for new lobby
const lobbyNamespace = this.io.of(`/${roomKey}`) // issue
this.lobbies[roomKey] = new Lobby(roomKey, lobbyNamespace)
return roomKey
}

How to have collections populated in a module for use inside events/commands

I know that to have a collection populated such as guilds and channels, the bot must have logged in already, i.e. it can be used inside command files as well as inside events. What I have is a module that will display my logs inside my control discord server, and I want to be able to reference this module inside my events as well as my commands.
I have tried importing the module inside of the events, as well as other options that would make sense.
This is the code inside my module
const Discord = require('discord.js')
const bot = new Discord.Client()
const CC = '../settings/control-center.json'
const CCFile = require(CC)
const GUILD = bot.guilds.get(CCFile.GUILD)
const STARTUP = bot.channels.get(CCFile.STARTUP)
const INFO = bot.channels.get(CCFile.INFO)
const ERRORS = bot.channels.get(CCFile.ERRORS)
const RESTART = bot.channels.get(CCFile.RESTART)
const EXECUTABLES = bot.channels.get(CCFile.EXECUTABLES)
class Control {
/**
* Implement control center logging
* #param {string} message - What to send to the startup channel
* #return {string} The final product being sent to the startup channel
*/
STARTUP(message) {
return STARTUP.send(`${message}`)
}
}
module.exports = Control
I want to be able to globally use this module/the functions inside, so that my code can be more compact. So how can I have it so that this code is only loaded once the bot is logged in?
In your module code, you are creating a new Discord client instance, and never calling the login method.
A better approach would be to pass the bot object in your method
module file
const CC = '../settings/control-center.json';
const CCFile = require(CC);
const GUILD = CCFile.GUILD;
const STARTUP = CCFile.STARTUP;
const INFO = CCFile.INFO;
const ERRORS = CCFile.ERRORS;
const RESTART = CCFile.RESTART;
const EXECUTABLES = CCFile.EXECUTABLES;
class Control {
startup(bot, message) {
return bot.channels.get(STARTUP).send(message);
}
}
module.exports = Control
app file
// Use the bot here
const Discord = require('discord.js')
const bot = new Discord.Client()
const control = require('path/to/control.js');
[...]
// to send a message when ready, try something like this
bot.on('ready', () => {
control.startup(bot, 'bot is ready');
});
// don't forget to login
bot.login('YOUR-TOKEN-HERE');

calling a function object to load your js

function FriendlyChat() {
// statements
}
FriendlyChat.protoype.somemethod = function() {
// statements
};
FriendlyChat.protoype.somemethod2 = function() {
//statements
};
window.onload = function() {
window.friendlyChat = new FriendlyChat();
};
So i noticed the above structure for js while working on a google codelab.
And I have two ques.
in normal objects you have to call the function i.e Object.somemethod()
How does this structure call the methods assigned to it.
From my limited understanding, Firendlychat.protoype.the method treats the
function as an object and the methods are passed to the new object created on
window.onload.Via
inheritance, The object created i.e friendlychat has all these methods.
Yet none of the methods are called in any way. How does this work?
Is there any advantage to structuring your code in this way other than
readability
Note :
Main function
function FriendlyChat() {
this.checkSetup();
// Shortcuts to DOM Elements.
this.messageList = document.getElementById('messages');
this.messageForm = document.getElementById('message-form');
// Saves message on form submit.
this.messageForm.addEventListener('submit', this.saveMessage.bind(this));
this.signOutButton.addEventListener('click', this.signOut.bind(this));
this.signInButton.addEventListener('click', this.signIn.bind(this));
// Toggle for the button.
var buttonTogglingHandler = this.toggleButton.bind(this);
this.messageInput.addEventListener('keyup', buttonTogglingHandler);
this.messageInput.addEventListener('change', buttonTogglingHandler);
// Events for image upload.
this.submitImageButton.addEventListener('click', function(e) {
e.preventDefault();
this.mediaCapture.click();
}.bind(this));
this.mediaCapture.addEventListener('change',
this.saveImageMessage.bind(this));
this.initFirebase();
}
//the methods are setup here
// Sets up shortcuts to Firebase features and initiate firebase auth.
FriendlyChat.prototype.initFirebase = function() {
this.auth = firebase.auth();
this.database = firebase.database();
this.storage = firebase.storage();
// Initiates Firebase auth and listen to auth state changes.
this.auth.onAuthStateChanged(this.onAuthStateChanged.bind(this));
};
// Saves a new message on the Firebase DB.
FriendlyChat.prototype.saveMessage = function(e) {
e.preventDefault();
}
};
FriendlyChat.prototype.setImageUrl = function(imageUri, imgElement) {
imgElement.src = imageUri;
};
// Saves a new message containing an image URI in Firebase.
// This first saves the image in Firebase storage.
FriendlyChat.prototype.saveImageMessage = function(event) {
event.preventDefault();
var file = event.target.files[0];
// Clear the selection in the file picker input.
this.imageForm.reset();
// Check if the file is an image.
if (!file.type.match('image.*')) {
var data = {
message: 'You can only share images',
timeout: 2000
};
this.signInSnackbar.MaterialSnackbar.showSnackbar(data);
return;
}
// Check if the user is signed-in
if (this.checkSignedInWithMessage()) {
// TODO(DEVELOPER): Upload image to Firebase storage and add message.
}
};
// Signs-in Friendly Chat.
FriendlyChat.prototype.signIn = function() {
var provider = new firebase.auth.GoogleAuthProvider();
this.auth.signInWithRedirect(provider);
};
// Signs-out of Friendly Chat.
FriendlyChat.prototype.signOut = function() {
this.auth.signOut();
};
One of the advantages I've seen when using prototype inheritance was that you can control all instances of an object. For ex:
function FriendlyChat() {
this.chatIsActive = true;
}
FriendlyChat.prototype.deactivateChat = function(...rooms) {
for (chatRoom of rooms) {
chatRoom.chatIsActive = false;
}
};
var chat1 = new FriendlyChat();
var chat2 = new FriendlyChat();
var chatController = new FriendlyChat();
chatController.deactivateChat(chat1, chat2)
console.log(chat1.chatIsActive)
In ES6, however, you can do it:
class FriendlyChat {
constructor() {
this.chatIsActive = true;
}
static deactivateChat(...rooms) {
for (let chatRoom of rooms) {
chatRoom.chatIsActive = false;
}
}
}
var chat1 = new FriendlyChat();
var chat2 = new FriendlyChat();
FriendlyChat.deactivateChat(chat1, chat2)
console.log(chat1.chatIsActive)
And the another advantage of using prototype is that you can save memory spaces when you make an object from new keyword. For instance, the code in ES5 above, you can see chat1 and chat2 I've made by using new. Then chat1 and chat2 will be able to access deactivateChat() method which is in a sharing-space. It's because of the concept, called prototype-chaining.
And the next ES6 version is just a syntactic sugar - under the hood it does the same as ES5 version
I post this as a reference to others who have been faced with this dilemma.
First of all, ONe of the core issues for me was migrating from java, I seemed to be familiar territory but things work a bit different in js.I strongly recommend these links:
Objects in Detail
js Prototype
So the key to why this method works is due to the
window.friendlyapp =new friendlychat()
Now normally in most languages you have an object
obj() {
attr : value
method: function() {}
}
And then to use the method you do
var child = new obj();
child.method();
but in this method the var is made an instance of the window object and thats why none of the methods of the app need to be explicitly called.

How to include one .js file into another in Node.js? [duplicate]

I'm writing a simple server for Node.js and I'm using my own class called User which looks like:
function User(socket) {
this.socket = socket;
this.nickname = null;
/* ... just the typical source code like functions, variables and bugs ... */
this.write = function(object) {
this.socket.write(JSON.stringify(object));
}
};
and then later in the process I'm instantiating it a lot:
var server = net.createServer(function (socket) {
/* other bugs */
var user = new User(socket);
/* more bugs and bad practise */
});
Can I move my User class definition to another javascript file and "include" it somehow?
You can simply do this:
user.js
class User {
//...
}
module.exports = User // 👈 Export class
server.js
const User = require('./user.js')
let user = new User()
This is called CommonJS module.
ES Modules
Since Node.js version 14 it's possible to use ES Modules with CommonJS. Read more about it in the ESM documentation.
user.mjs (👈 extension is important)
export default class User {}
server.mjs
import User from './user.mjs'
let user = new User()
Using ES6, you can have user.js:
export default class User {
constructor() {
...
}
}
And then use it in server.js
const User = require('./user.js').default;
const user = new User();
Modify your class definition to read like this:
exports.User = function (socket) {
...
};
Then rename the file to user.js. Assuming it's in the root directory of your main script, you can include it like this:
var user = require('./user');
var someUser = new user.User();
That's the quick and dirty version. Read about CommonJS Modules if you'd like to learn more.
Another way in addition to the ones provided here for ES6
module.exports = class TEST{
constructor(size) {
this.map = new MAp();
this.size = size;
}
get(key) {
return this.map.get(key);
}
length() {
return this.map.size;
}
}
and include the same as
var TEST= require('./TEST');
var test = new TEST(1);
If you append this to user.js:
exports.User = User;
then in server.js you can do:
var userFile = require('./user.js');
var User = userFile.User;
http://nodejs.org/docs/v0.4.10/api/globals.html#require
Another way is:
global.User = User;
then this would be enough in server.js:
require('./user.js');

Mongoose possible issue

I have an issue using mongoose.
The application I am writing consists in a file watcher that notifies clients about certain events via email and socketio messages.
I made an example that shows the problem:
basically, there is a class called mainFileWatcher that contains a submodule which in turn watches for new files or folders created in the script directory, emitting an "event" event when that happens. The mainFileWatcher listens for that event and calls a static method of a mongoose Client model.
If you run the script setting REPL=true you'll be able to access a watcher.submodule object and manually emit an "event" event.
Now, if you manually trigger the event, you'll see a statement that
says that the "event" event was triggered and an email address as a response.
Otherwhise, if you create a file or a folder in the script folder, you'll
just see that statement. Actually, if you run the script with REPL=true
you'll get the email only after pressing any key, nothing otherwise.
The fact that you don't get the email address as a response means to me that
the code in the promise in mongoose model doesn't get called for some reason.
Here is the code, sorry I couldn't make it shorter
// run
// npm install mongoose bluebird inotify underscore
//
// If you run the script with REPL=true you get an interactive version
// that has as context the filewatcher that emit events in my original code.
// It can be acessed through the watcher.submodule object.
// The watcher triggers a "event" event when you create a file or a folder in the script
// directory.
//
// If you emit manually an "event" event with the watcher.submodule in the repl, you should see a
// statement that "event" was triggered and
// an email address (that belongs to a fake client created by the bootstrap
// function at startup).
// If instead you create a file or a folder in the script folder (or whatever folder you have setted),
// you should see this time you'll have no email response. Actually, if you run with REPL=true,
// you'll have no email response untill you press any key. If you run without REPL, you'll
// have no email response.
'use strict';
var mongoose = require('mongoose');
//mongoose.set('debug', true);
mongoose.Promise = require('bluebird');
var Schema = mongoose.Schema;
var EventEmitter = require('events');
var util = require('util');
var _ = require("underscore");
var Inotify = require('inotify').Inotify;
var inotify = new Inotify();
// Schema declaration
var clientSchema = new Schema({
emails: {
type: [String]
}
}, {strict:false});
clientSchema.statics.findMailSubscriptions = function (subscription, cb) {
this.find({
subscriptions: {$in: [subscription]},
mailNotifications: true
}).exec().then(function (clients) {
if(!clients || clients.length === 0) return cb(null, null);
var emails = [];
clients.forEach(function (client) {
Array.prototype.push.apply(emails, client.emails)
});
return cb(null, emails);
}).catch(function(err){
console.error(err);
})
};
var clientModel = mongoose.model('Client', clientSchema);
// Mongoose connection
mongoose.connect('mongodb://localhost:27017/test', function (err, db) {
if (err) console.error('Mongoose connect error: ', err);
});
mongoose.connection.on('connected', function () {
console.log('Mongoose connected');
});
// bootstrap function: it inserts a fake client in the database
function bootstrap() {
clientModel.findOne({c_id: "testClient"}).then(function (c) {
if(!c) {
var new_c = new clientModel({
"name": "Notifier",
"c_id": "testClient",
"subscriptions": ["orders"],
"registeredTo": "Daniele",
"emails": ["email#example.com"],
"mailNotifications": true
});
new_c.save(function(err){
if (err) console.error('Bootstrap Client Error while saving: ', err.message );
});
}
});
}
// submodule of the main file watcher: it looks for new files created in the script dir
var submoduleOfFileWatcher = function() {
var _this = this;
var handler = function (event) {
var mask = event.mask;
var type = mask & Inotify.IN_ISDIR ? 'directory' : 'file';
if (mask & Inotify.IN_CREATE) {
_this.emit("event", event);
}
}
var watcher = {
path: '.', // here you can change the path to watch if you want
watch_for: Inotify.IN_CREATE,
callback: handler
}
EventEmitter.call(this);
this.in_id = inotify.addWatch(watcher);
}
util.inherits(submoduleOfFileWatcher, EventEmitter);
// Main File Watcher (it contains all the submodules and listensto the events emitted by them)
var mainFileWatcher = function () {
this.start = function() {
bootstrap();
_.bindAll(this, "onEvent");
this.submodule = new submoduleOfFileWatcher();
this.submodule.on("event", this.onEvent)
};
this.onEvent = function() {
console.log("event triggered");
clientModel.findMailSubscriptions("orders", function(err, mails) {
if (err) console.error(err);
console.log(mails); // THIS IS THE CODE THAT OUTPUTS ONLY IF YOU TRIGGER THE "event" EVENT manually
})
}
}
// Instantiate and start the watcher
var watcher = new mainFileWatcher()
watcher.start();
// start the repl if asked
if (process.env.REPL === "true") {
var repl = require('repl');
var replServer = repl.start({
prompt: 'filewatcher via stdin> ',
input: process.stdin,
output: process.stdout
});
replServer.context.watcher = watcher;
}
Just copy and paste the code, install deps and run it.
Things I tried:
I changed the mongoose Promise object to use bluebird promises, hoping that
I could intercept some exception.
I browsed Mongoose calls with node-inspector and indeed the find method gets called and it seems that it throws no exceptions. I really can't figure out what's happening because I don't get any errors at all.
It is not the database connection (I tried to open one just before the findMailSubscriptions call and got an exception for trying to open an already opened connection).
I figure it might be some issues with scopes or promises.
Is there something I am missing about mongoose, or is it just my code that causes this behaviour?

Categories

Resources