1

I am writing an app that has features that can be turned on and off via a config.json that looks something like this:

"appFeatures": {
    "header": {
      "create": true,
      "title": "Here Comes the Sun"
    },
    "imageStrip": {
      "create": false,
      "imageDirectory":  "../data/images",
      "imageDimensions": [288, 162]
    },
    "areaChart": {
      "create": true
    },
    "axes": {
      "create": true
    }
}

For each feature there is already a corresponding class of the same name that implements the feature. I'd like to use the name of the feature to create a new instance of the class. After fetching the config.json, I have code (within a Main.js class) that looks like:

this.features = Object.entries(this.config.appFeatures)
  .filter((entry) => {
    return entry[1].create === true;
  });

this.features.forEach((feature) => { this.createFeatureInstances(feature[0]); });

And then I try to create instances, a la this.header = new Header():

  createFeatureInstances(featureName) {
    const className = `${featureName.replace(featureName[0], featureName[0].toUpperCase())}`;

    this[`${featureName}`] = new Function(`
      return class ${className} {}
    `)();

This creates a new, empty Header class and, I suppose, it's instance. It is not the Header class that I have already written and want to create an instance for. How might I write the createFeatureInstances function so that I can create the instance of each class that corresponds to a feature?

EDIT Because new features may be added to this app in the future by others, I would like to minimize the times that I hard code which features are available to the app. With my current design, another developer can add another feature by writing a new feature class, importing that class into the Main.js class, and pop the config entries into the config .json without having to touch anything else in the code. For this reason, solutions like this one: Create an instance of a class in ES6 with a dynamic name? won't give me a solution because they rely on having a complete list of the classes that should already exist.

3
  • 1
    Possible duplicate of Create an instance of a class in ES6 with a dynamic name? Commented Apr 17, 2018 at 1:22
  • You need to have a name-class mapping somewhere. Factory function or somewhere else is your call. Your current solution lacks this, which is causing the problem: "...creates a new, empty Header class and, I suppose, it's instance. It is not the Header class that I have already written and want to create an instance for" Commented Apr 17, 2018 at 2:47
  • @MaazSyedAdeeb, thanks for letting me know that this is the case. I think this is the answer. If you'd like to post it, I'll accept it. I am importing the feature modules into Main.js via import Feature1 from './features/Feature1.js, for example. Based on your response, I now include the following in the constructor of Main.js: this.Feature1 = Feature1. Then I can use this[${featureName}] = new this[${className}](); and all works as desired. My next question is then stackoverflow.com/questions/49885861/… Commented Apr 17, 2018 at 19:16

2 Answers 2

1

You need to have a name-class mapping somewhere. Factory function or somewhere else is your call. Your current solution lacks this, which is causing the problem:

...creates a new, empty Header class and, I suppose, it's instance. It is not the Header class that I have already written and want to create an instance for

Some explanation with a simple example

// Imagine you've defined Test
class Test {
  callMe() {}
}

// And your consumer wants a className Test to be created
const className = "Test";

// This is a simple map of the name to class
const nameToClass = {
  "Test": Test
}

// This creates a new class called Test, not your defined class
const AttemptedDynamicTest = new Function(`
      return class ${className} {}
    `)();

// The next two logs just prove the previous comment
console.log(Test === AttemptedDynamicTest); // false
console.log(typeof AttemptedDynamicTest.prototype.callMe); // undefined

// What you really need is to maintain a map, and just use it to dynamically
// create a new instance every time
console.log(typeof nameToClass[className].prototype.callMe); // function

Sign up to request clarification or add additional context in comments.

Comments

0

You can use a string to initialize a cooresponding (valid and existing) class using the following snippet:

var dynamicClassInstance = new this[classNameString]();

1 Comment

When I use this[${featureName}] = new this[className](); I get the following error: TypeError: this[className] is not a constructor

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.