My application has LogComponent . Inside ngOnInit I retrieve the logs from the server through LogService and display them in the grid.
Here are two ways to do this:
First:
import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { Observable } from 'rxjs/Observable'; import { Log } from './log'; import { LogService } from './log.service'; import * as globals from '../../globals'; import { DataTransformService, HttpHandlerService, StateManagerService } from '../../shared'; @Component({ selector: 'app-log', templateUrl: 'log.component.html', styleUrls: ['log.component.css'], providers: [ DataTransformService, HttpHandlerService, LogService, StateManagerService] }) export class LogComponent implements OnInit { localGlobals: any = globals; logs: Log[]; currentPage: number; totalRecords: number = 0; firstRowIndex: number = 0; constructor( private stateManagerService: StateManagerService, private httpHandlerService: HttpHandlerService, private logService: LogService, private dataTransformService: DataTransformService, private router: Router ) {} ngOnInit() {
1st approach uses this function to retrieve logs:
this.logService.search(this.currentPage, respHandler).subscribe( logs => { this.logs = logs; }, err => console.error(err) );
The second way to get the logs is Observable.forkJoin :
import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { Observable } from 'rxjs/Observable'; import { Log } from './log'; import { LogService } from './log.service'; import * as globals from '../../globals'; import { DataTransformService, HttpHandlerService, StateManagerService } from '../../shared'; @Component({ selector: 'app-log', templateUrl: 'log.component.html', styleUrls: ['log.component.css'], providers: [ DataTransformService, HttpHandlerService, LogService, StateManagerService] }) export class LogComponent implements OnInit { localGlobals: any = globals; logs: Log[]; currentPage: number; totalRecords: number = 0; firstRowIndex: number = 0; constructor( private stateManagerService: StateManagerService, private httpHandlerService: HttpHandlerService, private logService: LogService, private dataTransformService: DataTransformService, private router: Router ) {} ngOnInit() {
The second approach uses this function to extract the logs:
Observable.forkJoin( this.logService.search(this.currentPage, respHandler) ).subscribe( data => { this.logs = data[0]; }, err => console.error(err) );
I have a test for this component that checks that properties are initialized after init:
import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { Router } from '@angular/router'; import { ReplaySubject } from 'rxjs/Rx'; import { Log } from './log'; import { LogComponent } from './log.component'; import { LogService } from './log.service'; import { DataTransformService, HttpHandlerService, LocalStorageService, StateManagerService } from '../../shared'; let comp: LogComponent; let fixture: ComponentFixture<LogComponent>; let de: DebugElement; describe('LogComponent', () => { // async compile html and css beforeEach(async(() => { let project = new ReplaySubject(1); let logServiceStub = { search: (pageNum: number, respHandler: any) => { let logs = [ { id: 1, ip: '127.0.0.1', user_id: 1, notification_event_id: 1, created_at: 1 }, { id: 2, ip: '127.0.0.2', user_id: 2, notification_event_id: 2, created_at: 2 } ]; project.next(logs); return project; } }; let routerStub = { url: 'log_url' }; TestBed.configureTestingModule({ declarations: [ LogComponent ], imports: [ FormsModule ], providers: [ DataTransformService, HttpHandlerService, LocalStorageService, { provide: Router, useValue: routerStub }, StateManagerService ], schemas: [ NO_ERRORS_SCHEMA ] }) .overrideComponent(LogComponent, { set: { providers: [ { provide: LogService, useValue: logServiceStub } ] } }) .compileComponents(); })); // synchronous beforeEach(() => { fixture = TestBed.createComponent(LogComponent); // LogComponent test instance comp = fixture.componentInstance; de = fixture.debugElement; }); it('initial variables are set after init', () => { fixture.detectChanges(); console.log(comp.logs); //TODO: test not working when requests are in forkJoin expect(comp.currentPage).toBe(1); expect(comp.firstRowIndex).toBe(0); expect(comp.logs).not.toBeUndefined(); expect(comp.logs.length).toBe(2); }); });
Problem : when running a test using the 1st approach ( without Observable.forkJoin ) it works fine. BUT , when I run the test using the 2nd approach ( with Observable.forkJoin ), it fails with an error:
Expected undefined not to be undefined.
Thus, the logs are not initialized after fixture.detectChanges (). It looks like if the request is inside Observable.forkJoin (), then initialization fails. I understand that there is only 1 request in Observable.forkJoin (), and I can go with the 1st approach, but I have other components in the system that use more than one request, so I need this problem to solve.
I hope you understand what I mean.
Thanks in advance.