Angular 2/4: Is it possible to encapsulate classes in a NgModule?

784 views Asked by At

I have several classes that I would like to include in a module, so I can then just import the module, as it was a different package, and use those classes from it. Here's a small example:

human.ts (my class file)

export class Human {

  private numOfLegs: Number;

  constructor() {
    this.numOfLegs = 2;
  }
}

test.module.ts (my module file)

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { Human } from './human';

@NgModule({
  imports: [CommonModule],
  declarations: [
    Human
  ],
  providers: [],
  exports: [Human]
})
export class TestModule {}

How do I instantiate the Human class in a component? I've tried both:

import { TestModule } from './test.module';

and

import { Human } from './test.module';

But if I do new Human() I still get cannot find name Human

3

There are 3 answers

3
jaibatrik On BEST ANSWER

Angular modules and ES6 / TypeScript / Node modules are different. Angular modules are a collection of Components, Services and Directives; whereas the ES6 modules comprise of classes in most cases.

If you want to reuse your NgModule which is dependent on other non-Angular classes you can export them as ES6 modules and use them elsewhere. Have a file like export.ts or index.ts and place the following export statements there -

export { TestModule } from './test.module';
export { Human } from './human';

Now, when you want to use the NgModule somewhere, you import it with a command like the following -

import { TestModule } from '../lib/export'; 
0
Estus Flask On

A class shouldn't be provided in declarations or exports, these ones are for components directives and pipes, providing Human there is a mistake.

The first option is that Human is provided as value provider (not as class provider) in the module, so it should be instantiated manually. This option is preferred when a class accepts non-DI arguments.

@NgModule({
  providers: [{ provide: Human, useValue: Human }]
})
export class TestModule {}

...

import { Human } from '...';

@Component(...)
class SomeComponent {
  constructor(@Inject(Human) Human: typeof Human) {
    this.human = new Human();
  }
}

The second option is to make Human a component provider. It is instantiated for each component instance. In this case TestModule is redundant. This option is generally preferred:

import { Human } from '...';

@Component({ providers: [Human], ... })
class SomeComponent {
  constructor(public human: Human) {}
}

In both cases Human DI token should be imported in component file where it's used.

2
Yakov Fain On

Make the Human class injectable and declare it in the providers section of your test module.

If you app module (the root one) loads the test module eagerly, the providers declared in the test module will be available in the app module and you'll be able to inject Human in your components from the root module.

The situation is different if you load your test module lazily - these have their own injectors and don't share providers with other modules.

@NgModule({
  imports: [CommonModule],
  providers: [Human]
})
export class TestModule {}

I assume that you're loading the TestModule using the router configuration:

@NgModule({
  imports: [ BrowserModule, TestModule,
    RouterModule.forRoot([
      {path: 'test', loadChildren: TestModule},
      )
  ],
    bootstrap:    [ AppComponent ]
})

In AppComponent you can inject Human:

export class AppComponent {

  constructor(human: Human) {
    console.log(human.numOfLegs);
  }
}

Make sure that numOfLegs is public.