You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular CLI 15中如何通过路由传递isAdmin变量至子组件

Angular 15 组件间传递isAdmin状态的解决方案

问题场景

我有一个使用Angular CLI 15的学校项目,需要将AppComponent中的isAdmin变量传递给TipsListComponent。AppComponent始终处于显示状态,默认路由会在AppComponent内部的<router-outlet>中展示TipsListComponent。需求是TipsListComponent表格里的删除按钮仅在isAdmin为true时显示,尝试过直接导入变量和路由参数,但都没解决问题。

现有代码

app.component.ts

import { Component } from '@angular/core';
import { Router } from '@angular/router';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  public isAdmin = true;
  public isConnected = false;
  title = 'Terraria';
}

app.component.html

<mat-toolbar>
  <div id="centered">
    <img src="../assets/logo.png" id="logo" routerLink="/" />
  </div>

  <div id="right_nav">
    <button
      [hidden]="isConnected"
      class="logButton"
      mat-flat-button
      color="grey"
      style="background-color: #666b6f; color: white"
      routerLink="/login"
    >
      Se connecter
    </button>
    <button
      [hidden]="isConnected"
      class="logButton"
      mat-flat-button
      color="grey"
      style="margin-left: 4px; background-color: #666b6f; color: white"
      routerLinkActive="activebutton"
      routerLink="/register"
    >
      S'enregistrer
    </button>

    <button
      [hidden]="!isAdmin"
      class="logButton"
      mat-flat-button
      color="grey"
      style="margin-left: 4px; background-color: #666b6f; color: white"
      matTooltip="Outils d'administration"
    >
      <mat-icon>build</mat-icon>
    </button>

    <button
      [hidden]="!isConnected"
      class="logButton"
      mat-flat-button
      color="grey"
      style="margin-left: 4px; background-color: #666b6f; color: white"
      (click)="isConnected = !isConnected"
      matTooltip="Se déconnecter"
    >
      <mat-icon>exit_to_app</mat-icon>
    </button>
  </div>
</mat-toolbar>
<div id="sapcing" style="height: 85px"></div>
<div id="body">
  <router-outlet></router-outlet>
</div>

tips-list.component.ts

import { Component, Input } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Sort } from '@angular/material/sort';
@Component({
  selector: 'app-tips-list',
  templateUrl: './tips-list.component.html',
  styleUrls: ['./tips-list.component.css'],
})
export class TipsListComponent {
  searchTitle = '';
  searchAuthor = '';
  searchTag = '';
  disableThirdHeader = false;
  tips = [   
    { name: 'Frozen yogurt', tags: '159', author: 'admin' },
    {name: 'Ice cream sandwich',tags: '237',author: 'admin',},
    { name: 'Eclair', tags: '262', author: 'admin' },
    { name: 'Cupcake', tags: '305', author: 'admin' },
    { name: 'Gingerbread', tags: '356', author: 'user' },
  ];

  sortedData = this.tips.slice();

  sortData(sort: Sort) {
    const data = this.tips.slice();

    if (!sort.active || sort.direction === '') {
      this.sortedData = data;
    } else {
      this.sortedData = data.sort((a, b) => {
        const aValue = (a as any)[sort.active];
        const bValue = (b as any)[sort.active];
        return (aValue < bValue ? -1 : 1) * (sort.direction === 'asc' ? 1 : -1);
      });
    }
  }
}

tips-list.component.html

<mat-form-field class="example-full-width">
  <mat-label>Rechercher titre</mat-label>
  <input
    matInput
    placeholder="Ex : Œil de cthulhu"
    [(ngModel)]="searchTitle"
    autocomplete="off"
  />
</mat-form-field>
<mat-form-field class="example-full-width">
  <mat-label>Rechercher tag</mat-label>
  <input
    matInput
    placeholder="Ex : Œil de cthulhu"
    [(ngModel)]="searchTag"
    autocomplete="off"
  />
</mat-form-field>

<mat-form-field class="example-full-width">
  <mat-label>Rechercher auteur</mat-label>
  <input
    matInput
    placeholder="Ex : Œil de cthulhu"
    [(ngModel)]="searchAuthor"
    autocomplete="off"
  />
</mat-form-field>

<table matSort (matSortChange)="sortData($event)">
  <tr>
    <th mat-sort-header="name">Titre</th>
    <th mat-sort-header="tags">Tags</th>
    <th mat-sort-header="author">Auteur</th>
  </tr>

  <tr
    *ngFor="
      let tip of sortedData
        | filter : searchTitle
        | filterTags : searchTag
        | filterAuthor : searchAuthor
    "
  >
    <td>{{ tip.name }}</td>
    <td>{{ tip.tags }}</td>
    <td>{{ tip.author }}</td>
    <button mat-icon-button>
      <mat-icon color="warn" *ngIf="isAdmin">delete</mat-icon>
    </button>
  </tr>
</table>

解决方案

方法一:通过<router-outlet>传递输入属性

Angular 15及以上支持通过<router-outlet>的[outletContext]直接向路由加载的组件传值,步骤简单直接:

  1. 修改AppComponent模板
    给<router-outlet>绑定outletContext,把isAdmin传进去:
<router-outlet [outletContext]="{ isAdmin: isAdmin }"></router-outlet>
  1. 在TipsListComponent接收输入
    在组件类里添加@Input()声明isAdmin变量:
import { Component, Input } from '@angular/core';
// ... 其他导入

export class TipsListComponent {
  @Input() isAdmin!: boolean; // 新增这一行
  // ... 其他现有代码
}

这样模板里的*ngIf="isAdmin"就能正常生效了。

方法二:使用共享服务(适合全局状态共享)

如果后续isAdmin需要在多个组件中同步变化,推荐用共享服务:

  1. 创建AuthService
    先生成服务文件:
ng generate service auth

然后在auth.service.ts里定义状态容器:

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

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private isAdminSubject = new BehaviorSubject<boolean>(true);
  public isAdmin$ = this.isAdminSubject.asObservable();

  setIsAdmin(value: boolean) {
    this.isAdminSubject.next(value);
  }
}
  1. 在AppComponent同步状态到服务
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from './auth.service'; // 导入服务

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  public isAdmin = true;
  public isConnected = false;
  title = 'Terraria';

  constructor(private authService: AuthService) {
    // 把初始状态同步到服务
    this.authService.setIsAdmin(this.isAdmin);
  }

  // 后续如果isAdmin变化,调用这个方法更新服务状态
  // updateAdminStatus(value: boolean) {
  //   this.isAdmin = value;
  //   this.authService.setIsAdmin(value);
  // }
}
  1. 在TipsListComponent订阅状态
import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Sort } from '@angular/material/sort';
import { AuthService } from '../auth.service'; // 导入服务
import { Observable } from 'rxjs';

@Component({
  selector: 'app-tips-list',
  templateUrl: './tips-list.component.html',
  styleUrls: ['./tips-list.component.css'],
})
export class TipsListComponent implements OnInit {
  searchTitle = '';
  searchAuthor = '';
  searchTag = '';
  disableThirdHeader = false;
  isAdmin$!: Observable<boolean>; // 声明可观察对象
  tips = [   
    // ... 现有数据
  ];

  sortedData = this.tips.slice();

  constructor(private authService: AuthService) {}

  ngOnInit() {
    this.isAdmin$ = this.authService.isAdmin$; // 订阅状态
  }

  // ... 现有sortData方法
}
  1. 修改TipsListComponent模板
    用async管道订阅可观察对象:
<button mat-icon-button>
  <mat-icon color="warn" *ngIf="isAdmin$ | async">delete</mat-icon>
</button>

为什么之前的方法不行?

  • 直接导入AppComponent变量:组件实例是独立的,导入类根本拿不到运行时的实例变量值。
  • 路由参数:适合传静态或一次性数据,没法处理动态变化的状态(比如登录后切换管理员权限),而且需要在路由配置里定义,灵活性差。

内容的提问来源于stack exchange,提问作者Kazeno

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 13:15:51