ES 6 - Using Classes - javascript

I am learning ES 6 syntax of classes. I come from a C# background, so I apologize if my terminology isn't correct. Or, if I'm doing something that looks odd.
I'm building a web app as a learning exercise. It's built on Node and Express. I have some routes defined like this:
'use strict';
module.exports = function() {
const app = this;
app.use('/blog', function(req, res) {
console.log('loading blog postings');
res.render('blog', {});
});
app.use('/', function(req, res) {
console.log('looking up: ' + req.path);
res.render('home', {});
});
};
I'm trying to put some viewModels behind these views. So, I have a directory called viewModels. That directory has these files:
index.js
blog.js
home.js
The files currently, probably inaccurately, look like this:
index.js
'use strict';
module.exports = function() {
const HomeViewModel = require('./home);
const BlogViewModel = require('./blog);
};
blog.js
export default class BlogViewModel {
constructor() {
this.title = 'My Blog';
}
}
home.js
export default class HomeViewModel {
constructor() {
this.title = 'Home';
}
}
My thought, was that I could use index.js as a way to define my package or namespace. Then, in my routing code, I could do something like this:
'use strict';
module.exports = function() {
const app = this;
const ViewModels = require('../viewModels/index');
app.use('/blog', function(req, res) {
console.log('loading blog postings');
let viewModel = new ViewModels.BlogViewModel();
res.render('blog', viewModel);
});
app.use('/', function(req, res) {
console.log('looking up: ' + req.path);
let viewModel = new ViewModels.HomeViewModel();
res.render('home', viewModel);
});
};
However, when I attempt this, I get some runtime errors that say "Error: Cannot find module '../viewModels/index'". This implies that I'm not setting up my module properly. But, it looks like I am what am I doing wrong?

Your index.js file is incorrect, you don't export ViewModels from there. Change it to this:
'use strict';
module.exports = {
HomeViewModel: require('./home'),
BlogViewModel: require('./blog')
};
And ... viewModels it's good for C#, but not for Node.js. In node it should be just models, IMO.
Update:
Node.js doesn't completely support all ES6 features, especially new modules declaration: https://nodejs.org/en/docs/es6/. You should use the standard CommonJs module declaration to export your function:
'use strict';
class HomeViewModel {
constructor() {
this.title = 'Home';
}
}
module.exports = HomeViewModel;

Actually, I am not really sure what you want to ask. If I answered wrong stuff, just never mind.
First, the reason why you get error Error: Cannot find module '../viewModels/index' is because you put two dots there. It should be just one dot means start from here. However, I am not sure if this is the problem. I wanted to ask where you put routing code, but I do not have permission to comment yet. (Ahhhh.... Stack overflow you kidding me...)
Second, here is the proper way to export a class in ES6.
For example:
AClass.js
'use strict';
//This module can be used within the class. However, you cannot use it in another file.
const AModule = require('AModule');
//The class name used here just for debug output.
module.exports = class AClass {
constructor(startValue) {
//Not like C#. JavaScript does not define private or public.
this.value = startValue;
}
method(incValue) {
this.value += incValue;
AModule(); //Just show you can use this module within the class;
}
}
main.js
'use strict';
//You need to put a ./ before the path if you are include another module made by yourself.
//You do not need it for a npm module.
const AClass = require('./AClass.js');
//Now you just required the class of the AClass, you still need to new one;
var aClass = new AClass(500);
//After new one, you can call its method.
aClass.method(30);
//You can access its property by a dot;
console.info(aClass.value); //530
This is the 100% work way to create a class in ES6.
Here is detailed documentation:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
Anyways, classes in JavaScripts are just like functions, and a function's prototype will be its methods. new Class() is just create a object, run the function Class with object as this (Same as Class.bind(obj, parameters)), then link the new object's constructor property to the function used.
module.exports = xxx is just make the xxx the value of this module. For instance, if you module.exports = 'Hello'; and console.info(require('module'));, you will get Hello.

Related

Testing node js module with injected dependecy

I am java, c# programmer with more than 10 years experience. Now I have to write some code in js and really, I feel like Alice in Wonderland....
I have myModule.js :
module.exports = function (logger) {
return {
read: function (param) {
......
logger.eror(....)
},
write: function (param) {
......
logger.info(....)
}
};
}
The logger.js, (legacy code, I cannot change it) looks similar:
module.exports = function(some_settings){
return {
info: function () {
......
},
error: function () {
......
}
};
}
Important thing: logger is not included to myModule by require("logger") but injected in some "global" index.js file which looks like:
var settings = require("../someSettings.js")();
var logger = require("../logger.js")(settings);
var myModule = require("../myModule .js")(logger);
Now I want to write UT for myModule in mocha. So I want to mock logger. And I face the wall.
I tried proxyquire but this is not good solutions because logger is not included to myModule by require.
I tried sinon stub and mock e.g.:
var logger = require("logger.js");
var loggerMock = sinon.mock(logger);
var myModule= require("myModule.js")(loggerMock);
Or:
var logger = require("logger.js");
var loggerStub = sinon.stub(logger, "info", function() {return ....});
var myModule= require("myModule.js")(loggerStub);
But I still have some errors like :
"Attempted to wrap undefined property error as function"...
Please, be my white rabbit in this javascript freeky world...
When you are testing your module, you can use a fake object for your logger object, i.e.:
const logger = {info: noop, error: noop, debug:...};
function noop() {}
or well create a mock object with sinonjs if you prefer create it programatically and use spy features.
Then, you can inject it wrapping your module definition in a closure (as you posted):
# myModule.js:
module.exports = (dep1, dep2, ...) => {
// module definition
}
Or use rewire package, to "inject" module scoped dependencies (it works only for local-vars, and not need to create a closure in your module):
# myModuleTest
myModule = rewire('./myModule');
myModule.__set__('logger', { /* fake logger */ });

Module inheritance in NodeJS

I am trying to implement some functionality into a NodeJS application where some of my controllers (modules) will inherit from one bass module in order to store all common functionality in there. I am having difficulty doing this though and I am unsure why.
Here is what I have so far:
My base module (from which others will inherit from):
var http = require('http');
function ApiBaseController() {
var self = this;
}
ApiBaseController.prototype.makeRequest = function() {
};
module.exports = ApiBaseController;
A child module (that will inherit from the one above:
var APIBaseController = require(__dirname + '/apiBase/ApiBaseController.js'),
inherits = require('util').inherits;
function JustGivingAPI() {
APIBaseController.apply(this);
}
inherits(APIBaseController, JustGivingAPI);
JustGivingAPI.prototype.getAmount = function() {
console.log("here");
};
module.exports = JustGivingAPI;
And the client code which requires the module:
var express = require('express');
var router = express.Router();
var JustGivingController = require('../controllers/api/JustGivingAPI');
var APIObject = new JustGivingControllerObject();
I am trying to implement the aply Javascript method from within the JustGivingAPI module's constructor to inherit the parent module's methods but they don't appear in the returned module.
Can anyone advise me as to where I have gone wrong and if this is a good idea or not?
thanks!
I had the objects the wrong way round in the util.inherits function!
Should be:
util.inherits(JustGivingAPI, APIBaseController);

Requirejs dynamic reference to sub module methods?

Using requires, I’ve split larger class structures down into modules that use other modules within directory. There’s a main file that instantiates the other sub modules. This is an API class with two modules. One that deals with posting data to the endpoint, and the other that holds functions that are helpers to that post module:
define([
'./Post',
'./Helper'
], function (PostModule, HelperModule) {
'use strict';
var module = function () {
this.Post = new PostModule();
this.Helper = new HelperModule();
};
return module;
});
Now I can chain these modules like this:
var foo = new ApiModule();
foo.Post.postToAPI();
foo.Helper.matchAPIactionToEvent();
which is exactly what I want..
BUT, the problem is within the Post.js file, is that it doesn’t know anything about the Helper.js file. So I can’t take advantage of any of those methods. What I would like to do within the Post.js file is to be able to reference the other functions within the same class like so:
define([
'../environment',
'loglevel',
'../utility/Utility',
'jquery'
], function (EnvironmentModule, log, UtilityModule, $) {
'use strict';
var module = function () {
var environment,
utility,
api;
environment = new EnvironmentModule();
utility = new UtilityModule();
this.postToAPI = function (event, payload, callback) {
var apiEndpoint,
requestIdString,
eventAndAction;
apiEndpoint = environment.getEnvironmentPath().API;
requestIdString = utility.String.generateRandomString(32);
/*** THIS IS HOW I WANT TO CALL THE HELPER METHODS ***/
eventAndAction = this.Helper.matchAPIactionToEvent(event);
/*** THIS IS HOW I WANT TO CALL THE HELPER METHODS ***/
payload.event = eventAndAction.event;
payload.action = eventAndAction.action;
payload.requestID = requestIdString;
payload = $.param(payload);
$.post(apiEndpoint + '?' + payload, function (result) {
if (callback) {
callback(result);
}
});
return;
};
};
return module;
});
I figured out a working solution to the this, where I pass '../api/Helper' as one of the array values in the define statement of Post.js; but I don’t want to do that. What I want is to have Post.js be able to access any method from any other modules that are contained within the same directory. That way I don’t have to explicitly define them. It seems wrong to instantiate a new ApiModule() within Post.js. Here’s the directory structure:
Modules/api/Api.js
Modules/api/Post.js
Modules/api/Helper.js
...
I hope that makes sense. Is this possible?
Since you want any of the child modules to access any other child modules, what you could do is pass the parent module as an argument to the constructors of the child modules:
var module = function () {
this.Post = new PostModule(this);
this.Helper = new HelperModule(this);
};
Have the child modules store this information:
var module = function (parent) {
this.parent = parent;
Then use this this.parent to call the methods on other child modules:
eventAndAction = this.parent.Helper.matchAPIactionToEvent(event);
Note that if you would load '../api/Helper' with RequireJS in Post.js as you were thinking of doing, you would not be able to use the same object instance as the one defined on the parent module. You'd have to construct a new object for use in Post.js.

node.js expose variable to module?

I have read a lot of articles about how to create modules in node.js, and you can use module.exports to expose module internals to the file which includes it.. awesome!
How does this work the other way around? I'll use the following as an example :-)
USER.JS
function User() {
this.property = 'value';
this.doSomething = function() {
var getStuff = mainFileFunction();
// do something with getStuff
}
module.exports = User;
MAIN.JS
var myArray = [];
myArray.push('something');
myArray.push('something else');
mainFileFunction() {
for(thing in myArray) {
return myArray[thing];
}
}
var u = new user();
log(u.property); <--- THIS IS EXPOSED, COOL!
u.doSomething(); <--- This will throw an error because mainFileFunction is not defined in user.js :-(
If I were to move mainFileFunction to the user file, then it still wouldn't work because the myArray array wouldn't be defined... and if I were to move that over too, I wouldn't be able to use it in other functions in main (which I want to) :-)
I'm sorry if I'm missing something really obvious here... What I want is to expose the parts of my choosing from modules I include (module.export works for that) but I also want to expose everything from the main file to all the includes..
or just expose everything to everything? is that totally messy and horrible??
Just to explain what I am trying to do here... I want to have classes defined in separate files, but I want to instantiate a bunch of them as objects in the main file and store them in arrays.. I want the objects to contain methods which can access arrays of the other object types.
Thanks guys! :-)
You can use globals, or have a proper circular dependency (requireing both files), however - this is usually a bad habit which can lead to maintainability problems in the future.
Instead, you can use dependency injection and inject doSomething into your module.
This basically gives you the following for free:
You can test User with a simple mock implementation of doSomething later and verify the correctness of your code
The dependencies of a user are explicit and not implicit, which makes it obvious what a user needs.
I'll provide two implementations, one using constructor dependency injection and one with a module wide setting.
USER.JS
function User(dependentFunction) {
this.property = 'value';
this.doSomething = function() {
var getStuff = dependentFunction();
// do something with getStuff
}
}
module.exports = User;
MAIN.JS
...
var u = new User(mainFileFunction);
u.doSomething(); // this will now work, using mainFileFunction
What happens here is fairly simple, and we know what's going on.
This can also be a module wide setting
USER.JS
function User(depFunc) {
this.property = 'value';
this.doSomething = function() {
var getStuff = depFunc();
// do something with getStuff
}
}
function UserFactory(depFunc){
return function(){
return new User(depFunc);
}
}
module.exports = UserFactory;
MAIN.JS
var getUser = UserFactory(mainFileFunction);
var u = getUser(); // will return a new user with the right function
+1 to Benjamin answer for dependency injection.
I would like to add another way to inject objects in your modules by passing the dependency in the require like require('./module.js')(dependentFunction);
//MAIN.js
var db = function() {
this.rows = [];
this.insert = function(name) {
this.rows.push(name);
console.log('Db: Inserting user with name ' + name);
}
this.getAll = function(){
return this.rows;
}
}
var fakeDb = new db();
var user = require('./user.js')(fakeDb);
user.add('Jhon');
user.add('Rose');
user.list();
and
//users.js
module.exports = function(db) {
return {
add: function(name) {
db.insert(name);
},
list: function() {
var users = db.getAll();
var i = users.length;
console.log('listing users\n-------');
while(i--) {
console.log(users[i]);
}
}
}
}
You should pass mainFileFunction as a parameter to the constructor of user.
USER.JS
function User(mainFileFunction) {
this.property = 'value';
this.doSomething = function() {
var getStuff = mainFileFunction();
// do something with getStuff
}
module.exports = User;
In your main.js use the following
var u = new user(mainFileFunction);
How about moving mainFileFunction to user.js, and have the function accept an array as an argument:
mainFileFunction(array) {
for(thing in array) {
return array[thing];
}
}
And then when you call it from main.js, pass the function your array:
u.doSomething(myArray);

Including JavaScript class definition from another file in Node.js

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');

Categories