1

I'm trying to write a little Prove of Concept to find out whether "Angular2 in typescript + Electron" will fullfill the requirements of a future Desktop App project.

But I'm having trouble... Am I trying to achieve the impossible or is there a way to do it? Here's my context:


Short version:

How can i make this run:

@Injectable()
export class PluginManager {    
  getPluginComponentForEventType(eventtype: string): Type {
    //return is angular "Type", later consumed by Dynamic Loader
    var externalModule = require('../mtplugins/' + eventtype + 'plugin.ts');
    return externalModule.TheRenderer;
  }
}

Without getting this error:

"../mtplugins/foorendererplugin.ts not declared as a dependency"

Long Version with Context:

Core requirement is a kind of "dynamic frontend plugin system" as I would call it. This means: One part of my UI must be replaceable at runtime - but I don't know the components that will be inserted there in advance.

Example scenario:

My App receives a "foo" event:

leEvent: { eventtype: 'foo', payload: '...' }

So it will tell its Plugin manager: Hey I need a UI Component to render a 'foo' event.

The plugin manager searches for a 'fooplugin.ts' file which contains the component, loads the component class from this file dynamically and returns the type. If it does not have a foo plugin now, the user may download this file from the plugin market to the plugin folder and then tries again.

My app then uses DynamicComponentLoader.loadIntoLocation to integrate this dynamically loaded type to the dom and then tells the component to render(leEvent).

This last part with dynamic loading components in angular is no problem, my pain point is the plugin manager method to load a class from a file... The best thing I got so far is this:

@Injectable()
export class PluginManager {    
  getPluginComponentForEventType(eventtype: string): Type {
    //return is angular "Type", later consumed by Dynamic Loader
    var externalModule = require('../mtplugins/' + eventtype + 'plugin.ts');
    return externalModule.TheRenderer;
  }
}

I run my tsc with "--module commonjs".

When I call the method it tells me that "../mtplugins/foorendererplugin.ts not declared as a dependency"... well, I can't declare the dependency in advance because its only known at runtime.


So, is there a way to load typescript classes from files that are only known at runtime? In Java I think I would do it with a Classloader, so i feel like I need typescript's counterpart of that thing. I actually thought I've read that typescript with commonjs and require() would support this, so maybe this mechanism and angular's system.js integration are biting each other?

I'm new to angular2 and typescript and electron and node, so please forgive if I overlook something basic.


EDIT

As pointed out by Louy the error message is nothing typescript specific. It was thrown by System.js...

Again it turns out that a bit more time spent on learning the technologies you want to use is always a good investment. Indeed I did a novice mistake here: mixing up different module-loading systems. I thought I had to work with CommonJS to be able to load external modules at runtime, but runtime loading is well supported by System.js, too - that means:

instead of

require(..)

I simply had to use

declare var System: any; //System.js Loader
...
System.import('./app/' + eventtype+ 'plugin.js');

Then all you have to do is keep the plugins independent of actual code of the application's core (as this causes module chaos... and is an architectural smell either way), instead depend only on interfaces (Typescript ftw!).

And that's all you need for your pluggable angular2 UI :)

user2663481
  • 21
  • 1
  • 6

1 Answers1

0

Why not make a dictionary of objects? You won't lose types this way.

// import all of them...
import pluginA from '../mtplugins/Aplugin.ts'

// add all here
const plugins: {[name: string]: /*typeof plugin*/} = {
  a: pluginA,
  // ...
}

// and then...

var externalModule = plugins[eventtype];
Louay Alakkad
  • 7,132
  • 2
  • 22
  • 45
  • Hi, Thank you for the quick answer! :) Your dictionary solution is indeed a very good pattern for this requirement and probably I'm gonna build such a dictionary of Plugins in the real project. But I'm afraid it does not solve the problem, that I don't know which modules to load at compile time. The problem is the first line: "import all of them"... I don't know all of them yet ;) The dictionary must be extensible at runtime. like: the user downloads a file or my app does it automatically and then adds it to the dictionary. – user2663481 Dec 11 '15 at 20:44
  • Let me explain here. Your plugin files were not included in the project at build time because they were not referenced anywhere. **You need to reference all files somewhere to get them included.** Either add them manually or check your build process. The issue is probably there. Build process is any one of these: grunt, gulp, webpack, browserify, etc... – Louay Alakkad Dec 11 '15 at 22:01
  • also btw, don't reference files with the ".ts" extension. `require` them without any extension. – Louay Alakkad Dec 11 '15 at 22:03
  • ".ts": thx for the tip! ... file loading: so, loading a file that I don't know at build time is not possible? – user2663481 Dec 11 '15 at 22:23
  • It's theoretically possible, but not in your build system. It's also a bad practice because it doesn't allow minification etc... – Louay Alakkad Dec 11 '15 at 23:11
  • typescript has nothing to do with the build system btw – Louay Alakkad Dec 11 '15 at 23:12
  • Ah i see, i was searching at the wrong place - it's not a typescript problem – user2663481 Dec 11 '15 at 23:58
  • Nope. "not declared as a dependency" is not a typescript error. :) – Louay Alakkad Dec 11 '15 at 23:59