Angular 8中如何通过Input/Output实现组件间点击传值?
Hey there! Let's break down how to polish your current implementation and explore the Input/Output approach for component communication.
First: Refining Your Service-Based Approach
Your existing BehaviorSubject service works perfectly, but we can make it cleaner, more maintainable, and even more efficient:
1. Consolidate State into a Single Object
Instead of two separate BehaviorSubjects for value1 and value2, wrap them into a single state object. This reduces boilerplate and keeps related state grouped:
// myservice.service.ts import { Injectable } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; // Define a type for your auth state (type-safe!) interface AuthState { showAuthLinks: boolean; showLogoutLink: boolean; } @Injectable({ providedIn: 'root' }) export class MyserviceService { // Initial state private authStateSubject = new BehaviorSubject<AuthState>({ showAuthLinks: true, showLogoutLink: false }); // Expose as observable (read-only) authState$: Observable<AuthState> = this.authStateSubject.asObservable(); constructor() { } // Semantic methods instead of generic "changevalue" login() { this.authStateSubject.next({ showAuthLinks: false, showLogoutLink: true }); } logout() { this.authStateSubject.next({ showAuthLinks: true, showLogoutLink: false }); } }
2. Use Async Pipe to Avoid Memory Leaks
In your components, instead of manually subscribing and unsubscribing, use Angular's async pipe. It automatically manages subscriptions and cleans them up when the component is destroyed:
// headcomp.component.ts import { Component } from '@angular/core'; import { MyserviceService } from '../../Service/myservice.service'; @Component({ selector: 'app-headcomp', templateUrl: './headcomp.component.html', styleUrls: ['./headcomp.component.css'] }) export class HeadcompComponent { // Just expose the observable to the template authState$ = this.myapi.authState$; constructor(public myapi: MyserviceService) { } myclick() { this.myapi.login(); } myclick2() { this.myapi.logout(); } }
Update your template to use the async pipe:
<!-- headcomp.component.html --> <div class="row header"> <div class="col-md-8 no_padding"> <div class="logo"> <img src="./assets/images/logo.png" /> </div> </div> <div class="col-md-4 no_padding" style="margin-top: 4px;"> <div class="logo"> <nav> <a routerLink="/loginpage" (click)="myclick2()" *ngIf="(authState$ | async).showAuthLinks" class="mya2">Login</a> <a routerLink="/registerpage" (click)="myclick2()" *ngIf="(authState$ | async).showAuthLinks" class="mya2">Register</a> <a [routerLink]="[{outlets: {dashboard: ['frontpage']}}]" (click)="myclick()" *ngIf="(authState$ | async).showAuthLinks" class="mya2">Other Page</a> <a (click)="myclick2()" *ngIf="(authState$ | async).showLogoutLink" class="mya2">Logout</a> </nav> </div> </div> </div>
3. Simplify Root Component Subscription
If your root component only needs to log the state, use takeUntil to clean up subscriptions safely:
// app.component.ts import { Component, OnInit, OnDestroy } from '@angular/core'; import { MyserviceService } from './Service/myservice.service'; import { takeUntil } from 'rxjs/operators'; import { Subject } from 'rxjs'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit, OnDestroy { title = 'Batchproject1'; private destroy$ = new Subject<void>(); constructor(public myapi: MyserviceService) { } ngOnInit() { this.myapi.authState$ .pipe(takeUntil(this.destroy$)) .subscribe(state => { console.log('App Component', state.showAuthLinks, state.showLogoutLink); }); } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); } }
Second: Using Input/Output for Parent-Child Communication
Input/Output is ideal when you're dealing with direct parent-child component relationships (like your app-root and app-headcomp). Here's how to implement it:
Step 1: Define Output Events in Child Component (headcomp)
We'll use EventEmitter to send events from the child to the parent, and Input to receive state from the parent:
// headcomp.component.ts import { Component, Input, Output, EventEmitter } from '@angular/core'; @Component({ selector: 'app-headcomp', templateUrl: './headcomp.component.html', styleUrls: ['./headcomp.component.css'] }) export class HeadcompComponent { // Receive state from parent @Input() showAuthLinks: boolean = true; @Input() showLogoutLink: boolean = false; // Emit events to parent when buttons are clicked @Output() loginRequested = new EventEmitter<void>(); @Output() logoutRequested = new EventEmitter<void>(); myclick() { this.loginRequested.emit(); } myclick2() { this.logoutRequested.emit(); } }
Step 2: Update Child Template
Remove service references and use the Input properties directly:
<!-- headcomp.component.html --> <div class="row header"> <div class="col-md-8 no_padding"> <div class="logo"> <img src="./assets/images/logo.png" /> </div> </div> <div class="col-md-4 no_padding" style="margin-top: 4px;"> <div class="logo"> <nav> <a routerLink="/loginpage" (click)="logoutRequested.emit()" *ngIf="showAuthLinks" class="mya2">Login</a> <a routerLink="/registerpage" (click)="logoutRequested.emit()" *ngIf="showAuthLinks" class="mya2">Register</a> <a [routerLink]="[{outlets: {dashboard: ['frontpage']}}]" (click)="loginRequested.emit()" *ngIf="showAuthLinks" class="mya2">Other Page</a> <a (click)="logoutRequested.emit()" *ngIf="showLogoutLink" class="mya2">Logout</a> </nav> </div> </div> </div>
Step 3: Handle Events in Parent Component (app-root)
The parent will maintain the state and pass it down to the child via Inputs, while listening for events to update the state:
// app.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'Batchproject1'; showAuthLinks = true; showLogoutLink = false; handleLogin() { this.showAuthLinks = false; this.showLogoutLink = true; console.log('App Component: User logged in', this.showAuthLinks, this.showLogoutLink); } handleLogout() { this.showAuthLinks = true; this.showLogoutLink = false; console.log('App Component: User logged out', this.showAuthLinks, this.showLogoutLink); } }
Update the parent template to bind Inputs and Outputs:
<!-- app.component.html --> <div class="main_wrapper"> <div class="container no_padding"> <app-headcomp [showAuthLinks]="showAuthLinks" [showLogoutLink]="showLogoutLink" (loginRequested)="handleLogin()" (logoutRequested)="handleLogout()" ></app-headcomp> </div> </div>
Which Approach Should You Use?
- Service with BehaviorSubject: Best for communication between components that don't have a direct parent-child relationship (e.g., sibling components, components in different routes) or when multiple components need access to the same state.
- Input/Output: Best for direct parent-child communication where the state is managed by the parent, and the child only needs to send events or receive state from the parent.
Both approaches are valid—choose based on your component hierarchy and state management needs!
内容的提问来源于stack exchange,提问作者Rahul

