I have weird issue when I implement ng-content between two component in Angular 5.
This my first component is FooterComponent
<div class="footer">
<ng-content select="footer"></ng-content>
</div>
And footer.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-footer',
templateUrl: './footer.component.html',
styleUrls: ['./footer.component.css']
})
export class FooterComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
And this the second component is AboutComponent
<app-footer>
<footer>
this is my footer
</footer>
</app-footer>
Also this app-module.ts in first try and in second try I import about.module.ts or footer.module.ts instead of AboutComponent and FooterComponent.
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { AppComponent } from './app.component';
import { AboutComponent } from './about/about.component';
import { FooterComponent } from './footer/footer.component';
@NgModule({
declarations: [
AppComponent,
AboutComponent,
FooterComponent
],
imports: [
BrowserModule,
],
providers: [],
bootstrap: [AppComponent],
schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
})
export class AppModule { }
And app.component.html have just router-outlet tag
<router-outlet></router-outlet>
So I have two issues first one is when I run Angular using ng serve command line I have not see any output. The second and important one is when I run ng test command line I see this Bug...
AboutComponent should create
Failed: Template parse errors:
'app-footer' is not a known element:
1. If 'app-footer' is an Angular component, then verify that it is part of
this module.
2. If 'app-footer' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to
the '@NgModule.schemas' of this component to suppress this message. ("
[ERROR ->]<app-footer>
<footer>
this is my footer
"): ng:///DynamicTestModule/AboutComponent.html@0:0
So I have been try many solution like
Generate about.module.ts and import CUSTOM_ELEMENTS_SCHEMA and try NO_ERRORS_SCHEMA. And also import FooterComponent and finally import about.module.ts in app.module.ts
I do the same thing with FooterComponent.
I have been try using CUSTOM_ELEMENTS_SCHEMA and NO_ERRORS_SCHEMA in every module and it's a give me the same result.
And this package.json
{
"name": "ng-app",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build --prod",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "^5.2.0",
"@angular/common": "^5.2.0",
"@angular/compiler": "^5.2.0",
"@angular/core": "^5.2.0",
"@angular/forms": "^5.2.0",
"@angular/http": "^5.2.0",
"@angular/platform-browser": "^5.2.0",
"@angular/platform-browser-dynamic": "^5.2.0",
"@angular/router": "^5.2.0",
"core-js": "^2.4.1",
"rxjs": "^5.5.6",
"zone.js": "^0.8.19"
},
"devDependencies": {
"@angular/cli": "~1.7.3",
"@angular/compiler-cli": "^5.2.0",
"@angular/language-service": "^5.2.0",
"@types/jasmine": "~2.8.3",
"@types/jasminewd2": "~2.0.2",
"@types/node": "~6.0.60",
"codelyzer": "^4.0.1",
"jasmine-core": "~2.8.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~2.0.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "^1.2.1",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.1.2",
"ts-node": "~4.1.0",
"tslint": "~5.9.1",
"typescript": "~2.5.3"
}
}
Finally this new Angular-cli Project I created.
Thanks.
First thing you need to do is include the RouterModule
in the app.module.ts. You are trying to use the router outlet (<router-outlet></router-outlet>
) without that. That will not work.
Updated app.module.ts:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { FooterModule } from './footer/footer.module';
import { FooterComponent } from './footer/footer.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FooterModule,
RouterModule.forRoot([
{path: '', component: FooterComponent}
])
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
See the new import for the RouterModule
. Also, after adding RouterModule
to the imports array, I also added a default route that goes to the FooterComponent
. I needed something to route to, that is what I chose. Change this to what ever you like as your application progresses.
Mind you, even with these changes, you will see nothing in the browser. You will need to add some content to the footer in order to see something. I will leave that part up to you. If you right-click and Inspect the main page while running ng serve
, you will see that the footer elements do get rendered (they just don't have any valuable content, yet).
As far as testing, you need to add the FooterComponent
to the declarations section in the spec for the AboutComponent
, along with the import. Updated spec below.
import { FooterComponent } from './../footer/footer.component';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AboutComponent } from './about.component';
describe('AboutComponent', () => {
let component: AboutComponent;
let fixture: ComponentFixture<AboutComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ AboutComponent, FooterComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AboutComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
Since the AboutComponent
is hosting the FooterComponent
, the unit test needs to know about all the components in the mix. This takes care of that.
Then, in order to clear the eventual error you will get related to testing the AppComponent
, you need to mock a router outlet for use in the spec. That code is below.
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { Component } from '@angular/core';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent, RouterOutletStubComponent
],
}).compileComponents();
}));
it('should create the app', async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
it(`should have as title 'app'`, async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('app');
}));
});
@Component({selector: 'router-outlet', template: ''})
class RouterOutletStubComponent { }
Since you removed all the original HTML for the AppComponent
to use it as a router outlet, I had to remove one of the unit tests, since it was no longer needed.
The RouterOutletStubComponent
is the interesting thing here. This allows the AppComponent
unit tests to run successfully. All the AppComponent
is at this point is a router outlet container.
Happy coding!