Angular Frontend only displaying app.component.html - javascript

I'm currently making a frontend to my API and now it's time to make another component.
I've tried everything, even created a app-routing.module.ts, but it only displays my app.component.html !!
I don't get why ??
My app-routing.module.ts :
import { AppComponent } from './app.component';
import { clientprodComponent } from './clientproduct/clientprod.component';
import { NgModule } from '#angular/core';
import { RouterModule, Routes } from '#angular/router';
const appTtitle = document.title;
const routes: Routes = [
{path:"", component: AppComponent},
{path:"clientprod", component: clientprodComponent},
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
export const routingComponents = [AppComponent, clientprodComponent]
My clientprod.component.ts:
import { CloudProductService } from './cloudproduct.service';
import { CloudProduct } from './cloudproduct';
import { Component, OnInit } from '#angular/core';
import { HttpErrorResponse } from '#angular/common/http';
import { NgForm } from '#angular/forms';
import * as $ from 'jquery';
#Component({
selector: 'app-client',
templateUrl: './clientprod.component.html',
styleUrls: ['./clientprod.component.css']
})
export class clientprodComponent implements OnInit {
public cloudProducts: CloudProduct[] | null;
public editProduct: CloudProduct | null;
public deleteProduct: CloudProduct | null;
constructor(private cloudProductService: CloudProductService){}
ngOnInit(){
this.getCloudProducts();
}
public getCloudProducts():void{
this.cloudProductService.getCloudProducts().subscribe(
(response: CloudProduct[]) =>{
this.cloudProducts = response;
},
(error: HttpErrorResponse) => {
alert(error.message);
}
);
}
public onOpenModal(cloudProduct: CloudProduct | null, mode: string): void {
const container = document.getElementById('main-container');
const button = document.createElement('button');
button.type = 'button';
button.style.display = 'none';
button.setAttribute('data-toggle', 'modal');
if (mode === 'add') {
button.setAttribute('data-target', '#addProductModal');
}
if (mode === 'edit') {
this.editProduct = cloudProduct;
button.setAttribute('data-target', '#updateProductModal');
}
if (mode === 'delete') {
this.deleteProduct = cloudProduct;
button.setAttribute('data-target', '#deleteProductModal');
}
container!.appendChild(button);
button.click();
}
}
My clientprod.component.spec.ts :
import { ComponentFixture, TestBed } from '#angular/core/testing';
import { clientprodComponent } from './clientprod.component';
describe('clientprodComponent', () => {
let component: clientprodComponent;
let fixture: ComponentFixture<clientprodComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ clientprodComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(clientprodComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
I don't get it, I have it imported aswell in my app.module.ts...
Why is it just displaying app.component.html, no matter the url ?

Do you have this line bootstrap: [AppComponent], in your app.module ?
This means AppComponent it's the entry point of the application, everything needs to go inside this one.
if you want to load diferent components base on route, you need to:
add <routet-outlet></router-outlet> to your app.component.html
add routes as you did
const routes: Routes = [
{path:"clientprod", component: clientprodComponent},
];
without routing to Appcomponent because all this routing goes inside it.
make sure to import AppRoutingModule in AppModule
Hope it helps :)

Related

Cannot read property 'getAboutInfo' of undefined at <Jasmine>

This is my code in angular, the functionality is working all fine but the test cases are getting failed. Please tell me what i am doing wrong in the code?
The error I am getting
HeadlessChrome 83.0.4103 (Windows 10.0.0) AboutComponent should create FAILED
TypeError: Cannot read property 'getAboutInfo' of undefined
at **
at AboutComponent.ngOnInit (http://localhost:9876/karma_webpack/src/app/about/about.component.ts:44:28)
at callHook (http://localhost:9876/karma_webpack/node_modules/#angular/core/ivy_ngcc/fesm2015/core.js:3937:1)
at callHooks (http://localhost:9876/karma_webpack/node_modules/#angular/core/ivy_ngcc/fesm2015/core.js:3901:1)
at executeInitAndCheckHooks (http://localhost:9876/karma_webpack/node_modules/#angular/core/ivy_ngcc/fesm2015/core.js:3842:1)
at refreshView (http://localhost:9876/karma_webpack/node_modules/#angular/core/ivy_ngcc/fesm2015/core.js:11795:1)
at renderComponentOrTemplate (http://localhost:9876/karma_webpack/node_modules/#angular/core/ivy_ngcc/fesm2015/core.js:11903:1)
at tickRootContext (http://localhost:9876/karma_webpack/node_modules/#angular/core/ivy_ngcc/fesm2015/core.js:13379:1)
at detectChangesInRootView (http://localhost:9876/karma_webpack/node_modules/#angular/core/ivy_ngcc/fesm2015/core.js:13413:1)
at RootViewRef.detectChanges (http://localhost:9876/karma_webpack/node_modules/#angular/core/ivy_ngcc/fesm2015/core.js:15093:22)
at ComponentFixture._tick (http://localhost:9876/karma_webpack/node_modules/#angular/core/ivy_ngcc/fesm2015/testing.js:323:1)
import { async, ComponentFixture, TestBed} from '#angular/core/testing';
import { AboutComponent } from './about.component';
import { AboutService } from './about.service';
import { HttpClientTestingModule, HttpTestingController } from '#angular/common/http/testing';
import { Observable, of } from 'rxjs';
import { I18nService } from 'src/utils/i18n.service';
import { MatDialogRef, MAT_DIALOG_DATA } from '#angular/material/dialog';
import { AppModule } from './../app.module';
describe('AboutComponent', () => {
let component: AboutComponent;
let fixture: ComponentFixture<AboutComponent>;
let dialogSpy: jasmine.Spy;
let app: any;
const mockDialogRef = {
close: jasmine.createSpy('close')
};
let service: any;
const data = '20/04/2019';
let getAboutInfoSpy: any;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [AboutComponent],
imports: [HttpClientTestingModule , AppModule],
providers: [{ provide: AboutService, useValue: service },
I18nService,
{ provide: MAT_DIALOG_DATA, useValue: {} },
{ provide: MatDialogRef, useValue: mockDialogRef}]
}).compileComponents();
}));
beforeEach(async () => {
fixture = TestBed.createComponent(AboutComponent);
component = fixture.componentInstance;
await fixture.whenStable();
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('infoList should be empty array', () => {
expect(app['dataList'].length).toBe(0);
});
it('when OnInit invoked through service data will return to infoList ', async(() => {
service = fixture.debugElement.injector.get(AboutService);
spyOn(service, 'getAboutInfo').and.returnValue(of(data));
app.ngOnInit();
expect(app['dataList'].length).toBe(3);
}));
it('onCancel should close the dialog', async( () => {
component.closePopup();
expect(mockDialogRef.close).toHaveBeenCalled();
}));
});
import { Component, OnInit, Inject } from '#angular/core';
import { AboutService } from './about.service';
import { Subscription } from 'rxjs';
import { MatDialogRef} from '#angular/material/dialog';
import { I18nService } from 'src/utils/i18n.service';
#Component({
selector: 'app-about',
templateUrl: './about.component.html',
styleUrls: ['./about.component.scss']
})
export class AboutComponent implements OnInit {
private aboutServiceSubscription: Subscription;
dataList: any;
locales: any = {};
translator: any;
constructor(
private dialogRef: MatDialogRef<AboutComponent>,
public aboutService: AboutService,
private i18Service: I18nService) {}
ngOnInit() {
this.translator = this.i18Service.getTranslator();
this.translator.translateObject.subscribe((item: any) => {
this.locales = item;
});
this.aboutServiceSubscription = this.aboutService.getAboutInfo().subscribe((data: any) => {
if (data) {
data = data.split('/');
this.dataList = data;
}
});
}
/**
* Closes the poup
* #memberof AboutComponent
*/
closePopup() {
this.dialogRef.close();
}
}
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class AboutService {
constructor(private http: HttpClient) {
}
getAboutInfo() {
return this.http.get('/assets/aboutInfo.txt', {responseType: 'text'})
}
}
The error message indicates that you did not mock AboutService correctly, you passed an undefined to useValue, so the AboutService obtained through the Injector is undefined. You can't use spyOn to a undefined value.
Here is a working example, with irrelevant code removed:
about.component.ts:
import { Component, OnInit } from '#angular/core';
import { AboutService } from './about.service';
import { Subscription } from 'rxjs';
#Component({
selector: 'app-about',
})
export class AboutComponent implements OnInit {
private aboutServiceSubscription: Subscription;
dataList: any;
locales: any = {};
translator: any;
constructor(public aboutService: AboutService) {}
ngOnInit() {
this.aboutServiceSubscription = this.aboutService
.getAboutInfo()
.subscribe((data: any) => {
if (data) {
data = data.split('/');
this.dataList = data;
}
});
}
}
about.service.ts:
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root',
})
export class AboutService {
constructor(private http: HttpClient) {}
getAboutInfo() {
return this.http.get('/assets/aboutInfo.txt', { responseType: 'text' });
}
}
about.component.spec.ts:
import { HttpClientTestingModule } from '#angular/common/http/testing';
import { ComponentFixture, TestBed } from '#angular/core/testing';
import { of } from 'rxjs';
import { AboutComponent } from './about.component';
import { AboutService } from './about.service';
fdescribe('62700708', () => {
let component: AboutComponent;
let fixture: ComponentFixture<AboutComponent>;
let aboutServiceSpy: jasmine.SpyObj<AboutService>;
const data = '20/04/2019';
beforeEach(() => {
aboutServiceSpy = jasmine.createSpyObj('AboutService', ['getAboutInfo']);
aboutServiceSpy.getAboutInfo.and.returnValue(of(data));
TestBed.configureTestingModule({
declarations: [AboutComponent],
imports: [HttpClientTestingModule],
providers: [{ provide: AboutService, useValue: aboutServiceSpy }],
}).compileComponents();
});
beforeEach(async () => {
fixture = TestBed.createComponent(AboutComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('when OnInit invoked through service data will return to infoList ', () => {
expect(aboutServiceSpy.getAboutInfo).toHaveBeenCalledTimes(1);
expect(component.dataList).toEqual(['20', '04', '2019']);
});
});
unit test result:

How to load data service done first when app starting in Angular 8

This is my Angular App. My app will get data from API (temporarily in JSON file) and show in many another sibling component. So I decide to create a category.service.ts that I get and store data in. I using APP_INITIALIZER to run this service first when my app started. But there is a problem that: This service is running first, AppComponent runs before service get data done. So my view have empty of data.
If I click button routing to this component, everything run perfect. But when I go to this component by url path or F5(refresh page), nothing is shown
category.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class CategoryService {
DATA_CATEGORIES = 'assets/categories.json';
private _categories = [];
constructor(private http: HttpClient) {
}
get categories() {
return this._categories;
}
Init(): Promise<any> {
return new Promise<void>(resolve => {
this.http.get(this.DATA_CATEGORIES).subscribe(data => {
this._categories = Array.from(Object.keys(data), k => data[k]);
console.log("load data...");
});
resolve();
});
}
}
app.module.ts
export function initializeCategoryService(catService: CategoryService) {
return (): Promise<any> => {
return catService.Init();
}
}
#NgModule({
declarations: [
AppComponent,
HomeComponent,
StoriesFilterPipe,
ViewStoryComponent,
ViewCatComponent,
FrontEndComponent,
SearchComponent,
BackEndComponent,
CrudStoryFormComponent,
CrudStoryComponent,
JwPaginationComponent,
CrudCatComponent,
CrudCatFormComponent,
CrudCatSearchResultComponent,
CatListComponent
],
imports: [
BrowserModule,
FormsModule,
AppRoutingModule,
HttpClientModule,
],
providers: [
StoryService,
CategoryService,
{
provide: APP_INITIALIZER, useFactory: initializeCategoryService, deps: [CategoryService], multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
What I will suggest is to use Observable
like in your category service
import { Observable, Subject } from 'rxjs';
export class CategoryService {
private loadDataSub = new Subject<any>();
loadDataObservable$ = this.loadDataSub.asObservable();
emitLoadDataSuccess() {
this.loadDataSub.next();
}
Init(): Promise<any> {
return new Promise<void>(resolve => {
this.http.get(this.DATA_CATEGORIES).subscribe(data => {
this._categories = Array.from(Object.keys(data), k => data[k]);
console.log("load data...");
this.emitLoadDataSuccess(); // here we are emitting event
});
resolve();
});
}
}
And In your component
export class AppComponent implements OnInit {
constructor(private categoryService: CategoryService) {
this.categoryService.loadDataObservable$.subscribe(() => {
// here you can get data, this will only trigger when data is loaded from API
});
}
}
This is common case - i.e. you shows page while data is not avaliable yet - at slow and bad connections for instance, and it can do even more - connection was broken and data was nto recieved.
So, your page should be able to show not only data recieved, but also two another states: loading and error.
(So the advise is "add loader").
// data.service.ts
import { Injectable } from "#angular/core";
import { HttpClient, HttpClientModule } from "#angular/common/http";
#Injectable()
export class DataService {
private _categories = [];
constructor(private http: HttpClient) {}
get categories() {
return this._categories;
}
getData(): Promise<any[]> {
return new Promise<any[]>(resolve => {
this.http.get('https://api.myjson.com/bins/18qku4').subscribe(data => {
this._categories = Array.from(Object.keys(data), k => data[k]);
console.log("load data...");
resolve(this._categories);
});
});
}
}
// app.module.ts
import { NgModule, APP_INITIALIZER } from "#angular/core";
import { BrowserModule } from "#angular/platform-browser";
import { FormsModule } from "#angular/forms";
import { RouterModule } from "#angular/router";
import { ListDataComponent } from "./list-data/list-data.component";
import { AppComponent } from "./app.component";
import { DataService } from "./data.service";
import { HttpClientModule } from "#angular/common/http";
import {DetailComponent} from './detail/detail.component'
#NgModule({
imports: [
BrowserModule,
FormsModule,
HttpClientModule,
RouterModule.forRoot([
{ path: "", component: ListDataComponent },
{ path: "detail", component: DetailComponent }
])
],
declarations: [AppComponent, ListDataComponent,DetailComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
//list-data.component.ts
import { Component, OnInit } from "#angular/core";
import { DataService } from "../data.service";
#Component({
selector: "app-list-data",
templateUrl: "./list-data.component.html",
styleUrls: ["./list-data.component.css"],
providers: [DataService],
})
export class ListDataComponent implements OnInit {
categories = [];
constructor(service: DataService) {
service.getData().then(data => {
debugger;
this.categories = data;
});
}
ngOnInit() {}
}
There are alternatives to resolve this issue:
One is you can use a loader which you can display until the service call finishes.
Second is you can use *ngIf="categories?.length" which will keep your component hides until your service call finishes.
I hope it will resolve your issue.

how to test a function with condition in ngOnInit

I want to test a function with condition that comes from a service in ngOnInit. I tried many way but no success. i have all kinds of mistakes.
my component
export class MainSectionComponent implements OnInit {
propertiesFrDb: PropertyPost[];
constructor(
private getPropertiesFrDbService: GetPropertiesFrDbService,
private propertyWarehouseService: PropertyWarehouseService,
private router: Router,
config: NgbCarouselConfig,
private userService: UserService,
private sharedFunctionService: SharedFunctionService,
private returnResponseAfterUserLoginService: ReturnResponseAfterUserLoginService,
private localStorageService: LocalStorageServiceService,
private dialogLoginService: DialogLoginService,
#Inject(PLATFORM_ID) private platformId: Object
) {
// this.isBrowser = isPlatformBrowser(platformIds);
}
ngOnInit() {
this.getPropertiesFrDb();
}
getPropertiesFrDb() {
if (this.propertyWarehouseService.currentValuesProperties) {
this.propertyWarehouseService.getPropertyHome$.subscribe(
prop => {
console.log(prop);
return this.propertiesFrDb = prop
}
)
} else {
this.getPropertiesFrDbService.getHomeProperties()
.subscribe(property => {
// console.log(property['results']);
this.propertyWarehouseService.setPropertiesHome(property['results'])
return this.propertiesFrDb = property['results']
},
)
}
}
I want to test this.getPropertiesFrDb() in ngOnInit
i would like to test the case with this.propertyWarehouseService.currentValuesProperties !== ''and checked that this.getPropertiesFrDbService.getHomeProperties() is called and checked the value of propertiesFrDb
and my spec.ts file
import { async, ComponentFixture, TestBed, fakeAsync, tick } from '#angular/core/testing';
import { MainSectionComponent } from './home-properties.component';
import { CUSTOM_ELEMENTS_SCHEMA } from '#angular/core';
import { MaterialModule } from 'src/app/material/material.module';
import { HttpClientTestingModule } from '#angular/common/http/testing';
import { RouterTestingModule } from '#angular/router/testing';
import { GetPropertiesFrDbService } from 'src/app/services/getPropertiesFromDb/get-properties-fr-db.service';
import { MOCKPROPERTIES, MockPropertyWarehouseService } from 'src/app/mocks/property-post';
import { NgxPaginationModule, PaginatePipe } from 'ngx-pagination';
import { PropertyWarehouseService } from 'src/app/services/propertyWarehouse/property-warehouse.service';
import { BsDropdownModule } from 'ngx-bootstrap';
import { NgbModule } from '#ng-bootstrap/ng-bootstrap';
import { StorageServiceModule } from 'angular-webstorage-service';
import { of } from 'rxjs/internal/observable/of';
fdescribe('MainSectionComponent', () => {
let component: MainSectionComponent;
let fixture: ComponentFixture<MainSectionComponent>;
const PROPERTYMODEL = MOCKPROPERTIES;
const spyPropertyWarehouseService = jasmine.createSpyObj('spypropertyWarehouseService', ['currentValuesProperties', 'getPropertyHome$']);
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
MaterialModule,
HttpClientTestingModule,
RouterTestingModule.withRoutes([]),
NgxPaginationModule,
BsDropdownModule.forRoot(),
NgbModule,
StorageServiceModule,
],
declarations: [
MainSectionComponent,
],
providers: [
{
provide: PropertyWarehouseService,
useValue: spyPropertyWarehouseService
}
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MainSectionComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', (() => {
// console.log('properties', component);
expect(component).toBeTruthy();
}));
it('Should get propertiesFrDb from GetPropertiesFrDbService', async(() => {
spyPropertyWarehouseService.currentValuesProperties.and.returnValue(PROPERTYMODEL);
spyPropertyWarehouseService.getPropertyHome$.and.returnValue(of(PROPERTYMODEL));
expect(component.propertiesFrDb).toBe(PROPERTYMODEL);
console.log('spy',spyPropertyWarehouseService);
}));
});
Try creating a stub as below:
export class PropertyWarehouseServiceStub{
currentValuesProperties = '';
getPropertyHome$ = new BaheviorSubject<any>('someObj');
setPropertiesHome(){ }
}
export class GetPropertiesFrDbServiceStub{
getHomeProperties(){
return of({results: 'val'})
}
}
in component file make the service public in constructor so that we can override some of its behaviors:
constructor(...,
public propertyWarehouseService: PropertyWarehouseService,
public getPropertiesFrDbService: GetPropertiesFrDbService,
....)
and in spec file as :
providers: [
{
provide: PropertyWarehouseService,
useClass: PropertyWarehouseServiceStub
},{
provide: GetPropertiesFrDbService,
useClass: GetPropertiesFrDbServiceStub
}
],
......
....
..
it('should call getPropertiesFrDb() in ngOnInit',()=>{
spyOn(component,'getPropertiesFrDb').and.callThrough();
component.ngOnInit();
expect(component.getPropertiesFrDb).toHaveBeenCalled();
})
it('inside getPropertiesFrDb() should call getPropertiesFrDbService.getHomeProperties() when "propertyWarehouseService.currentValuesProperties" is empty,()=>{
spyOn(component.getPropertiesFrDbService,'getHomeProperties').and.callThrough();
spyOn(component.propertyWarehouseService,'setPropertiesHome').and.callThrough();
component.getPropertiesFrDb();
expect(component.getPropertiesFrDbService.getHomeProperties).toHaveBeenCalled();
expect(component.propertyWarehouseService.setPropertiesHome).toHaveBeenCalledWith('val');
expect(component.propertiesFrDb).toBe('someObj');
})
it('inside getPropertiesFrDb() should not call getPropertiesFrDbService.getHomeProperties() when "propertyWarehouseService.currentValuesProperties" is NOT empty,()=>{
component.propertyWarehouseService.currentValuesProperties = 'Not empty';
spyOn(component.getPropertiesFrDbService,'getHomeProperties').and.callThrough();
spyOn(component.propertyWarehouseService,'setPropertiesHome').and.callThrough();
component.getPropertiesFrDb();
expect(component.getPropertiesFrDbService.getHomeProperties).not.toHaveBeenCalled();
expect(component.propertyWarehouseService.setPropertiesHome).not.toHaveBeenCalledWith('val');
expect(component.propertiesFrDb).toBe('val');
})
You can refer to this intro article written by me on Karma-jasmine which contains more article references for several test use cases.
This one is very much similar to what you are looking for. I am planning to write some more articles, in case you wanna follow.
Also, I have no clue on why you are using return as below inside getPropertiesFrDb()
return this.propertiesFrDb = prop
which is of no use because no value of this function has been assigned to any variable inside ngOnInit.

Angular test entry component from another component

I'm a beginner with Angular.
I created a simple application with Angular Bootstrap who open a simple modal.
Before to continue, I want to implements my tests.
My problems :
I would like test this workflow for MainComponent :
Click on the button openModal (or call openModal)
Verify all content of this modal
Simulate/click on dismiss and close, and verify that my functions
getDismissReason and closeModal are called.
My questions :
Q1
When we execute :
expect(modalService.open).toHaveBeenCalled();
How to be sure that the modal is really open or function really called ?
Q2 :
When i try this code :
const h4DebEl : DebugElement = fixtureMainComponent.debugElement.query(By.css('h4'));
Why it's null ?
I think it's because the H4 tag is in element of ModalComponent.
Then, I tested :
fixtureModalComponent = TestBed.createComponent(ModalComponentComponent);
But I have an error provider for activeModal...
Please find bellow my actual code :
MainComponentComponent.component.ts
import { Component, OnInit } from '#angular/core';
import { NgbModal, ModalDismissReasons } from '#ng-bootstrap/ng-bootstrap';
import { ModalComponentComponent } from '../modal-component/modal-component.component';
#Component({
selector: 'app-main-component',
templateUrl: './main-component.component.html',
styleUrls: ['./main-component.component.scss']
})
export class MainComponentComponent implements OnInit {
constructor(
// Use modalService to open a modal
private modalService: NgbModal
) { }
ngOnInit() {
}
openModal() {
const modalRef = this.modalService.open(ModalComponentComponent);
// Define the value #Input person in ModalComponentComponent
modalRef.componentInstance.person = {
name: "Itachi",
lastName: "Uchiwa"
};
modalRef.result.then((result) => {
this.closeModal();
}, (reason) => {
this.dismissReason(reason);
});
}
/**
* Close modal
*/
closeModal() {
console.log('Closed modal');
}
/**
* Dismiss modal
*/
dismissReason(reason: any) {
if (reason === ModalDismissReasons.ESC) {
this.dismissReasonEsc();
} else if (reason === ModalDismissReasons.BACKDROP_CLICK) {
this.dismissReasonBackdropClick();
} else {
this.dismissReasonUnknownReason();
}
}
dismissReasonEsc() {
console.log('Dismiss called by pressing ESC');
}
dismissReasonBackdropClick() {
console.log('Dismiss called by pressing BACKDROP_CLICK');
}
dismissReasonUnknownReason() {
console.log("Dismiss called");
}
}
MainComponentComponent.component.spec.ts
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { NgbModal, NgbModalRef, ModalDismissReasons } from '#ng-bootstrap/ng-bootstrap';
import { AppModule } from '../app.module';
import { MainComponentComponent } from './main-component.component';
import { ModalComponentComponent } from '../modal-component/modal-component.component';
import { By } from '#angular/platform-browser';
import { DebugElement } from '#angular/core';
describe('MainComponentComponent', () => {
let component: MainComponentComponent;
// Wrapper MainComponentComponent
let fixtureMainComponent : ComponentFixture<MainComponentComponent>;
let modalService: NgbModal;
let modalRef: NgbModalRef;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports : [ AppModule ]
}).compileComponents().then(() => {
modalService = TestBed.get(NgbModal);
fixtureMainComponent = TestBed.createComponent(MainComponentComponent);
component = fixtureMainComponent.componentInstance;
modalRef = modalService.open(ModalComponentComponent);
spyOn(modalService, "open").and.returnValue(modalRef);
spyOn(component, "openModal").and.callThrough();
spyOn(component, "dismissReason").and.callThrough();
spyOn(component, "dismissReasonEsc").and.callThrough();
spyOn(component, "dismissReasonBackdropClick").and.callThrough();
spyOn(component, "dismissReasonUnknownReason").and.callThrough();
spyOn(component, "closeModal").and.callThrough();
});
}));
afterAll(() => {
modalService.dismissAll();
fixtureMainComponent.destroy();
component = null;
modalRef.close();
modalRef = null;
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('Open modal', () => {
component.openModal();
expect(modalService.open).toHaveBeenCalled();
});
it('Open modal with click on the button', () => {
fixtureMainComponent.debugElement.query(By.css('#openModalBtn')).triggerEventHandler("click", null);
fixtureMainComponent.detectChanges();
expect(component.openModal).toHaveBeenCalled();
expect(modalService.open).toHaveBeenCalled();
});
it('Check element in modal', () => {
fixtureMainComponent.debugElement.query(By.css('#openModalBtn')).triggerEventHandler("click", null);
fixtureMainComponent.detectChanges();
// Return null
const h4DebEl : DebugElement = fixtureMainComponent.debugElement.query(By.css('h4'));
// const h4HtmlEl : HTMLElement = h4DebEl.nativeElement;
// expect(h4element.textContent).toEqual("Header :");
});
/**
* Check the dismiss method ESC
* To do same for BACKDROP_CLICK, ...
*/
it('Dimiss with ESC', () => {
component.openModal();
modalRef.dismiss(ModalDismissReasons.ESC);
expect(component.dismissReason).toHaveBeenCalled();
expect(component.dismissReasonEsc).toHaveBeenCalled();
});
});
ModalComponentComponent.component.ts
import { Component, Input } from '#angular/core';
import { NgbActiveModal } from '#ng-bootstrap/ng-bootstrap';
#Component({
selector: 'app-modal-component',
template: `
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">Header : {{person.lastName}} {{person.name}}</h4>
<button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross click')">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
Content modal
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-dark" (click)="activeModal.close('Save click')">Save</button>
</div>
`
})
export class ModalComponentComponent {
#Input() person;
constructor(public activeModal: NgbActiveModal) { }
}
app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '#angular/core';
import { NgbModule } from '#ng-bootstrap/ng-bootstrap';
import { AppComponent } from './app.component';
import { MainComponentComponent } from './main-component/main-component.component';
import { ModalComponentComponent } from './modal-component/modal-component.component';
#NgModule({
declarations: [
AppComponent,
MainComponentComponent,
ModalComponentComponent
],
imports: [
BrowserModule,
NgbModule.forRoot()
],
providers: [],
bootstrap: [AppComponent],
entryComponents: [ ModalComponentComponent ],
schemas : [ CUSTOM_ELEMENTS_SCHEMA ]
})
export class AppModule { }
Thank you for your help and your time,
Best regards,
Q1 How to be sure that the modal is really open or function really called ?
really open: not the concern of your test, should be tested in ng-bootstrap
or function really called: is the test says its open, then its open :)
Q2
- debugelement: no idea
Mock activemodal
const activeModal: any = jasmine.createSpyObj('activeModal', ['close', 'dismiss']);
Add to testbed providers
{ provide: NgbActiveModal, useValue: activeModal }

Angular 2+, on drop-down change read value and load data - json files

Need bit help in the Angular any version(2,3,4,5), I am trying from last 2 weeks. Any help would be appreciated.
Apologies, due to big code I am not able to add it in Plunker or JSfiddle.
My workflow goes like this
1 - load the metadata.json
2 - read the first value from the metadata.json
3 - load the first json from folder in APP_INITIALIZER
4 - Populate all the values from metadata.json in dropdown
5 - whenever dropdown value changed load the relaevant json and get the objects display in the UI
I have 3 components
Navigation.component (Dropdown change is triggering here)
dashboard.component (data will be changed based on dropdown content)
programmer.component (data will be changed based on dropdown content)
Whenever Dropdown change event is triggered I want to load the data from json.
metadata.json
[
{
"name": "Q_1090",
"value": "project_q_1090.json"
},
{
"name": "Q_1234",
"value": "project_q_1234.json"
},
{
"name": "Q_1528",
"value": "project_q_1528.json"
}
]
app.config.ts
import { Injectable } from '#angular/core';
import { Http } from "#angular/http";
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class AppConfig {
config: any;
user: any;
objects: any;
fileName: any;
constructor(private http: Http) {
console.log('ConfigService called.')
}
load(projectName) {
return new Promise((resolve) => {
/** This method: Loads "host_configuration.json" to get the current working environment. */
this.http.get('./assets/host_configuration.json').map(res => res.json())
.subscribe(config => {
console.log('Configuration loaded');
this.config = config;
/** This method: Loads all the objects from json */
let getSummaryParameters: any = null;
getSummaryParameters = this.http.get('./assets/json/' + projectName);
if (getSummaryParameters) {
getSummaryParameters
.map(res => res.json())
.subscribe((response) => {
this.objects = response;
return resolve(true);
});
} else {
return resolve(true);
}
});
});
}
loadMetadata() {
return new Promise((resolve) => {
//reading metadata.json
this.http.get('./assets/metadata.json').map(res => res.json())
.subscribe(fileName => {
console.log('metadata loaded');
this.fileName = fileName;
return resolve(true);
});
});
}
}
app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule, APP_INITIALIZER } from '#angular/core';
import { FormsModule } from '#angular/forms';
import { RouterModule } from '#angular/router';
import { HttpClientModule } from '#angular/common/http';
import { HttpModule, JsonpModule } from '#angular/http';
import { AppRoutes } from './app.routing';
import { AppConfig } from './app.config';
import { AppComponent } from './app.component';
import { NavigationComponent } from './navigation/navigation.component';
import { SharedModule } from './shared/shared.module';
import { BrowserAnimationsModule } from '#angular/platform-browser/animations';
import { BreadcrumbsComponent } from './navigation/breadcrumbs/breadcrumbs.component';
import { TitleComponent } from './navigation/title/title.component';
#NgModule({
declarations: [
AppComponent,
NavigationComponent,
BreadcrumbsComponent,
TitleComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
RouterModule.forRoot(AppRoutes),
SharedModule,
HttpClientModule,
HttpModule,
JsonpModule,
FormsModule
],
providers: [AppConfig,
{
provide: APP_INITIALIZER,
useFactory: (config: AppConfig) => () => config.load('project_q_1234.json'),
deps: [AppConfig],
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
dashboard.component.ts
import { Component, OnInit } from '#angular/core';
import { Chart } from 'chart.js';
import { AppConfig } from '../../app.config';
declare var Chart;
#Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: [
'./dashboard.component.css'
]
})
export class DashboardComponent implements OnInit {
constructor(public appConfig: AppConfig, private hostConfig: AppConfig, public getSummaryParameters: AppConfig) { }
ngOnInit() {
this.updates();
}
updates() {
//assign all Parameters to objects
this.objects = this.getSummaryParameters.objects;
var JsonData = this.objects.Information.data;
console.log(JsonData["0"]["0"] + " : " + JsonData["0"][1]);
}
}
programmer.component.ts
import { Component, OnInit, ViewEncapsulation } from '#angular/core';
import { Chart } from 'chart.js';
import { CommonModule } from '#angular/common';
import { NgbModal, ModalDismissReasons } from '#ng-bootstrap/ng-bootstrap';
import { AppConfig } from '../../app.config';
declare function ChangeSortOrder(): any;
#Component({
selector: 'app-simple-page',
templateUrl: './programmer.component.html'
})
export class ProgrammerComponent implements OnInit {
objects;
constructor(public appConfig: AppConfig, private hostConfig: AppConfig, public getSummaryParameters: AppConfig, private modalService: NgbModal) { }
ngOnInit() {
this.updateData();
}
updateData() {
//assign all Parameters to objects
this.objects = this.getSummaryParameters.objects;
}
}
navigation.component.ts
import { Component, ElementRef, OnInit, ViewChild, Injectable, NgModule } from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router';
import { Http } from "#angular/http";
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { AppConfig } from '../app.config';
import { DashboardComponent } from '.././pages/dashboard/dashboard.component';
import { ProgrammerComponent } from '.././pages/programmer/programmer.component';
#Component({
selector: 'app-admin',
templateUrl: './navigation.component.html',
providers: [DashboardComponent, ProgrammerComponent]
})
#Injectable()
export class NavigationComponent implements OnInit {
fileName: any;
selectedfileName: any;
config: any;
objects: any;
constructor(public menuItems: MenuItems, private http: Http, private appConfig: AppConfig, public router: Router,
private hostConfig: AppConfig, public getSummaryParameters: AppConfig, private dashboardComponent: DashboardComponent,
private programmerComponent: ProgrammerComponent) {
}
ngOnInit() {
this.appConfig.loadMetadata().then(fileName => {
this.fileName = this.appConfig.fileName;
//Initial loading for project Drop-down, Fetch first JSON from metadata.json
this.selectedfileName = 'project_q_1234.json';
});
}
refreshApp(projectName) {
this.appConfig.load(projectName).then(objects => {
this.objects = objects;
this.updateData();
//this commented code partially works but data is not loading properlly
//this.dashboardComponent.updates();
//this.programmerComponent.updateData();
//this.qCProgrammerComponent.updateQCData();
});
}
updateData() {
console.log("Dropdown change start");
//load all the host related settings
this.config = this.hostConfig.config;
localStorage.setItem('url', this.config.host);
localStorage.setItem('folder', this.config.folder);
}
As you can't share a demo, I made my own to show how to load data from API / local json you can try from here.
Feel free to ask if it's not the scenario you want / I understand wrong.
DEMO
Here two things are done, first of all, get the metadata from constructor which will load your data on initializing your app, second select a click method in options to get the selected data and then that data can send to the url to get another data.
I don't know which css framework you using I used here angular material 2.
app.component.html
<p>
Using jsonplaceholder.typicode.com API
</p>
<mat-form-field style="width: 100%">
<mat-select placeholder="Select Any Users" [(value)]="selectedUser">
<mat-option *ngFor="let meta of metadata" (click)="getInfoAboutIndividualMeta(meta)" [value]="meta.name">
{{ meta.name }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field style="width: 100%" *ngIf="selectedUser">
<mat-select placeholder="Select Posts from {{selectedUser}}">
<mat-option *ngFor="let post of posts" (click)="selectedPost(post)" [value]="post.title">
{{ post.title }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-card *ngIf="selectPost">
<h1>{{selectPost?.title}}</h1>
<p [innerHTML]="selectPost?.body"></p>
</mat-card>
app.component.ts
name = 'Angular 6';
metadata: any[];
posts: any[];
selectedUser: string;
selectPost: Object;
constructor(private appConfig: AppConfig) {
this.metadata = [];
this.posts = [];
this.initialize();
}
initialize() {
this.appConfig.getMetadataJSON().subscribe(res => {
this.metadata = res;
this.selectedUser = this.metadata[0].name;
});
}
getInfoAboutIndividualMeta(meta: Object) {
console.log(meta);
const userId = meta.id;
this.appConfig.getIndividualMetadataJSON(userId).subscribe( res => {
this.posts = res;
});
}
selectedPost(post: Object) {
this.selectPost = post;
}
app-config.class.ts
import { Injectable } from '#angular/core';
import { HttpClient } from "#angular/common/http";
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { Observable } from 'rxjs';
#Injectable()
export class AppConfig {
constructor(private httpClient: HttpClient) {
}
public getMetadataJSON(): Observable<any> {
// Due to stackblitz can't get the local access I put this value to another api source
// const apiUrl = './assets/metadata.json'; // You can use this as well
const apiUrl = 'https://jsonplaceholder.typicode.com/users';
return this.httpClient.get(apiUrl);
}
public getIndividualMetadataJSON(userId: number): Observable<any> {
const apiUrl = 'https://jsonplaceholder.typicode.com/posts?userId=' + userId;
return this.httpClient.get(apiUrl);
}
}

Categories