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

Angular 2:如何让指令订阅服务Observable实现自动更新

让Angular指令订阅服务Observable实现自动视图更新

这种场景我太熟了——想让权限指令完全自主响应服务的权限变化,不用组件每次手动传值,确实是解耦的好思路。下面一步步给你实现:

1. 先搭建你的权限服务

首先得有一个能推送权限更新的服务,这里用BehaviorSubject最合适,因为它能保存当前值,新订阅的观察者也能拿到最新的权限状态:

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

@Injectable({ providedIn: 'root' })
export class PermissionService {
  // 私有Subject,内部用来更新权限
  private _currentPermissions$ = new BehaviorSubject<string[]>([]);
  // 公开的Observable,供外部订阅(只能读,不能写)
  public currentPermissions$: Observable<string[]> = this._currentPermissions$.asObservable();

  // 更新权限的方法,在任意组件/服务里调用这个方法就能推送新权限
  updatePermissions(permissions: string[]) {
    this._currentPermissions$.next(permissions);
  }
}

2. 实现权限指令

接下来写指令,核心就是注入权限服务,订阅它的currentPermissions$流,每次收到新权限就更新视图:

import { Directive, ElementRef, OnInit, OnDestroy } from '@angular/core';
import { PermissionService } from './permission.service';
import { Subscription } from 'rxjs';

@Directive({
  selector: '[appPermission]',
  // 可以接收需要校验的权限,比如 <div appPermission="edit">...</div>
  inputs: ['requiredPermission: appPermission']
})
export class PermissionDirective implements OnInit, OnDestroy {
  requiredPermission: string;
  private permissionSubscription: Subscription;

  constructor(
    private el: ElementRef,
    private permissionService: PermissionService
  ) {}

  ngOnInit() {
    // 订阅权限变化流
    this.permissionSubscription = this.permissionService.currentPermissions$.subscribe(permissions => {
      this.updateElementVisibility(permissions);
    });
  }

  private updateElementVisibility(permissions: string[]) {
    // 判断当前元素是否拥有所需权限,控制显示/隐藏
    const hasPermission = permissions.includes(this.requiredPermission);
    this.el.nativeElement.style.display = hasPermission ? 'block' : 'none';
    // 也可以添加/移除CSS类,比如:
    // if (hasPermission) {
    //   this.el.nativeElement.classList.remove('hidden');
    // } else {
    //   this.el.nativeElement.classList.add('hidden');
    // }
  }

  ngOnDestroy() {
    // 一定要取消订阅,防止内存泄漏!
    if (this.permissionSubscription) {
      this.permissionSubscription.unsubscribe();
    }
  }
}

3. 怎么用?

在模板里直接用指令,不用传动态输入(除非你需要指定单个权限):

<!-- 需要edit权限才显示 -->
<button appPermission="edit">编辑</button>
<!-- 需要delete权限才显示 -->
<button appPermission="delete">删除</button>

然后在任意地方调用服务的updatePermissions方法,比如登录后获取权限:

import { Component } from '@angular/core';
import { PermissionService } from './permission.service';

@Component({
  selector: 'app-login',
  template: '<button (click)="login()">登录</button>'
})
export class LoginComponent {
  constructor(private permissionService: PermissionService) {}

  login() {
    // 模拟登录后获取权限
    const userPermissions = ['edit', 'view'];
    this.permissionService.updatePermissions(userPermissions);
    // 此时所有带appPermission指令的元素都会自动更新!
  }
}

额外优化:用async pipe简化订阅(可选)

如果你的指令是结构型的,也可以用async pipe自动处理订阅和取消订阅,避免手动管理Subscription:

// 结构型指令版本
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
import { PermissionService } from './permission.service';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Directive({
  selector: '[appPermission]'
})
export class PermissionStructuralDirective {
  @Input() set appPermission(requiredPermission: string) {
    const hasPermission$: Observable<boolean> = this.permissionService.currentPermissions$.pipe(
      map(permissions => permissions.includes(requiredPermission))
    );

    hasPermission$.subscribe(hasPermission => {
      if (hasPermission) {
        this.vcRef.createEmbeddedView(this.templateRef);
      } else {
        this.vcRef.clear();
      }
    });
  }

  constructor(
    private templateRef: TemplateRef<any>,
    private vcRef: ViewContainerRef,
    private permissionService: PermissionService
  ) {}
}

用的时候像这样:

<div *appPermission="'edit'">只有拥有edit权限才会显示这个区块</div>

这样一来,权限服务更新时,所有使用该指令的视图都会自动同步,完全不需要组件手动传递数据,指令自己就会“监听”服务的变化并做出响应。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 08:24:34