I'm using ElementRef in the CommentComponent which is exported to others modules like ArticleModule, ProductModule etc...
// CommentModule
#NgModule({
exports: [ CommentComponent ],
declarations: [ CommentComponent ],
providers: [ CommentService]
})
export class CommentModule { }
// Comment Component (belongs to CommentModule)
#Component({
selector: 'comments',
templateUrl: 'comment.component.html',
styleUrls: ['comment.component.scss']
})
export class CommentComponent implements OnInit {
suggOnTyping: string = 'hidden';
constructor(private _eref: ElementRef){}
#HostListener('document:click', ['$event'])
onClickOutside(event: any) {
event.preventDefault();
event.stopPropagation();
if (!this._eref.nativeElement.contains(event.target))
this.suggOnTyping = 'hidden';
}
}
// Article Module
#NgModule({
imports: [
CommonModule,
RouterModule,
CommentModule,
ArticleRoutingModule],
declarations: [ ArticleComponent ],
providers: [ArticleService, CommentService, CommentComponent],
schemas: [ NO_ERRORS_SCHEMA ]
})
ArticleComponent calls the CommentComponent from view like this:
<div class="article">
.......
<comments></comments>
</div>
Now when I'm trying to route through ArticleComponent, I'm getting:
core.js:1673 ERROR Error: Uncaught (in promise): Error: StaticInjectorError(AppModule)[NgClass -> ElementRef]:
StaticInjectorError(Platform: core)[NgClass -> ElementRef]:
NullInjectorError: No provider for ElementRef!
Error: StaticInjectorError(AppModule)[NgClass -> ElementRef]:
StaticInjectorError(Platform: core)[NgClass -> ElementRef]:
NullInjectorError: No provider for ElementRef!
It seems ElementRef cannot pass through 'provider' because when I remove it from CommentComponent everything works fine.
What is the problem ?
I'm using Angular 6 by the way
Remove CommentComponent from providers list of AritcleModule. CommentComponent is already declared in CommentModule.
// Article Module
#NgModule({
declarations: [ ArticleComponent],
providers: [ArticleService, CommentService, CommentComponent ], //<-- remove from here
schemas: [ NO_ERRORS_SCHEMA ]
})
Remove ElementRef from constructor which is throwing this error and if you want to access the element then you can put the reference for element and use #ViewChild decorator in ts file.
Example :
html
<div #ele>hello/div> <!-- element reference goes here -->
ts
#ViewChild("ele") _eref: ElementRef;
Related
I would like use provider value defined in other module. Here is example:
app.module.ts
...
import { ThemeModule } from '../shared/modules/theme/theme.module';
...
#NgModule({
declarations: [
RootComponent,
LoginScreenComponent,
],
imports: [
BrowserModule.withServerTransition({ appId: 'serverApp' }),
BrowserAnimationsModule,
AppRoutingModule,
ConfigModule,
ThemeModule,
....
],
providers: [
...
{ provider: "THEME_NAME", useValue: "VALUE" },
],
bootstrap: [RootComponent]
})
export class MDMToolModule {}
theme.module.ts
import { NgModule } from '#angular/core';
import { ThemeService } from './services/ThemeService';
#NgModule({
imports: [],
declarations: [],
providers: [
{provide: "ThemeService", useFactory: (THEME_NAME) => {
return new ThemeService(THEME_NAME)
}},
],
exports: []
})
export class ThemeModule {}
Is there possibility to pass VALUE defied not in module like above example (THEME NAME)?
if you're providing value in root module then it will be available to all other modules too so you can then simply ask for that value using Inject decorator like this:
#Injectable()
export class ThemeService {
constructor(#Inject("THEME_NAME") theme: string) {}
}
otherwise you can import your MDMToolModule inside ThemeModule though judging by the code you provided I'm assuming this MDMToolModule is your root module,
you can also use Injection Token to avoid using those magic strings like this:
const ThemeName = new InjectionToken("token to inject theme name");
and then use it inside theme.module.ts
#NgModule({
providers: [
{ provide: ThemeName, useValue: "DarkKnight" },
ThemeService
]
})
export class ThemeModule {}
theme.service.ts
#Injectable()
export class ThemeService {
constructor(#Inject(ThemeName) theme: string) {}
}
I want to pass a variable from one component to another and I'm using #input
This is my parent component :
#Component({
selector: 'aze',
templateUrl: './aze.component.html',
styleUrls: [('./aze.component.scss')],
providers: [PaginationConfig],
})
export class azeComponent implements OnInit {
public hellovariable: number = 100;
}
This is the template of the parent component:
<app-my-dialog [hellovariable]="hellovariable"></app-my-dialog>
This is my child component :
#Component({
selector: 'app-my-dialog',
templateUrl: './my-dialog.component.html',
styleUrls: ['./my-dialog.component.css']
})
export class MyDialogComponent implements OnInit {
#Input() hellovariable: number;
constructor() { }
ngOnInit() {
console.log(hellovariable);
}
This is the child template:
<h3>Hello I am {{hellovariable}}<h3>
And this is the app.module.ts:
#NgModule({
declarations: [
AppComponent,
MyDialogComponent
],
entryComponents: [
MyDialogComponent
],
imports: [
BrowserModule,
routing,
NgbModule,
BrowserAnimationsModule,
ToastrModule.forRoot(),
RichTextEditorAllModule,
FullCalendarModule,
NgMultiSelectDropDownModule.forRoot(),
LeafletModule.forRoot(),
NgxGalleryModule,
HttpClientModule,
MatDialogModule,
ReactiveFormsModule
],
When I show my parent component template I get this error:
Can't bind to 'hellovariable' since it isn't a known property of 'app-my-dialog'.
Any idea on how to fix this?
Few thing to try
First make sure you have import Input into your component
import { Component, Input } from '#angular/core';
Then follow pascal convention when naming class
export class azeComponent implements OnInit
should change to
export class AzeComponent implements OnInit
Then register your component into your module declarations
Also import BrowserModule into your module also something like this
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
#NgModule({
declarations: [
AppComponent,
MyDialogComponent,
AzeComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Mostly you'll get this error because you forgot to declare the component into your app.module.ts or did it wrong
app.module.ts
import { YourSpecificComponent } from './components/measureUnit/measure-unit-monthly/measure-unit-monthly.component';
#NgModule({
declarations: [
AppComponent,
MessageComponent,
DashboardComponent,
.....,
YourSpecificComponent, // declared here
}
your-specific.component.ts
#Component({
selector: 'app-your-specific', // your selector
templateUrl: './your-specific.component.html',
styleUrls: [
'../some.css'
]
})
export class YourSpecificComponent implements AfterContentInit {
.....
ok this error causes because you need to import MyDialogComponent in azeComponent's module.
In the AppComponent, I'm using the nav component in the HTML code. The UI looks fine. No errors when doing ng serve. and no errors in console when I look at the app.
But when I ran Karma for my project, there is an error:
Failed: Template parse errors:
'app-nav' is not a known element:
1. If 'app-nav' is an Angular component, then verify that it is part of this module.
2. If 'app-nav' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '#NgModule.schemas' of this component to suppress this message.
In my app.module.ts:
there is:
import { NavComponent } from './nav/nav.component';
It is also in the declarations part of NgModule
#NgModule({
declarations: [
AppComponent,
CafeComponent,
ModalComponent,
NavComponent,
NewsFeedComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
JsonpModule,
ModalModule.forRoot(),
ModalModule,
NgbModule.forRoot(),
BootstrapModalModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
I'm using the NavComponent in my AppComponent
app.component.ts
import { Component, ViewContainerRef } from '#angular/core';
import { Overlay } from 'angular2-modal';
import { Modal } from 'angular2-modal/plugins/bootstrap';
import { NavComponent } from './nav/nav.component';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Angela';
}
app.component.html
<app-nav></app-nav>
<div class="container-fluid">
</div>
I have seen a similar question, but the answer in that question says we should add NgModule in the nav component that has a export in that, but I'm getting compile error when I do that.
There is also: app.component.spec.ts
import {NavComponent} from './nav/nav.component';
import { TestBed, async } from '#angular/core/testing';
import { AppComponent } from './app.component';
Because in unit tests you want to test the component mostly isolated from other parts of your application, Angular won't add your module's dependencies like components, services, etc. by default. So you need to do that manually in your tests. Basically, you have two options here:
A) Declare the original NavComponent in the test
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent,
NavComponent
]
}).compileComponents();
}));
B) Mock the NavComponent
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent,
MockNavComponent
]
}).compileComponents();
}));
// it(...) test cases
});
#Component({
selector: 'app-nav',
template: ''
})
class MockNavComponent {
}
You'll find more information in the official documentation.
You can also use NO_ERRORS_SCHEMA
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
}));
https://2018.ng-conf.org/mocking-dependencies-angular/
For me importing the component in the parent resolved the issue.
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent,
NavComponent
]
}).compileComponents();
}));
Add this in spec of the parent where this component is used.
One more reason is that there can be multiple .compileComponents() for beforeEach() in your test case
for e.g.
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [TestComponent]
}).compileComponents();
}));
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientModule],
declarations: [Test1Component],
providers: [HttpErrorHandlerService]
}).compileComponents();
});
Step 1: Create stubs at beginning of spec file.
#Component({selector: 'app-nav', template: ''})
class NavComponent{}
Step 2: Add stubs in component's declarations.
TestBed.configureTestingModule({
imports: [
RouterTestingModule
],
declarations: [
AppComponent,
NavComponent
],
}).compileComponents();
Another source for this error are the tests for the parent component, app-root, that include the tag of this component, app-nav.
Even though the message is about app-nav, the child component, the fix should be added in the tests of app-root, the parent component.
The fix can be a mock:
app.component.spec.ts:
#Component({selector: 'app-nav', template: ''})
class NavComponentStub {
}
What happens is that you create the root component, with it's tests, they pass. Latter on you add the child component in the root component tag and now you have to update the root component tests even if you just added a tag in the template.
Also the message doesn't say which test fails and from the message you might be led to believe that it's the child component's tests, when in fact they are the parent's tests.
If you create a stub and still get the same error it might be because of --watch mode on. Try to stop it and run again.
I'm writing an AWS security groups module clone with Angular and ngrx. I'm trying to pass a method into the dumb component for creating a new security group.
...
const schemas: any[] = [];
#NgModule({
imports: [
CommonModule
],
declarations: [
SecurityGroupsComponent,
SecurityGroupsTableComponent,
SecurityGroupsListComponent
],
schemas: schemas,
exports: [SecurityGroupsComponent]
})
export class SecurityGroupsModule { }
import { Component } from '#angular/core';
#Component({
selector: 'app-security-groups-table',
templateUrl: './security-groups-table.component.html',
})
export class SecurityGroupsTableComponent {
onCreateSecurityGroup() {
console.log('Create Security Group');
}
}
<app-security-groups-list
[createSecurityGroup]="onCreateSecurityGroup"></app-security-groups-list>
import { Component, Input } from '#angular/core';
#Component({
selector: 'app-security-groups-list',
templateUrl: './security-groups-list.component.html',
})
export class SecurityGroupsListComponent {
#Input()
createSecurityGroup: Function;
selectSecurityGroup(securityGroupId: string) {
this.securityGroupSelected.next(securityGroupId);
}
}
and when the template is rendered I get this exception:
Can't bind to 'createSecurityGroup' since it isn't a known property of 'app-security-groups-list'.
What am I doing wrong?
It seems you have missed to add SecurityGroupsListComponent to NgModule.
Please make sure you do below step before use it:
#NgModule({
imports: [...],
declarations: [SecurityGroupsListComponent]
})
export const DECLARATIONS = [SecurityGroupsComponent,
SecurityGroupsTableComponent,
SecurityGroupsListComponent]
and then
declarations: DECLARATIONS,
schemas: schemas,
exports: DECLARATIONS
I currently have a module setup like below (exerpt);
AppModule
RoutingModule
AuthRouteGuard
AuthModule
LoginFormComponent
AuthService
I have defined my AuthService (responsible for handling user authentication and provides a method for determining whether the current user is authenticated) as a provider in my AuthModule;
// auth.module.ts - uses https://github.com/auth0/angular2-jwt
export function authHttpServiceFactory(http: Http, options: RequestOptions) {
return new AuthHttp(new AuthConfig({
tokenName: jwtLocalStorageKey
}), http, options);
}
export let authHttpServiceProvider = {
provide: AuthHttp,
useFactory: authHttpServiceFactory,
deps: [Http, RequestOptions]
};
#NgModule({
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule
],
exports: [AuthComponent],
declarations: [AuthComponent, LoginComponent, RegisterComponent],
providers: [
AuthService,
authHttpServiceProvider
]
})
export class AuthModule { }
I can use this service with no problem within its sibling LoginFormComponent. When I attempt to use the AuthService within the AuthRouteGuard class in the RoutingModule however I get the following error;
Error: Invalid provider for the NgModule 'AuthModule' - only instances of Provider and Type are allowed, got: [?undefined?, ...]
I have the AuthModule imported within the RoutingModule. The error above occurs as soon as the AuthService is defined as a dependency for the AuthRouteGuard;
export class AuthRouteGuard implements CanActivate {
constructor(
private router: Router,
private authService: AuthService // Removing this injection removes the error
) {}
canActivate() {
// #todo: if not authenticated
this.router.navigate(['/login']);
return true;
}
}
What am I missing here, and why would injecting the service in the constructor cause an invalid provider error that does not occur when that injection is removed?
Edit - Same error occurs if the authHttpServiceProvider provider is removed altogether, so the AuthModule module looks like;
#NgModule({
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule
],
exports: [AuthComponent],
declarations: [AuthComponent, LoginComponent, RegisterComponent],
providers: [
AuthService
]
})
export class AuthModule { }
Add authHttpServiceProvider to imports of the module. It's exported to global and not available to module. So you can't provide the service because you have unknown provider to the module.
#NgModule({
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule
],
exports: [AuthComponent],
declarations: [AuthComponent, LoginComponent, RegisterComponent],
providers: [
AuthService
]
})
export class AuthModule {
The actual problem was within the AuthService itself.
AuthModule defined a constant;
export const jwtKey = 'jwt';
Which was being imported into the AuthService and used;
import { jwtKey } from '../auth.module';
For some reason if I remove this import everything works fine.