Using JavaScript ES6 I have several classes into which I want to separate the various concerns of my app. I have a main.js file in which I wish to instantiate the classes and then be able to reference and call methods across them.
My question is: What is the best/standard way to do this? My current approach is as follows;
In main.js I create an App class which instantiates two classes
import ClassOne from './ClassOne';
import ClassTwo from './ClassTwo';
export default class App {
constructor () {
this.one = new ClassOne(this);
this.two = new ClassTwo(this);
}
}
const app = new App();
Then in ClassOne.js I do something like this
export default class ClassOne {
constructor (app) {
this.app = app;
this.app.two.callMethod();
}
}
It certainly works, but does it look stoopid & is there a better way of doing it?
I would suggest having a setter in ClassOne and ClassTwo. That way only ClassOne and ClassTwo are dependent on each other, and not on an instance of App.
ClassOne.js
export default class ClassOne {
setTwo (two) {
this.two = two;
this.two.callMethod();
}
}
ClassTwo.js
export default class ClassTwo {
setOne (one) {
this.one = one;
this.one.callMethod();
}
}
main.js
import ClassOne from './ClassOne';
import ClassTwo from './ClassTwo';
export default class App {
constructor () {
this.one = new ClassOne();
this.two = new ClassTwo();
this.one.setTwo(this.two);
this.two.setOne(this.one);
}
}
const app = new App();
Related
I'm building an app with webpack for the first time and i'm trying to wrap my head around organizing class files. I'm having trouble getting the code to work. I'm still new to ES6 and such, so my code below is probably very wrong but i'm not sure its my approach/concept or its my syntax.
entry is index.js and I also have these files
import App from './js/app.js';
import User from './js/user.js';
import Guest from './js/guest.js';
const app = new App();
const user = new User();
const guest = new Guest();
$(document).ready(function () {
app.DatabaseStore.senddata();
console.log( user.getall() );
});
src/js/app.js the main global method/variable class
import CookieStore from './cookie.js';
import DatabaseStore from './database.js';
export default class App {
constructor() {
this.cookieStore = new CookieStore();
this.databaseStore = new DatabaseStore();
}
gettime() {
return 'time';
}
}
src/js/user.js methods that are for users
import App from './app.js';
export default class User extends App {
constructor() {
this.mydata = App.cookieStore.getData();
console.log(this.mydata );
}
getall() {
return ['foo', 'bar', 'baz'];
}
}
src/js/guest.js methods that are for guests
import App from './app.js';
export default class Guest extends App {
constructor() {
this.mydata = App.cookieStore.getData();
}
}
src/js/cookie.js cookie manipulating methods
export default class CookieStore {
constructor() {}
mydata() {return 'foo';}
}
src/js/database.js firebase methods
export default class DatabaseStore {
constructor() {}
senddata() {
this.mydata = App.cookieStore.getData();
}
You are trying to access instance property statically. You need to create an instance of App class before trying to access cookieStore property. You can create an instance and export it in your app.js to have singleton instance.
//in your app.js
export const app = new App();
in other files
import {app} from './js/app.js'
app.cookieStore.getData();
How can I make a Singleton class if I want to import it at multiple places?
I ended up with something like this, but I am exporting a new() instance (at least I think so) at each time I import it.
class RenderToRootAPI {
renderToRootComponent = null
setRenderComponent = ref => this.renderToRootComponent = ref
setInstance = instance => {
console.warn('setting instance')
this.renderToRootComponent.setInstance(instance)
}
deleteInstance = () => this.renderToRootComponent.deleteInstance
}
export default new RenderToRootAPI()
What you have written will export a singleton. It doesn't matter how many times you import it.
It might look a bit more clear if you write it like this as an example:
class RenderToRootAPI {
renderToRootComponent = null
setRenderComponent = ref => this.renderToRootComponent = ref
setInstance = instance => {
console.warn('setting instance')
this.renderToRootComponent.setInstance(instance)
}
deleteInstance = () => this.renderToRootComponent.deleteInstance
}
const renderToRootAPI = new RenderToRootAPI();
export default renderToRootAPI;
The class is not even exported, and the single exported instance will be used in all the modules that import it.
So I have come across an interesting issue while trying to extend a class to use in another class and then import it to another file.
'class-test.js':
export default class MyClass {
constructor () {
this.date_created = new Date()
this.posts = new Posts()
}
}
class Posts extends Array {
add (val) {
this.push(val)
}
}
Then when I create a new MyClass instance in another file (and import MyClass from class-test.js), the myClass.posts property is only being seen as an Array and so doesn't have the extended function add()
I think the problem is that the Posts class is not being moved with the MyClass class; but without casting I have no idea how to tell it to use that class.
Where I'm particularly frustrated is it works fine if all in one file:
class MyClass {
constructor () {
this.date_created = new Date()
this.posts = new Posts()
}
}
class Posts extends Array {
add (val) {
this.push(val)
}
}
var x = new MyClass('as', 'asd')
x.posts.add('asdf')
console.log(x.posts)
x.posts.add('qwer')
x.posts.add('zxcv')
console.log(x.posts)
Did you try to export, and import both classes ?
'class-def.js':
class MyClass {
constructor () {
this.date_created = new Date()
this.posts = new Posts()
}
}
class Posts extends Array {
add (val) {
this.push(val)
}
}
export { MyClass, Posts };
'class-test.js':
import { MyClass, Posts } from 'class-def.js';
var x = new MyClass('as', 'asd')
x.posts.add('asdf')
console.log(x.posts)
x.posts.add('qwer')
x.posts.add('zxcv')
console.log(x.posts)
Read the example below*, but don't pay too much attention to the EventEmitter inheritance, please – it just shows the utility of the class syntax.
I realize that the example is not correct ES2015, since there no such thing as a static class statement.
What would be the most syntactically lean way to make something like this work in ES2015?
class App extends EventEmitter {
addPage(name) {
this[name] = new App.Page;
this.emit("page-added");
}
static class Page extends EventEmitter {
constructor() {
super();
this._paragraphs = [];
}
addParagraph(text) {
this._paragraphs.push(text);
this.emit("paragraph-added");
}
}
}
Should I just split it up and use a class expression, like below? Seems less elegant.
class App extends EventEmitter {
addPage(name) {
this[name] = new App.Page;
this.emit("page-added");
}
}
App.Page = class extends EventEmitter {
constructor() {
super();
this._paragraphs = [];
}
addParagraph(text) {
this._paragraphs.push(text);
this.emit("paragraph-added");
}
};
Should I just split it up and use a class expression?
Yes, that's the way to go. If you insist on using a declaration, you'd have to make a App.Page = Page assignment afterwards.
You could have your class created inside addPage. It will create the "abstraction" I suppose you're looking for, but you'll pay in performance (each addPage call will be slower.
'use strict';
class EventEmitter {
emit(s) {
alert(s);
}
}
class App extends EventEmitter {
addPage(name) {
class Page extends EventEmitter {
constructor() {
super();
this._paragraphs = [];
}
addParagraph(text) {
this._paragraphs.push(text);
this.emit("paragraph-added");
}
}
this[name] = new Page;
this.emit("page-added");
}
}
var app = new App;
app.addPage("myPage");
app.myPage.addParagraph("Some content");
Alternatively, have both classes defined in a module, and only export the App class thus preventing pollution of the global scope.
I m actually learning typescript, and I m facing some problems with internal modules.
In fact, I have three files :
index.ts in which I start my app
///<reference path='RouteManager.ts'/>
import RouteManager = RestifyRouting.RouteManager;
var myManager = new RouteManager();
myManager.init("superpath");
RouteManager.ts that manage my REST routes
///<reference path='RouteParser.ts'/>
module RestifyRouting {
export class RouteManager {
routeParser:RouteParser;
constructor() {
}
public init(filePath) {
this.routeParser = new RouteParser();
this.routeParser.register("zfaf","callback");
console.log(filePath);
}
}
}
RouteParser which has to parse some string to get some informations
module RestifyRouting {
export class RouteParser {
constructor() {
}
public register(path, callback) {
console.log('super register');
}
}
}
I have a gulp file that creates my .js and d.ts files and it works great, except for the index.js file. The compiler tells my that RestifyRouting (which is my internal module) is undefined and I dont know why...
Can you help me ?
PS : every files are in the same folder, it's just a learning application.
Thanks for advance
As of TypeScript 1.5 the module syntax is aligned with ES6 module syntax and that is what I have been using as well...
You can remove any references to TypeScript modules and just export the classes directly
index.ts
import { RouteManager } from './RouteManager';
var myManager = new RouteManager();
myManager.init("superpath");
RouteManager.ts
import { RouteParser } from './RouteParser';
export class RouteManager {
routeParser:RouteParser;
constructor() {}
public init(filePath) {
this.routeParser = new RouteParser();
this.routeParser.register("zfaf","callback");
console.log(filePath);
}
}
RouteParser.ts
export class RouteParser {
constructor() {}
public register(path, callback) {
console.log('super register');
}
}
Keeping modules
If you'd like to keep using internal modules then you have to be sure to export your module as well as the classes inside the module.
// RouteManager.ts
export module RestifyRouting {
export class RouteManager{}
}
//index.ts
import { RestifyRouting } from './RouteManager';
//usage
var manager = new RestifyRouting.RouteManager();
Something to keep in mind is that you will not be able to import multiple items into the the same name.
// i.e.
import { RestifyRouting } from './RouteManager';
import { RestifyRouting } from './RouteParser';
NOTES
the {} syntax in the import statement can allow multiple imports
{ Class1, Class2 }
The {} can be skipped if you exporting a default:
//Source (foo.ts):
export default class Foo{}
//Reference:
import Foo from './foo';
//usage:
class User {
foo: Foo;
}