简体   繁体   中英

How to Update a Component without refreshing full page - Angular

My Page structure is:

<app-header></app-header>
<router-outlet></router-outlet>
<app-footer></app-footer>

How can I update/refresh the app-header component, without refreshing the whole page?

I want to hide a "Sign-In" link in the header, once the user had successfully logged in. The header is common in all the components/routes.

You can use a BehaviorSubject for communicating between different components throughout the app. You can define a data sharing service containing the BehaviorSubject to which you can subscribe and emit changes.

Define a data sharing service

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable()
export class DataSharingService {
    public isUserLoggedIn: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
}

Add the DataSharingService in your AppModule providers entry.

Next, import the DataSharingService in your <app-header> and in the component where you perform the sign-in operation. In <app-header> subscribe to the changes to isUserLoggedIn subject:

import { DataSharingService } from './data-sharing.service';

export class AppHeaderComponent { 
    // Define a variable to use for showing/hiding the Login button
    isUserLoggedIn: boolean;

    constructor(private dataSharingService: DataSharingService) {

        // Subscribe here, this will automatically update 
        // "isUserLoggedIn" whenever a change to the subject is made.
        this.dataSharingService.isUserLoggedIn.subscribe( value => {
            this.isUserLoggedIn = value;
        });
    }
}

In your <app-header> html template, you need to add the *ngIf condition eg:

<button *ngIf="!isUserLoggedIn">Login</button> 
<button *ngIf="isUserLoggedIn">Sign Out</button>

Finally, you just need to emit the event once the user has logged in eg:

someMethodThatPerformsUserLogin() {
    // Some code 
    // .....
    // After the user has logged in, emit the behavior subject changes.
    this.dataSharingService.isUserLoggedIn.next(true);
}

One of many solutions is to create an @Injectable() class which holds data that you want to show in the header. Other components can also access this class and alter this data, effectively changing the header.

Another option is to set up @Input() variables and @Output() EventEmitters which you can use to alter the header data.

Edit Examples as you requested:

@Injectable()
export class HeaderService {
    private _data;
    set data(value) {
        this._data = value;
    }
    get data() {
        return this._data;
    }
}

in other component:

constructor(private headerService: HeaderService) {}

// Somewhere
this.headerService.data = 'abc';

in header component:

let headerData;

constructor(private headerService: HeaderService) {
    this.headerData = this.headerService.data;
}

I haven't actually tried this. If the get/set doesn't work you can change it to use a Subject();

// Simple Subject() example:
let subject = new Subject();
this.subject.subscribe(response => {
  console.log(response); // Logs 'hello'
});
this.subject.next('hello');

To refresh the component at regular intervals I found this the best method. In the ngOnInit method setTimeOut function

ngOnInit(): void {
  setTimeout(() => { this.ngOnInit() }, 1000 * 10)
}
//10 is the number of seconds

Angular will automatically update a component when it detects a variable change .

So all you have to do for it to "refresh" is ensure that the header has a reference to the new data. This could be via a subscription within header.component.ts or via an @Input variable...


an example...

main.html

<app-header [header-data]="headerData"></app-header>

main.component.ts

public headerData:int = 0;

ngOnInit(){
    setInterval(()=>{this.headerData++;}, 250);
}

header.html

<p>{{data}}</p>

header.ts

@Input('header-data') data;

In the above example, the header will recieve the new data every 250ms and thus update the component.


For more information about Angular's lifecycle hooks, see: https://angular.io/guide/lifecycle-hooks

To update component

 @Injectable()
    export class LoginService{
    private isUserLoggedIn: boolean = false;

    public setLoggedInUser(flag) { // you need set header flag true false from other components on basis of your requirements, header component will be visible as per this flag then
    this.isUserLoggedIn= flag;
    }


public getUserLoggedIn(): boolean {
return this.isUserLoggedIn;
}

Login Component ts
            Login Component{
             constructor(public service: LoginService){}

public login(){
service.setLoggedInUser(true);
}
            }
Inside Header component

 Header Component ts
        HeaderComponent {
         constructor(public service: LoginService){}

         public getUserLoggedIn(): boolean { return this.service.getUserLoggedIn()}
        }

template of header component: Check for user sign in here

<button *ngIf="getUserLoggedIn()">Sign Out</button>
<button *ngIf="!getUserLoggedIn()">Sign In</button>

You can use many approach like show hide using ngIf

App Component ts
AppComponent {
 public showHeader: boolean = true;
}
App Component html
<div *ngIf='showHeader'> // you show hide on basis of this ngIf and header component always get visible with it's lifecycle hook ngOnInit() called all the time when it get visible
<app-header></app-header>
</div>
<router-outlet></router-outlet>
<app-footer></app-footer>

You can also use service

@Injectable()
export class AppService {
private showHeader: boolean = false;

public setHeader(flag) { // you need set header flag true false from other components on basis of your requirements, header component will be visible as per this flag then
this.showHeader = flag;
}

public getHeader(): boolean {
return this.showHeader;
}
}

App Component.ts
    AppComponent {
     constructor(public service: AppService){}
    }

App Component.html
    <div *ngIf='service.showHeader'> // you show hide on basis of this ngIf and header component always get visible with it's lifecycle hook ngOnInit() called all the time when it get visible
    <app-header></app-header>
    </div>
    <router-outlet></router-outlet>
    <app-footer></app-footer>

You just need to notify Angular to update the component with a ChangeDetectorRef . So, inside your header component:

constructor(private cd: ChangeDetectorRef) {}

loggedUserEvent(user: User): void {
  this.username = user.username; 
  this.enableDisconnectButton();

  //... Add more logic to it

  this.cd.detectChanges();
}

That should be enough

All the previous answers work great. But just for some variety, here's a different solution:

Step 1 - Add an Output to the child component.ts file with a new Event Emitter.

Step 2 - Emit the output when your desired action in your child component takes place

Step 3 - Attach the output to your child-selector in your parent component.html

Step 4 - add the necessary function in your parent component to change the data.

The full code will look something like this:

 // --- child.ts --- import { Component, OnInit,Input,Output,EventEmitter } from '@angular/core'; @Output() refreshData = new EventEmitter<{ update: boolean }>(); // note: the 'update' variable isn't needed in the code I've given you, but I've included it in case you want to know how to pass data in the event emitter @Input() myData: boolean | undefined; signInOut(){ this.refreshData.emit({update:true}) } // --- child.html --- <div>Signed in = {{myData}}</div> <button (click) = signInOut()>Click me to sign in or out and refresh the data</button> // --- parent.html --- <child-selector [myData] = "isSignedIn" (refreshData) = dataChange($event)></child-selector> // --- parent.ts --- isSignedIn = false dataChange(eventData: { update: boolean }){ console.log('test') this.isSignedIn =.this.isSignedIn }

Hope this helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM