ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked in HTTP loading interceptor - angular8

Goal:
Im trying to show a loading icon on every ajax call.To this end, I added an HTTP interceptor which sets a variable to true when one or more requests are ongoing and to false when all have completed. The UI tests for this value and shows a loader or not, depending.
Problem:
On every ajax call, an error is thrown:
ExpressionChangedAfterItHasBeenCheckedError:
Expression has changed after it was checked.
Previous value: 'ngIf: [object Object]'. Current value: 'ngIf: true'.
Simplified Stakckblitz with reproducible error:
https://stackblitz.com/edit/angular-h4rpfb
Code:
appcomponent.html:
<p *ngIf="loaderService.isLoading | async">
Loading!
</p>
<p *ngIf="!(loaderService.isLoading | async)">
Not Loading!
</p>
<button (click)="loadSomething()">Load Something</button>
{{matches|async}}
appcomponent.ts:
import { Component } from "#angular/core";
import { HttpClient } from "#angular/common/http";
import { LoaderService } from "./core";
import { Observable } from "rxjs";
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
matches: Observable<any>;
constructor(public loaderService: LoaderService, private http: HttpClient) {}
loadSomething() {
this.matches = this.http.get("https://jsonplaceholder.typicode.com/posts");
}
}
loader.interceptor.ts:
import { Injectable } from '#angular/core';
import {
HttpErrorResponse,
HttpResponse,
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor
} from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import { LoaderService } from './loader.service';
#Injectable()
export class LoaderInterceptor implements HttpInterceptor {
private requests: HttpRequest<any>[] = [];
constructor(private loaderService: LoaderService) { }
removeRequest(req: HttpRequest<any>) {
const i = this.requests.indexOf(req);
if (i >= 0) {
this.requests.splice(i, 1);
}
console.log(i, this.requests.length);
this.loaderService.isLoading.next(this.requests.length > 0);
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
this.requests.push(req);
this.loaderService.isLoading.next(true);
return Observable.create(observer => {
const subscription = next.handle(req)
.subscribe(
event => {
if (event instanceof HttpResponse) {
this.removeRequest(req);
observer.next(event);
}
},
err => { this.removeRequest(req); observer.error(err); },
() => { this.removeRequest(req); observer.complete(); });
// teardown logic in case of cancelled requests
return () => {
this.removeRequest(req);
subscription.unsubscribe();
};
});
}
}
loader.service.ts:
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { ReplaySubject } from 'rxjs/ReplaySubject';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class LoaderService {
public isLoading = new BehaviorSubject(false);
constructor() {}
}

Ok I got it to work by adding this to the component with the loader:
changeDetection: ChangeDetectionStrategy.OnPush
So the appcomponent.html now looks like this:
import { Component,ChangeDetectionStrategy } from "#angular/core";
import { HttpClient } from "#angular/common/http";
import { LoaderService } from "./core";
import { Observable } from "rxjs";
#Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
matches: Observable<any>;
constructor(public loaderService: LoaderService, private http: HttpClient) {}
loadSomething() {
this.matches = this.http.get("https://jsonplaceholder.typicode.com/posts");
}
}
Example:
https://stackblitz.com/edit/angular-n6fzjm

Related

NullInjectorError: No provider for ReducerManager on angular spec file testing

I am testing ngrx/store based component. on test getting NullInjectorError: No provider for ReducerManager
Error message:
StaticInjectorError(Platform: core)[StoreFeatureModule -> ReducerManager]:
NullInjectorError: No provider for ReducerManager!
Error: StaticInjectorError(DynamicTestModule)[StoreFeatureModule -> ReducerManager]:
StaticInjectorError(Platform: core)[StoreFeatureModule -> ReducerManager]:
NullInjectorError: No provider for ReducerManager!
How to fix this? here is my test spec file:
import { HeaderNavShellComponent } from './header-nav-shell.component';
import { HeaderComponent } from './../../header/header.component';
import { HeaderNavComponent } from './../../components/header-nav/header-nav.component';
import { StoreModule, Store } from '#ngrx/store';
import { TranslateFakeLoader,TranslateLoader,TranslateModule,TranslateService, TranslateStore } from '#ngx-translate/core';
import { RouterTestingModule } from '#angular/router/testing';
import { reducerShared } from "./../../state/reducers/shared.reducer";
import { HttpClientModule, HttpClient } from '#angular/common/http';
import { of } from 'rxjs';
import * as fromRoot from "./../../../calendar/state";
// import { reducerCalendar } from "./../../../calendar/state/calendar.reducer";
describe('HeaderNavShellComponent', () => {
let component: HeaderNavShellComponent;
let fixture: ComponentFixture<HeaderNavShellComponent>;
let dispatchSpy;
let store:Store<fromRoot.NewState>
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ HeaderNavShellComponent, HeaderComponent, HeaderNavComponent ],
imports:[
HttpClientModule,
StoreModule.forFeature("shared", reducerShared ),
RouterTestingModule,
TranslateModule.forChild({
          loader: {
            provide: TranslateLoader,
            useClass: TranslateFakeLoader
          }
         })
],
providers:[TranslateService, TranslateStore, HttpClient ]
})
.compileComponents();
}));
beforeEach(fakeAsync(() => {
store = TestBed.get(Store);
spyOn(store, 'dispatch').and.callThrough();
fixture = TestBed.createComponent(HeaderNavShellComponent);
component = fixture.componentInstance;
fixture.detectChanges();
}));
it('should create', fakeAsync(() => {
expect(component).toBeTruthy();
}));
});
here is the ts file:
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { Store, select } from "#ngrx/store";
import { Observable } from "rxjs";
import { ModelEvent, EventState } from "./../../../calendar/models/model.event";
import { ModelLang, ModelLonguage } from "./../../../shared-components/models/models";
import { CalendarActions, Load, EventSelected } from "./../../../calendar/state/calendar.actions";
import * as fromRoot from "./../../../calendar/state";
import * as fromObservables from "./../../state";
import { Lang, LoadLang } from "./../../state/actions/shared.actions";
import { ShowNavi } from "./../../../shared-components/state/actions/shared.actions";
#Component({
selector: 'header-nav-shell',
templateUrl: './header-nav-shell.component.html',
styleUrls: ['./header-nav-shell.component.scss']
})
export class HeaderNavShellComponent implements OnInit {
eventList$:Observable<ModelEvent[]>;
eventListSize$:number;
currentLang$:Observable<string>;
langList$:Observable<ModelLonguage[]>;
constructor(private store:Store<fromRoot.NewState>, private router:Router) { }
ngOnInit() {
this.store.dispatch(new Load());
this.store.dispatch( new LoadLang());
this.eventList$ = this.store.pipe(select(fromRoot.getEvents));
this.currentLang$ = this.store.pipe(select(fromObservables.getCurrentLang));
this.langList$ = this.store.pipe(select(fromObservables.getLangList));
}
eventSelected(event) {
this.store.dispatch(new EventSelected(event));
this.router.navigateByUrl("iboCalendar");
}
langChanged(event) {
this.store.dispatch( new Lang(event.Name));
}
leftNaviHandler(event):void {
this.store.dispatch(new ShowNavi(event));
}
}
Thanks in advance

Unable to get data when making httpClient.get in angular 5

I am trying to do a http.get using httpclient. However, and i am getting
ReferenceError: data is not defined
Inside my component class when subscribing to service method.
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
const httpOptions = {
headers: new HttpHeaders()
.set('Authorization', 'Bearer xxxxx')
.set('Content-Type', 'application/json')
};
#Injectable()
export class SetsService {
constructor(private http: HttpClient) { }
getSet() {
return this.http.get('http://localhost:1234/api/users', httpOptions);
}
}
my component
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { Router } from '#angular/router';
import { SetsService } from '../services/sets.service';
import { Observable } from 'rxjs/Observable';
#Component({
selector: 'app-sets',
templateUrl: './sets.component.html',
styleUrls: ['./sets.component.scss']
})
export class SetsComponent implements OnInit {
id: string = "";
set: any = {};
constructor(private route: ActivatedRoute, private router: Router, private _setsService: SetsService) {
this.route.params.subscribe(res => this.id = res.id);
}
ngOnInit() {
this.getSets();
}
getSets(){
this._setsService.getSet().subscribe(
(data) => { this.set = data },
err => console.error(err),
() => { console.log('done loading set') }
);
}}
When i look at the network the API call is successful and returning data.
Inside the html I am simply trying to print the set object.

The requested path contains undefined segment at index 0

authentication.service.ts in that my login service is define like this
import { Injectable } from '#angular/core';
import { Http, Headers, Response, RequestOptions } from '#angular/http';
import { Observable } from 'rxjs';
import 'rxjs/Rx';
import 'rxjs/add/operator/map'
import { AuthHttp } from 'angular2-jwt';
import { Router } from '#angular/router';
#Injectable()
export class AuthenticationService {
token: string;
redirectUrl: any;
constructor(public router: Router, private http: Http, private authHttp: AuthHttp) {
//set token if saved in local storage
if (localStorage.getItem('authToken') != null) {
try {
// var currentUser =
JSON.parse(localStorage.getItem('currentUser'));
this.token = localStorage.getItem('authToken');
// this.token = currentUser && currentUser.token;
} catch (e) {
localStorage.removeItem("authToken");
}
}
}
login(username: string, password: string) {
return this.http.post('http://localhost:3002/api/authenticate',({ username: username, password: password }))
.map(res => {
console.log(res);
var authToken = res.json().token;
localStorage.setItem('authToken', authToken);
let redirectUrlTemp: any;
redirectUrlTemp = this.redirectUrl;
this.redirectUrl = null;
if (!redirectUrlTemp) {
console.log(redirectUrlTemp);
redirectUrlTemp = ['/login'];
}
this.router.navigate(redirectUrlTemp);
},
err => {
//alert(error.text());
console.log(err.text());
});
}
logout(){
// clear token remove user from local storage to log user out
this.token = null;
localStorage.removeItem('authToken');
}
}
when i try to post data using postman it works properly but from angular 2 it not allowing to redirect from next page
Login.component.ts
import { Component, OnInit } from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router';
import { AuthenticationService } from "app/services";
import { LoginRequest } from "app/models/models";
import { AlertService } from 'app/services';
import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx';
import { Http, Response } from '#angular/http';
#Component({
moduleId: module.id,
selector: 'login',
templateUrl: 'login.component.html'
})
export class LoginComponent implements OnInit {
model: any = {};
getLogin: LoginRequest[] = [];
loading = false;
error = '';
returnUrl: string;
constructor(
private route: ActivatedRoute,
private router: Router,
private http: Http,
private alertService: AlertService,
private authenticationService: AuthenticationService) { }
login() {
this.loading = true;
this.authenticationService.login(this.model.username, this.model.password).subscribe(data => {
this.router.navigate([this.returnUrl]);
},
error => {
this.alertService.error(error);
this.loading = false;
});
//let redirect = this.authenticationService.redirectUrl ? this.authenticationService.redirectUrl : '/allTask';
}
}
auth.guadr.ts
import { Injectable } from '#angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '#angular/router';
import { AuthenticationService } from "app/services";
#Injectable()
export class AuthGuard implements CanActivate {
constructor(private router: Router, private authService: AuthenticationService) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
if (localStorage.getItem('authToken')) {
// logged in so return true
return true;
}
this.authService.redirectUrl =[state.url];
this.router.navigate(['/login']);
return false;
}
}
This error means that navigate array parameter with index 0 is undefined.
In your case you didn't set this.returnUrl in Login.component.ts
this.router.navigate([this.returnUrl]);

Creating restricted members area in Angular Fire 2

I am trying to make an application using angularfire 2. Can't find the perfect way to make the members area restricted that means only authenticated members can access that area. Here is my 'login.component.ts' file
import { Component, OnInit, HostBinding } from '#angular/core';
import { AngularFire, AuthProviders, AuthMethods } from 'angularfire2';
import { FormControl, FormGroup, Validators } from '#angular/forms';
import { Router } from '#angular/router';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css'],
})
export class LoginComponent {
state: string = '';
error: any;
login: FormGroup;
constructor(
public af: AngularFire,
private router: Router
) {
//official angfire 2 app example
//this.af.auth.subscribe(auth => console.log(auth));
}
ngOnInit() {
this.login = new FormGroup({
username: new FormControl('', Validators.required),
password: new FormControl('', Validators.required)
});
}
onSubmit() {
//console.log(this.login.value, this.login.valid);
var value = this.login.value;
//console.log(value.username);
//console.log(value.password);
this.af.auth.login({
email: value.username,
password: value.password,
},
{
provider: AuthProviders.Password,
method: AuthMethods.Password,
}).then(
(success) => {
console.log(success);
this.router.navigate(['/members']);
}).catch(
(err) => {
console.log(err);
this.error = err;
})
}
}
and the members.component.ts file
import { Component, OnInit } from '#angular/core';
import { AngularFire, AuthProviders, AuthMethods } from 'angularfire2';
import { Router } from '#angular/router';
#Component({
selector: 'app-other',
templateUrl: './members.component.html',
styleUrls: ['./members.component.css']
})
export class MembersComponent implements OnInit {
//name: "";
//state: string = '';
constructor(
public af: AngularFire,
private router: Router
) {
this.af.auth.subscribe(auth => {
if(auth) {
console.log(auth);
}
});
}
logout() {
this.af.auth.logout();
console.log('logged out');
this.router.navigate(['/login']);
}
ngOnInit() {
}
}
I know my code may be seem like a dumb one but actually I am trying to study over this. But there is not enough documentation to solve my problem I guess. Thanks in advance.
https://coursetro.com/posts/code/32/Create-a-Full-Angular-Authentication-System-with-Firebase
this is a great tutorial that solves your problem

How to use extends for this angulr2 scenario?

In my application core component is root component, In this page header , sidemenu designs are existed,
SideMenu is dynamic, so I write logic of the code in other sidemenu component,
Now I want to use the functionality of sidemenu in core component, I think it is
done by using extends,
My componets are
core component
import { Component, OnInit } from '#angular/core';
import {SidemenuComponent} from './sidemenu.component';
#Component({
selector: 'my-admin',
templateUrl: '../views/admin-header.html'
})
export class CoreComponent extends SidemenuComponent {
constructor( private router: Router) {
}
}
sidemenucomponent
import {OnInit } from '#angular/core';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/toPromise';
import {MenuService} from './core.service';
export class SidemenuComponent implements OnInit{
userroleId : any;
roleName: string;
menuItems:any;
constructor(private http: Http,private MenuService:MenuService) {
this.userroleId = localStorage.getItem("roleId")
}
ngOnInit(): void {
this.getSideMenu();
}
getSideMenu () {
if( this.userroleId == 1) {
this.MenuService.getAdminMenu().subscribe(menuItems => this.menuItems= menuItems, error => console.log(error));
}
if(this.userroleId == 2){
this.MenuService.getpractitionerMenu().subscribe(menuItems => this.menuItems= menuItems, error => console.log(error));
console.log('ss')
}
}
}
MenuService
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class MenuService {
constructor(private http: Http) {}
public getAdminMenu(): Observable<any> {
return this.http.get("./app/core/adminsidemenu.json")
.map((res:any) => res.json())
.catch(this.handleError);
}
public getpractitionerMenu(): Observable<any> {
return this.http.get("./app/core/practitioner.json")
.map((res:any) => res.json())
.catch(this.handleError);
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error); // for demo purposes only
return Promise.reject(error.message || error);
}
}
Here I am unable extends the sidemenu component in core component.
I am getting error in my console:
Constructors for derived classes must contain a 'super' call.
Please help me
It appears you may be missing the #Component decorator on the SidemenuComponent.
Here's a Plunker with a simple extended Component, and here's a good article on it: Component Ineritance.
You need to call the constructor of the parent class in your child-class:
#Component({
selector: 'my-admin',
templateUrl: '../views/admin-header.html'
})
export class CoreComponent extends SidemenuComponent {
constructor( private router: Router, http: Http, menuService:MenuService) {
super(http,menuService);
}
}