ngIf Global Service ExpressionChangedAfterItHasBeenCheckedError - javascript

Developing a dashboard I find the following error, I understand that it is because I mishandle the Angular cycles.
I found workarounds but not a real solution.
I have a service that acts as a global scope then on the login screen when you pressed the button you updated the authenticated property to true.
The moment it becomes true I have a ngif that shows the header with the title which is stored in the global scope
From the home screen I updated the value of the title in global scope.
At that moment I get the error
ERROR Error: "ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'FormTitle:'. Current value: 'FormTitle: Home'."
global scope:
import { Injectable } from '#angular/core';
#Injectable()
export class ScopeService {
scope: any = {
FormTitle:"",
showMenu:false,
loading:false,
authenticated:false
};
}
app.component.html
<div class="container-flow">
<app-header *ngIf="ScopeService.scope.authenticated [FormTitle]="ScopeService.scope.FormTitle"></app-header>
<app-navigation *ngIf="ScopeService.scope.authenticated"></app-navigation>
<div class="main-container">
<router-outlet></router-outlet>
</div>
</div>
header.component.html
<div class="main-header">
<h1 class="title"> {{ FormTitle }} </h1>
</div>
home.commonent:
import { Component, OnInit } from '#angular/core';
import { ScopeService } from '../../services/scope.service';
import { ApiService } from '../../services/api.service';
import { Router , ActivatedRoute } from "#angular/router";
import { FormBuilder, FormGroup , Validators } from '#angular/forms';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
viewForm: FormGroup;
constructor(private formBuilder: FormBuilder,
private ScopeService:ScopeService,
private ApiService: ApiService,
private route: ActivatedRoute,
private router:Router) { }
ngOnInit() {
this.ScopeService.scope.FormTitle = this.route.snapshot.data['title'];
}
}
login click event:
continuar() {
this.ScopeService.scope.authenticated = true;
this.router.navigate(['home'],{ skipLocationChange : true });
}

Related

What has caused this error to keep emerging and how to prevent it from keep happening

I'm still new to Angular and recently I have been trying to make a login system that goes to another page. But unfortunately, I can't quite get to that stage yet as I have this problem in my code.
import { Component, OnInit } from '#angular/core';
import { AuthService } from '../../services/auth.service';
import { Router } from '#angular/router';
#Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss']
})
export class DashboardComponent implements OnInit {
constructor(public authservice: AuthService, private router: Router) { }
ngOnInit(): void {
}
}
the error that i keep getting from the vscode is:
Cannot find module '../../services/auth.service' or its corresponding type declarations
However, when i try to login to another page, it does not share the similar error.
import { Component, OnInit } from '#angular/core';
import { AuthService } from '../../services/auth.service';
import { Router } from '#angular/router';
#Component({
selector: 'app-userinfo',
templateUrl: './userinfo.component.html',
styleUrls: ['./userinfo.component.css']
})
export class UserinfoComponent implements OnInit {
constructor(public authservice: AuthService, private router: Router) { }
ngOnInit(): void {
}
}

Angular: hiding a component with *ngIf doesn't work

I have just started with Angular and have already faced an issue: a button actually toggles the variable I need (show) but it doesn't affect the course.component
course.component must show app-csgo-course, boolean show is true because the component is visible, but after it toggles in navbar.component, nothing changes.
<app-csgo-course *ngIf="show"> </app-csgo-course>
import { NavbarComponent } from './../navbar/navbar.component';
import { Component, OnInit} from '#angular/core';
import { CourseService } from 'src/app/course.service';
#Component({
selector: 'app-course',
templateUrl: './course.component.html',
styleUrls: ['./course.component.css']
})
export class CourseComponent implements OnInit {
constructor() { }
ngOnInit(): void { }
service = new CourseService;
show = this.service.GetShow();
}
In navbar.component there's a button which toggles the "show" variable
<button (click)="ToggleShow()" >
<li class="nav-item active" id="csgo-logo">
<a href="#">
<img class="game-logo" src="assets\img\csgo-logo.png" title="Counter Strike: Global Offensive">
<!-- <a>CS:GO <span class="sr-only">(current)</span></a> -->
</a>
</li>
</button>
import { CourseService } from 'src/app/cheat.service';
import { Component, OnInit, Input, Output, } from '#angular/core';
#Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.css']
})
export class NavbarComponent implements OnInit {
service = new CourseService;
show = this.service.GetShow();
ngOnInit(): void {
}
public ToggleShow() {
this.service.show = this.service.ToggleShow();
console.log(this.service.show);
return this.service.show;
}
}
The course.service file
#Injectable({
providedIn: 'root'
})
export class CourseService {
show: boolean = true;
GetShow() {
return this.show;
}
ToggleShow() {
return this.show = !this.show
}
constructor() { }
}
}
Would appreciate your help!
Since you are new to Angular, let me break it down for you.
You need to create a BehaviorSubject to capture the event of toggle (this is called reactive programming which is a achieved using RxJS in Angular ).
Do not use new for a service, rather inject it in constructor.
course.service
#Injectable({
providedIn: 'root'
})
export class CourseService {
private show: boolean = true;
private toggle$ = new BehaviorSubject<boolean>(true);
constructor() { }
toggleEvent() {
return this.toggle$.asObservable();
}
toggleShow() {
this.show = !this.show
this.toggle$.next(this.show);
}
}
in NavbarComponent
import { CourseService } from 'src/app/cheat.service';
import { Component, OnInit, Input, Output, } from '#angular/core';
#Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.css']
})
export class NavbarComponent implements OnInit {
show = boolean;
// IMP: make sure to inject the service and not do "new CourseService;"
constructor(public service: CourseService){}
ngOnInit(): void {
this.service.toggleEvent().subscribe(showFlag => {
this.show = showFlag;
})
}
public ToggleShow(): void {
this.service.toggleShow();
}
}
in courseComponent
import { Component, OnInit} from '#angular/core';
import { CourseService } from 'src/app/course.service';
#Component({
selector: 'app-course',
templateUrl: './course.component.html',
styleUrls: ['./course.component.css']
})
export class CourseComponent implements OnInit {
show: boolean ;
// IMP: make sure to inject the service and not do "new CourseService;"
constructor(public service: CourseService){}
ngOnInit(): void {
this.service.toggleEvent().subscribe(showFlag => {
this.show = showFlag;
})
}
}
PS: I would suggest you to read about "how to unsubscribe an observable" and how it causes memory leaks. Once you get some idea, you should implement that in the above provided code as well. That's a best practice. Happy learning. Let me know if you have any more questions

Theme switcher on localstorage observable service

I implemented a dynamic theme switcher (with tutorial) in my angular app. It work's but when I reload website, the choice is not remembered.
I read about localStorage and i will use it but still doesn't work because I don't know how "where" I should get data from this localStorage, that the choice of the theme will be remembered when I reload the page.
I have this code:
theme.service.ts new version
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class ThemeService {
private _themeDark: Subject<boolean> = new Subject<boolean>();
isDarkFunction() {
let value = localStorage.getItem('isDark');
this._themeDark.next(value);
return this._themeDark.asObservable();
}
isThemeDark = this.isDarkFunction();
setDarkTheme(isThemeDark: boolean) {
this._themeDark.next(isThemeDark);
localStorage.setItem('isDark', JSON.stringify(isThemeDark));
}
}
navbar.component.html
<div class="container-fluid switcher-container">
<mat-slide-toggle [checked]="isThemeDark | async" (change)="toggleDarkTheme($event.checked)">Dark theme</mat-slide-toggle>
</div>
navbar.component.ts
import { Component, OnInit } from '#angular/core';
import { ThemeService } from '../services/theme.service';
import { Observable } from 'rxjs/Observable';
#Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.scss']
})
export class NavbarComponent implements OnInit {
isThemeDark: Observable<boolean>;
constructor(
private themeService: ThemeService) { }
ngOnInit() {
this.isThemeDark = this.themeService.isThemeDark;
}
toggleDarkTheme(checked: boolean) {
this.themeService.setDarkTheme(checked);
}
}
app.component.ts
import { Component, OnInit } from '#angular/core';
import { Observable } from "rxjs/Observable";
import { ThemeService } from "./services/theme.service";
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
providers: [ThemeService]
})
export class AppComponent implements OnInit {
isThemeDark: Observable<boolean>;
constructor(
public themeService: ThemeService) {
}
ngOnInit() {
this.isThemeDark = this.themeService.isThemeDark;
}
}
Please help,
Regards
You might write something like the following in theme.service.ts.
I don't know if it will run flawlessly as is but the idea is to read from localstorage in isThemeDark().
isThemeDark() {
let value = localStorage.getItem('isDark');
this._themeDark.next(value);
return this._themeDark.asObservable();
}
I think it's because you when you do localStorage.getItem('isDark') the result is a string, not a boolean. Maybe try:
let value = JSON.parse(localStorage.getItem('isDark')) === true;
Also check manually if the localstorage is kept after a refresh. Some browsers have a setting to clear everything on refresh.

How to bind an HTTP Angular 4/5 request to html?

I have returned some raw JSON from an angular HTTPClient GET request and I am unsure how to now bind the properties of my JSON object to my html dynamically.
I would usually think to just store the returned JSON object into a variable and then reference it where I need it using dot notation, but Angular doesn't seem to work that way as I cannot set my http get request to a variable in ngOnInit and reference it.
I am using ngOnInit to initialize it when my Component loads and it is successfully logging to the console, but how do I get it binded INTO my html?
app.component.ts:
import { Component } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'Contacts';
constructor (private httpClient: HttpClient) {}
ngOnInit(): void {
this.httpClient.get('**URL PATH RETURNING JSON OBJECT**')
.subscribe((data) => {
console.log(data));
}
}
app.module.ts:
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { HttpClientModule } from '#angular/common/http';
import { AppComponent } from './app.component';
#NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
HTML:
<div id= contacts-container>
<header><h1> {{ title }} </h1></header>
<div id= "favoritesContainer">
<p>Favorite Contacts</p>
</div>
<ul>
<li *ngFor="let contact of contacts">
<div *ngIf= "!contact.isFavorite">
<img src={{contact.smallImageURL}} />
<h3><img src="../assets/Favorite Star (True)/Favorite — True.png">{{ contact.name }} </h3>
<br>
<p>{{ contact.companyName }}</p>
<hr>
</div>
</li>
</ul>
</div>
You don't seem to have a contacts variable in your app.component.ts.
This is what it should look like:
export class AppComponent {
title = 'Contacts';
contacts: any[]; // Add variable
constructor (private httpClient: HttpClient) {}
ngOnInit(): void {
this.httpClient.get('**URL PATH RETURNING JSON OBJECT**')
.subscribe((data)=>{
console.log(data);
this.contacts = data; // Once you get the data, assign the data returned to contacts
});
}
}
Try like this :
import { Component } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'Contacts';
constructor (private httpClient: HttpClient) {}
ngOnInit(): void {
this.httpClient.get('**URL PATH RETURNING JSON OBJECT**')
.subscribe((data)=>{
this.contacts = data;//I have assigned data here inside subscription
console.log(data);
});
}
}
and reference this.contacts in same way you are doing in HTML

How do i send data from component A to component B Angular 2

I want to display the username on my NavbarComponent, the data coming from LoginComponent.
login.component.ts
import { Component, OnInit } from '#angular/core';
import { FormBuilder,FormGroup,Validators,FormControl } from '#angular/forms';
import { AuthService } from '../../services/auth.service';
import { Router } from '#angular/router';
import { FlashMessagesService } from 'angular2-flash-messages';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
form : FormGroup;
message;
messageClass;
constructor(
private formBuilder: FormBuilder,
private authService:AuthService,
private router: Router,
private flashMessagesService:FlashMessagesService
) {
this.createForm();
}
createForm(){
this.form=this.formBuilder.group({
username:['', Validators.required],
password:['', Validators.required]
})
}
onLoginSubmit(){
const user={
username: this.form.get('username').value,
password: this.form.get('password').value
}
this.authService.login(user).subscribe(data=>{
if(!data.success){
this.messageClass="alert alert-danger";
this.message=data.message;
}
else{
this.messageClass="alert alert-success";
this.message=data.message;
this.authService.storeUserData(data.token,data.user);
setTimeout(()=>{
this.router.navigate(['/profile']);
},2000);
this.flashMessagesService.show('Welcome to bloggy, '+ this.form.get('username').value +' !',{cssClass: 'alert-info'});
}
});
}
}
navbar.component.ts
import { Component, OnInit } from '#angular/core';
import { AuthService } from '../../services/auth.service';
import { Router } from '#angular/router';
import { FlashMessagesService } from 'angular2-flash-messages';
#Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.css']
})
export class NavbarComponent implements OnInit {
usernameNav;
constructor(
private authService:AuthService,
private router:Router,
private flashMessagesService:FlashMessagesService
) { }
onLogoutClick(){
this.authService.logout();
this.flashMessagesService.show('You are logged out',{cssClass: 'alert-info'});
this.router.navigate(['/']);
}
ngOnInit() {
}
}
I am sorry if there's too much code but basically i want to take data.user.username from LoginComponent in the onLoginSubmit() function, send it to NavbarComponent, use it in a variable and display it on the html.
I tried to import the NavbarComponent, didn't work.
Pretty Interesting question , basically solution to your problem is Observable/subscriber, you need to
listen when the value changes in the login component and send it back to navbar component to display.
you can use global Observable like this
let suppose you create one Observable in your global file like this
public loggedInObs: Rx.Subject<any> = new Rx.Subject<any>();
public loggedInVar: boolean = false;
for using this you have to import some dependency like this
import { Observable } from 'rxjs/Rx';
import * as Rx from 'rxjs/Rx';
import 'rxjs/add/observable/of';
import 'rxjs/Rx';
import 'rxjs/add/operator/map';
Than in your login component you tell angular that there are some changes occurred like user login successfully.
and fire observable , so that angular will able to listen in whole app where you set subscriber to listen that user
have logged in into app. code for this as below
this.authService.login(user).subscribe(data=>{
if(!data.success){
this.messageClass="alert alert-danger";
this.message=data.message;
}
else{
this.messageClass="alert alert-success";
this.message=data.message;
localStorage.setItem('user_info', JSON.stringify(data.user))
/*for Global level observable fire here*/
this.global_service.loggedInVar = true; //i assume global_service here as your Global service where we declared variables
this.global_service.loggedInObs.next(this.global_service.loggedInVar);
this.authService.storeUserData(data.token,data.user);
setTimeout(()=>{
this.router.navigate(['/profile']);
},2000);
this.flashMessagesService.show('Welcome to bloggy, '+ this.form.get('username').value +' !',{cssClass: 'alert-info'});
}
now you can listen to this using subscriber everywhere in the app like this in your navbar component
userdata = JSON.parse(localStorage.getItem('user_info')); // in case of normal loading page
this.userName = userdata.user_name
this.global_service.loggedInObs.subscribe(res => { // in case of when without refresh of page
console.log('changes here in login');
userdata = JSON.parse(localStorage.getItem('user_info'));
this.userName = userdata.user_name
})
if any doubt let me know.

Categories

Resources