Search code examples
angularoverridingangular-pipe

Overridden Angular pipe is not used in sub-component


I'm overriding Angular's built-in date pipe to include better i18n for dates. I implemented the pipe in a "toolbox" project that is later included as a dependency in the main project. I wrapped the new pipe in a module:

import { NgModule } from '@angular/core';
import { DateProxyPipe } from './dateproxy.pipe';

@NgModule({
    declarations: [
        DateProxyPipe
    ],
    exports: [
        DateProxyPipe
    ]
})
export class PipesModule { }

In the main project's app.module.ts, I import that module and set it in the providers:

import { PipesModule } from '../../../src/pipes/pipes.module';

@NgModule({
  imports: [
    ...
  ],
  entryComponents: [
    ...
  ],
  declarations: [
    AppComponent,
    GraphLegendComponent,
    TimeComponent,
    ...,
    DateProxyPipe
  ],
  providers: [
    DateProxyPipe,
    ...
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

This works beautifully when used directly, for example the {{selectedTimespan.from | date:'medium'}} in the template of the TimeComponent I left in the listing above is displayed in different formats when I change the language, exactly like I want it.

But it doesn't work in sub-components - i.e. the date pipe that is used in the template of a sub-component of the listed GraphLegendComponent does NOT change as the one in TimeComponent does.

I tried declaring, importing, providing etc. the DateProxyPipe in all sorts of locations, but can't get it to work. What am I doing wrong?

Edit: One of the sub-components is the TimespanShiftSelectorComponent. This is its module:

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

...
import { TimespanShiftSelectorComponent } from './timespan-shift-selector/timespan-shift-selector.component';

const COMPONENTS = [
  ...,
  TimespanShiftSelectorComponent
];

@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    ...
  ],
  declarations: [
    COMPONENTS,
  ],
  exports: [
    COMPONENTS
  ],
  providers: [
    ...
  ]
})
export class TimeModule {
}

Solution

  • It's quite simple actually, the problem is that you don't import the PipesModule into all your modules. Just importing it to AppModule doesn't allow feature modules to gain access to the content of PipesModule.

    I would recommend creating a SharedModule where you import and export PipesModule as well as anything else that you need to share (excluding component modules as it will create circular dependency).

    For components create another ComponentModule and import and export your component modules.

    Now in your feature modules simply import SharedModule and ComponentsModule and you will have access to everything, everywhere.

    This is an example of a SharedModule which I'm using:

    @NgModule({
      imports: [
        CommonModule,
        FormsModule,
        ReactiveFormsModule,
        RouterModule,
    
        // 3rd party
        DirectivesModule,
        LoadingBarHttpClientModule,
        PipesModule,
        TranslateModule
      ],
      exports: [
        CommonModule,
        FormsModule,
        ReactiveFormsModule,
        RouterModule,
    
        // 3rd party
        DirectivesModule,
        LoadingBarHttpClientModule,
        PipesModule,
        TranslateModule
      ]
    })
    
    export class SharedModule {}
    

    And a ComponentsModule:

    import {COMPONENTS} from '../components/components';
    import {COMPONENTS_PROVIDERS} from '../components/providers';
    
    @NgModule({
      imports: [
        COMPONENTS
      ],
      exports: [
        COMPONENTS
      ]
    })
    
    export class ComponentsModule {
    
      public static forRoot() {
    
        return {
          ngModule: ComponentsModule,
          providers: COMPONENTS_PROVIDERS
        };
      }
    }