40

I'm developing a Github repo which follows the offical tutorial of Angular (Tour of Heroes). You can see all the code here.

My problem, is that I have a directive declared in the main module of the app (app.module) and, if I use it inside the AppComponent, it works good (the directive only highlight a text inside a DOM element).

But I have another module called HeroesModule within AppModule, and inside a component of this module, this directive doesn't work.

The main code, here:

app/app.module.ts

...

import { HighlightDirective } from "./shared/highlight.directive";

@NgModule({
    imports: [
        BrowserModule,
        FormsModule,
        HttpModule,
        InMemoryWebApiModule.forRoot(InMemoryDataService),
        AppRoutingModule,
        CoreModule,
        HeroesModule
    ],
    declarations: [
        AppComponent,
        HeroTopComponent,
        HighlightDirective <-------
    ],
    providers: [
        { provide: APP_CONFIG, useValue: AppConfig }
    ],
    bootstrap: [ AppComponent ]
})

...

app/heroes/heroes.module.ts

@NgModule({
    imports: [
        CommonModule,
        FormsModule,
        HeroRoutingModule
    ],
    declarations: [
        HeroListComponent,
        HeroSearchComponent,
        HeroDetailComponent,
        HeroFormComponent
    ],
    providers: [
        HeroService
    ],
    exports: [
        HeroSearchComponent
    ]
})

app/shared/highlight.directive.ts

import { Directive, ElementRef, Input } from '@angular/core';

@Directive({ selector: '[tohHighlight]' })

export class HighlightDirective {
    constructor(el: ElementRef) {
        el.nativeElement.style.backgroundColor = 'yellow';
    }
}

app/app.component.ts

<h1 tohHighlight>{{title}}</h1> <----- HERE WORKS
<toh-nav></toh-nav>
<router-outlet></router-outlet>

app/heroes/hero-list/hero-list.component.ts

<div *ngIf="selectedHero">
    <h2>
        {{selectedHero.name | uppercase}} is my hero
    </h2>
    <p tohHighlight>Test</p> <----- HERE IT DOESN'T
    <button (click)="gotoDetail()">View Details</button>
</div>

You can see, install it and test it by yourself in the Github repo, if you need it.

Alexander Abakumov
  • 10,817
  • 10
  • 71
  • 111
ismaestro
  • 5,270
  • 6
  • 31
  • 45
  • 1. The directive should belong to a module 2. One option is you can create a separate module for all your directives and inject the new one into your main application module – CrazyMac Jan 06 '17 at 12:11
  • 1
    Move it to another module (feature module) and then add this module to `import: []` of every module where you want to use it. – Günter Zöchbauer Jan 06 '17 at 12:13
  • 3
    @GünterZöchbauer You're right ofcourse. the excepted answer is wrong. you'd still need t import it to every module you need its exports. Only injectors/services are hoisted to root ( unless lazy) – Royi Namir Mar 28 '18 at 08:51
  • One pitfall I ran into is that changes to your module structure might not get picked up by a "hot" build process. I use webpack dev server (with `@ngtools/webpack` plugin) normally, and I was tearing my hair out trying to figure out why my directive wasn't getting applied. Once I restarted my build process, it worked immediately. – Coderer May 11 '21 at 08:00

2 Answers2

57

According @CrazyMac's comment, a good way would be to create a DirectivesModule. Inside this module you can declare and import all your directives then you will be able to import this module anywhere you want.

app/modules/directives/directives.module.ts

import { NgModule } from '@angular/core';
import { HighlightDirective } from './highlight.directive';

@NgModule({
  imports: [],
  declarations: [HighlightDirective],
  exports: [HighlightDirective]
})
export class DirectivesModule { }

app/modules/directives/highLight.directive.ts

import { Directive, ElementRef } from '@angular/core';

@Directive({ selector: '[myHighlight]' })
export class HighlightDirective {
  constructor(el: ElementRef) {
    el.nativeElement.style.backgroundColor = 'yellow';
  }
}

app/modules/otherModule/other-module.module.ts

import { DirectivesModule } from '../directives/directives.module';

@NgModule({
  imports: [
    DirectivesModule
  ],
  declarations: []
})
export class OtherModule { }
frido
  • 8,763
  • 3
  • 28
  • 46
Lilrom
  • 1,700
  • 2
  • 14
  • 29
  • 29
    This should be the accepted answer. Directives need to be imported into every module that will use it. You cannot declare a directive globally. https://angular.io/guide/sharing-ngmodules – jriver27 Aug 01 '18 at 17:37
  • agree !! i just was facing same issue, and even though taking accepted answer approach it did not work ! i was able to do it via loading my directives module in the module where I am using directive ! – kuldeep Dec 09 '19 at 14:48
  • I couldn't get this working for Angular9. Not sure what I am missing.. – Mehmet Kurtipek Sep 16 '20 at 17:18
43

If you need to use the Directive

@Directive({
  selector: '[appMyCommon]'
})
export class MyCommonDirective{}

everywhere you should create a new Module.

If you use the Angular CLI you can generate:

ng g module my-common

The Module:

@NgModule({
 declarations: [MyCommonDirective],
 exports:[MyCommonDirective]
})
export class MyCommonModule{}

Important! the exports allow you to use the Directives outside the Module.

Finally, import that Module in every Module where you need to use the Directive.

for example:

@NgModule({
  imports: [MyCommonModule]
})
export class AppModule{}

Example: Plunker

frido
  • 8,763
  • 3
  • 28
  • 46
YairTawil
  • 2,762
  • 17
  • 19
  • 1
    So, in this case, I have another module which I didn't mention called CoreModule. If I exports the directive in that module, and in AppModule I import CoreModule, this should work for all components? because it doesn't... :( – ismaestro Jan 06 '17 at 12:40
  • 1
    On the compnents of AppModule declarations it will work, but you need to import MyCommonModule on every independent module which want to use common components. – YairTawil Jan 06 '17 at 12:47
  • 8
    Thanks!! Finally I solved it creating a new module called SharedModule (that declares and export the directive) and import it in AppModule and HeroesModule. But, is there a possibility to import it only in AppModule and inherit it to all modules which AppModule import? – ismaestro Jan 06 '17 at 17:34
  • 2
    @YairTawil how does ionic to import all his components, without to declare in every module? where is defined like global? by example: , etc ? – stackdave Jul 12 '17 at 08:28
  • exports:[ionListComponent, ionSearchbarComponent] etc... you can export modules inside packages too: for example: ``` @IonicModule({ exports: [ListsModule, ButtonsModule.....] }) ``` – YairTawil Jul 13 '17 at 06:45
  • @Ismaestro Is there anything else you need to put inside that `SharedModule`? In my app, I have shared components, services, validators for Reactive Forms, etc. None of those require a separate module to import everywhere. Just importing in AppModule works. I feel so dirty doing this ONLY for Directives. – Ka Mok Mar 01 '18 at 16:53
  • 4
    [**That is simply not true**.](https://plnkr.co/edit/oGnWDdRxAeP4BPj7p2W5?p=preview) if you have another module which has a component and that component want something from the other module of yours - even if you exported it to the root - it WONT BE RECOGNIZED by the new module. Only injectors does – Royi Namir Mar 28 '18 at 08:50