I have created a custom pipe that uses the DecimalPipe transform() method. I am using this pipe inside one of feature modules and I have to add both of those pipes to providers: [] (because MyCustomPipe uses DecimalPipe), like so:
index.ts:
#NgModule({
imports: [
MaterialModule,
SharedModule
],
declarations: [
...
],
providers: [
DecimalPipe,
MyCustomPipe
...
My goal however is to not have to add DecimalPipe to a feature module in this way and have that dependence between MyCustomPipe and DecimalPipe 'hidden', so that who ever is consuming MyCustomPipe can just worry about importing MyCustomPipe from SharedModule. I tried to resolve this by trying to follow the SharedModule pattern and have the DecimalPipe exported from SharedModule (as I did with MyCustomPipe), like so:
shared.module.ts:
...import { DecimalPipe } from '#angular/common';
...export * from '../pipes/index';
#NgModule({
imports: [
CommonModule,
FormsModule,
HttpModule,
DecimalPipe
],
declarations: [
LoginComponent,
ErrorComponent,
MyCustomPipe,
],
exports: [
CommonModule,
HttpModule,
LoginComponent,
ErrorComponent,
DecimalPipe,
MyCustomPipe
]
})
However, when I try to do this I get the error "Error: (SystemJS) Unexpected pipe 'DecimalPipe' imported by the module 'SharedModule'. Please add a #NgModule annotation." . Now, I could add DecimalPipe to declarations: [] in SharedModule, but then I get the error warning me that DecimalPipe is declared both in SharedModule and CommonModule. I think this stems from my lack of understanding of the SharedModule pattern described in the docs. I am not 100% if this even is the right approach, as I have never tried to share a custom pipe that uses a build-in Angular pipe with feature modules.
You don't have to worry about importing/declaring the inbuilt DecimalPipe along with your customPipe that uses it when you use/reuse it elsewhere in your app. Just declare the customPipe only. In your custom pipe's definition, just import the DecimalPipe like
import { DecimalPipe } from '#angular/common';
Then in the feature module that uses it just define them as part of the declarations array in #NgModule. If importing this feature module elsewhere in other feature module is supposed to identify this particular pipe being used in that new feature module, then mention this customPipe also part of the exports array declaration of the earlier feature module (that is being reused).
#NgModule({
imports: [
CommonModule,
...
],
declarations: [
CustomPipe
],
exports: [
CustomPipe // <-- do this only if you need this pipe to be available external to this ReusedFeatureModule when this module is 'imported' to other feature modules
]
})
export class ReusedFeatureModule { }
CustomPipe
import { Pipe, PipeTransform } from '#angular/core';
import { DecimalPipe } from '#angular/common';
#Pipe({
name: 'customPipe'
})
export class CustomPipe implements PipeTransform {
...
private originalDecimalPipe: DecimalPipe; // this variable will have the inbuilt DecimalPipe inside the constructor body
constructor () {
this.originalDecimalPipe = new DecimalPipe('en-US'); // pass the current locale as the argument
}
transform(value: any): any { return <transformed-value>;} // implement your transform
}
You should add the pipe under declarations not under import
#NgModule({
imports: [
CommonModule,
FormsModule,
HttpModule
],
declarations: [
LoginComponent,
ErrorComponent,
MyCustomPipe,
DecimalPipe
],
exports: [
CommonModule,
HttpModule,
LoginComponent,
ErrorComponent,
DecimalPipe,
MyCustomPipe
]
})
Updated
Just for safe note: add pipe-class in declarations and pipe-module in imports
#NgModule({
declarations: [
CustomDatePipe
],
imports: [
CustomDateModule
]
})
export class AppModule { }
Related
I have a structure like this:
home - page
home2 - page
test-component - component
I need to be able to use the component(<app-test-component></app-test-component>) on both pages.
If I just use the tag:
ERROR Error: Uncaught (in promise): Error: Template parse errors:
'app-test-component' is not a known element:
If I just import (import {TestComponentComponent} from '../test-component/test-component.component') into home.module.ts and put it in the module import:
core.js:9110 ERROR Error: Uncaught (in promise): Error: Unexpected
directive 'TestComponentComponent' imported by the module
'HomePageModule'. Please add a #NgModule annotation.
A similar error if added to declarations.
Git.
How to import a component on both pages and use?
There are few changes that you should make:
You should not import component as I can see in the code.
Rearrange the project structure, and move reusable component such as TestComponent into a shared component. To give you an example, I'll give below example where I have created shared module names as TestModule
/app
/home
-home.component.ts
-home.module.ts
/test
-test.component.ts
-test.module.ts
-app.component.ts
-app.module.ts
app.module.ts
#NgModule({
declarations: [AppComponent],
imports: [BrowserModule, HomeModule, RouterModule.forRoot([{ path: '', component: HomeComponent }])],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
test.module.ts
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { TestComponent } from './test.component';
#NgModule({
declarations: [TestComponent],
exports: [TestComponent],
imports: [CommonModule],
})
export class TestModule {}
home.module.ts
#NgModule({
declarations: [HomeComponent],
exports: [HomeComponent],
imports: [
CommonModule,
TestModule
]
})
export class HomeModule { }
This set of project structure would certainly help you to reuse test.module features. Let me know if you need more code.
Declare TestComponentComponent only in home.module.
home.module.ts
#NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
RouterModule.forChild([
{
path: '',
component: HomePage
}
])
],
declarations: [HomePage, TestComponentComponent]
})
export class HomePageModule {}
app.module.ts
#NgModule({
declarations: [AppComponent],
entryComponents: [],
imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule],
providers: [
StatusBar,
SplashScreen,
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
],
bootstrap: [AppComponent]
})
export class AppModule {}
I have been trying to create my own datatable, but facing issues.
So my app structure is like so:
where hkdt in my custom datatable module, I want to use this in my app module so that it is accessible everywhere.
hkdt.component.ts is:
import { Component } from '#angular/core';
#Component({
selector: 'hkdt',
exportAs: "hkdt",
templateUrl: './hkdt.html',
styleUrls: ['./hkdt.css']
})
export class HkdtComponent {
}
hkdt.module.ts is:
import { CommonModule } from "#angular/common";
import { NgModule } from "#angular/core";
import {HkdtComponent} from "./hkdt.component";
#NgModule({
imports: [CommonModule],
declarations: [HkdtComponent],
exports: [HkdtComponent]
})
export class HkdtModule {
}
index.ts is:
export * from './hkdt.component';
export * from './hkdt.module';
and I want to use it in my app.module.ts like so:
In my imports array of app.module.ts I import HkdtModule from import {HkdtModule} from "./core/hkdt";
and using it in some module like so:
<hkdt></hkdt>
below is my app.module.ts:
import {HkdtModule} from "./core/hkdt";
#NgModule({
declarations: [
AppComponent,
SignInComponent,
HomeComponent
],
imports: [
BrowserModule,
HttpClientModule,
FormsModule,
AppRoutingModule,
BrowserAnimationsModule,
HkdtModule
],
providers: [
],
bootstrap: [AppComponent]
})
export class AppModule { }
but I get a console error:
Can anybody help me resolve me this problem.
Note: I do not want to import the component in the module and use it because I want to later publish my hkdt as a plugin and I want to make it work independently in a pluggable manner.
I have a lazy load module which needs to expose providers, so I am using the forRoot convention and returning the following code:
#NgModule({
imports: [RouterModule.forChild([
{path: "", component: LazyComponent},
])],
declarations: [LazyComponent],
})
export class LazyModule {
static forRoot() {
return {
ngModule: LazyModule,
providers: [provider]
};
}
}
The problem is when I invoke the forRoot in my app module the lazy load does not work anymore. ( I don't see the separate chunk in my console )
#NgModule({
declarations: [
AppComponent,
HelloComponent
],
imports: [
BrowserModule,
AppRoutingModule,
LazyModule.forRoot() <======== this stops the lazy load module
],
bootstrap: [AppComponent]
})
export class AppModule {
}
From what I learned it should only make the providers singleton, why it does not work?
As of right now, it's not possible to execute a forRoot (or any other configuration static method of this sort) in a module that will load lazily. The problem here is that such a method returns a ModuleWithProviders while loadChildren requires a NgModuleFactory.
I faced a similar issue where I had to lazy load a module with some configurations. So, I came up with this temporary fix.
Lazy Module
#NgModule({
imports: [
RouterModule.forChild([
{path: "", component: LazyComponent},
])],
declarations: [LazyComponent],
})
export class LazyModule {
// considering the function only handle providers
static setDynamicProviders(provider?: any) {
if(LazyModule && LazyModule.__annotations__ && LazyModule.__annotations__[0]){
LazyModule.__annotations__[0].providers.push(provider);
}
return LazyModule;
}
}
Parent Module
const someService = {
provide: 'SOME_TOKEN',
useFactory: (dependecy1) => (//dome something with dependency1)
deps: [SOME_DEPENDENCY_TOKEN]
}
#NgModule({
imports: [
RouterModule.forRoot([
path: 'lazy',
loadChildren: ()=>(import('*your path to lazy module*').then(ref => ref.LazyModule.setDynamicProvider(someService)))
])
]
})
So, I am just modifying the annotations created by #NgModule Decorator. At this stage, Angular does not support lazy loading with static methods that return ModuleWithProviders.
When you import a LazyModule in your AppModule imports array it is not "lazy" anymore. A lazy module should only be referenced in a dedicated RoutingModule.
So if I understood you correctly you would like to share a Service between your LazyModules?
If so remove LazyModule from AppModule and create a SharedModule and move your Service you like to share inside the providers array in SharedModule. Import SharedModule in your AppModule with forRoot and import your SharedModule without forRoot in your LazyModules
Try below code when we define a module is Lazy RouterModule.forChild:
#NgModule({
imports: [RouterModule.forChild([
{path: "", component: LazyComponent},
])],
declarations: [LazyComponent],
})
export class LazyModule { }
But when we load in the parent module, try the below code:
#NgModule({
declarations: [
AppComponent,
HelloComponent
],
imports: [
BrowserModule,
AppRoutingModule,
RouterModule.forRoot([
{
path: '',
loadChildren: './layout/layout.module#LayoutModule',
canActivate: [AuthGuard]
}
]) <======== right way to load lazy module
],
bootstrap: [AppComponent]
})
export class AppModule {
}
Note: FullPath is like './moduleFolder/lazy.module#LayoutModule'
LayoutModule is your exported Module Name
Please let me know
I have my main module like this, where I import the basic Libraries :
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { MaterialModule } from '#angular/material';
import { FormsModule } from '#angular/forms';
import { HttpModule } from '#angular/http';
import { MapModule } from './map/map.module';
import { AppComponent } from './app.component';
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
MapModule,
MaterialModule.forRoot()
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
My question is when I create a new Module within the app i.e Map Module do I need to reimport all those libraries to that Module. I was under the impression that if I import the libraries on the module it would work under child modules.
But in my Map Module I am getting errors like.
Can't bind to 'ngClass' since it isn't a known property of 'div'.
My Current MapModule looks like
import { NgModule } from '#angular/core';
import { MapComponent } from './map.component';
import { MapMenuComponent } from './map-menu/map-menu.component';
import { MapControlsComponent } from './map-controls/map-controls.component';
import { MapService } from './map.service';
#NgModule({
imports: [],
exports: [],
declarations: [MapMenuComponent, MapControlsComponent, MapComponent],
providers: [MapService],
})
export class MapModule { }
Do I need to reimport the MaterialModule, Forms etc into module again for the components in this module to work ?
You only need to reimport modules with declarations, i.e. modules that define new components, directives and pipes. Modules that register providers don't need to be imported.
In this list:
imports: [
BrowserModule,
FormsModule,
HttpModule,
MapModule,
MaterialModule.forRoot()
],
FormsModule modules need to be imported, by HttpModule need not. BrowserModule re-exports CommonModule, so in the other modules you would probably want to import CommonModule, not BrowserModule to get built-in directives like NgClass. By importing CommonModule you will not have this error:
Can't bind to 'ngClass' since it isn't a known property of 'div'.
You can use SharedModule. All modules those are used in multiple modules
sharedModule example
import { NgModule } from '#angular/core';
import { anyService} from './any.service';
#NgModule({
providers: [anyService]
})
export class SharedModule {}
Now you can import this shared module in any modules in which want to use this module
I have a case, where I have two modules, who have there own Pipe-definitions. One Module is a customer module, the other a product module. In products I want to use customer pipes and vice versa.
If modules in Angular 2 import each other like the following:
Example Root Module:
#NgModule({
imports: [
BrowserModule, AModule, BModule
],
declarations: [
AppComponent
],
bootstrap: [ AppComponent ]
})
export class AppModule { }
Example Module B:
#NgModule({
imports: [BModule],
declarations: [
],
exports: []
})
export class AModule { }
Example Module A:
#NgModule({
imports: [AModule],
declarations: [
],
exports: []
})
export class BModule { }
The browser throws an error:
Uncaught Error: Unexpected value 'undefined' imported by the module
'BModule'
I could create a SharedModule, that declares both Pipes and import that module into A and BModule. But this would not be SOLID anymore, because customer pipes and product pipes just belong into there own module and not in another shared module.
Any proposals?
I googled, but did not find anything :(