1

I'm building an app that will contain a number of routes, each defined as an NgModule. Each module is pretty similar, so I'd like to abstract it to a reusable function which takes a set of components, and builds a top-level @Component and @NgModule.

So here's one of those modules:

import { makeModule } from './module-factory';
import { Component } from '@angular/core';

@Component({
  selector: 'app-component-1',
  template: `<p>First Component</p>`
})
export class FirstComponent {}

@Component({
  selector: 'app-component-2',
  template: `<p>Second Component</p>`
})
export class SecondComponent {}

export const FooModule = makeModule('foo', [FirstComponent, SecondComponent]);

And here's the makeModule() function I'm trying to build:

import { NgModule, Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Route, RouterModule } from '@angular/router';

export const makeEntryComponent = (components: any[]) => {
  @Component({
    template: `<p>Entry Component</p>
      <ng-container *ngFor="let component of components">
        <ng-container *ngComponentOutlet="component"></ng-container>
      </ng-container>
    `
  })
  class EntryComponent {
    components = components;
  }
  return EntryComponent;
};
export const makeModule = (componentName: string, components: any[]): any => {
  const EntryComponent = makeEntryComponent(components);
  const routes: Route[] = [{ path: componentName, component: EntryComponent }];
  const declarations = [EntryComponent, ...components];

  @NgModule({
    imports: [CommonModule, RouterModule.forChild(routes)],
    declarations,
    entryComponents: components,
    exports: [EntryComponent]
  })
  class Module {}
  return Module;
};

So this works fine using the JIT compiler, but as soon as I run ng serve --aot, it fails with:

ERROR in Error: Error encountered resolving symbol values statically. Function calls are not supported. Consider replacing the function or lambda with a reference to an exported function (position 18:27 in the original .ts file), resolving symbol makeModule in C:/Users/IGEN261/code/github/ng-dynamic/src/app/module-factory.ts, resolving symbol FooModule in C:/Users/IGEN261/code/github/ng-dynamic/src/app/foo.module.ts, resolving symbol FooModule in C:/Users/IGEN261/code/github/ng-dynamic/src/app/foo.module.ts, resolving symbol AppModule in C:/Users/IGEN261/code/github/ng-dynamic/src/app/app.module.ts, resolving symbol AppModule in C:/Users/IGEN261/code/github/ng-dynamic/src/app/app.module.ts

I feel like there has to be a way to make this consistent with AOT, but I'm not sure how. Should I be structuring my code in a different manner? Or do I have to abandon any prospect of DRY in this codebase to make it AOT-compatible?

FYI - the full project is available at GitHub here:https://github.com/mattdsteele/ng-dynamic-aot

matthewsteele
  • 1,797
  • 1
  • 15
  • 31
  • 1
    AOT is terrible about this and has many other limitations. When I last checked, `=>` was still unsupported in decorators but I don't think that's the issue here. You could try using `function` though.` The real problem is that you are forced to work in an poorly undocumented, ES-incompatible, and unspecified TypeScript dialect. – Aluan Haddad Dec 01 '17 at 07:37
  • Yeah changing the arrows to `function` didn't fix it. – matthewsteele Dec 01 '17 at 14:27
  • I think you are hosed. Maybe your entry point can be AOT compiled and have lazy JIT feature modules? Seems like a lot of complexity though. – Aluan Haddad Dec 01 '17 at 14:35

0 Answers0