惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 【当耐特】
云风的 BLOG
云风的 BLOG
罗磊的独立博客
C
Check Point Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Blog — PlanetScale
Blog — PlanetScale
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
月光博客
月光博客
大猫的无限游戏
大猫的无限游戏
Google DeepMind News
Google DeepMind News
Engineering at Meta
Engineering at Meta
N
Netflix TechBlog - Medium
宝玉的分享
宝玉的分享
Recent Announcements
Recent Announcements
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园_首页
J
Java Code Geeks
Apple Machine Learning Research
Apple Machine Learning Research
人人都是产品经理
人人都是产品经理
爱范儿
爱范儿
I
InfoQ
Hugging Face - Blog
Hugging Face - Blog
T
Tailwind CSS Blog
B
Blog RSS Feed

博客园 - dudu

test .NET CQRS 的实现中引入 ReadOnlyRepository 初试 .NET CQRS 开源库 LiteBus 什么是 Agentic ? 初试 Microsoft Agent Framework 初识 Microsoft Agent Framework:一句话介绍 ASP.NET Core 中读取 UserAgent 的正确姿势 记录一下对 ASP.NET Core Middleware 进行单元测试的代码 C# 实现通用的 IdEqualityComparer 量子网络操作系统 QNodeOS 资料收集 Kubernetes 集群上部署 Open WebUI 在 Kubernetes 集群的 GPU 节点上部署 Ollama 尝试在 Kubernetes 集群上用阿里云 GPU 实例部署 Ollama + DeekSeek-R1 阿里云 GPU 实例云服务器本地部署 DeepSeek R1 尝试使用阿里云计算巢部署 DeepSeek-R1 Angular 中依赖注入问题造成 Observable 订阅不更新 园子博客后台 Angular 升级:手工迁移至 Standalone Component Angular 中使用 ChildContent 记录 园子博客后台升级至 angular 19 后 eslint 9 迁移记录 学习大模型(LLM)的英文好文收集
用 Angular Signal Inputs 完成一个组件的重构
dudu · 2025-05-18 · via 博客园 - dudu

昨天,园子的博客后台终于完成了前端框架升级,从 angular 15 升级到了 angular 19。Angular 从版本 17 开始了文艺复兴(renaissance)之路, 变化很大,这下可以在开发中使用 angular 的新特性了。

这篇博文记录一下昨天晚上用 angular 17 引入的新特性 signal inputs 完成了一个组件的重构。

重构前的组件模版文件

<div class="cnb-panel-header"
     [class.cnb-panel-header-clickable]="headerClickable"
     (click)="headerClickable ? toggleCollapsed() : undefined">    
    <span>{{name}}</span>
    <div class="cnb-panel-header-extra" *ngIf="headerExtra">
        <ng-container *ngTemplateOutlet="headerExtra; context: headerExtraContext ?? {}"></ng-container>
    </div>
</div>
<div class="panel-content"
     [ngClass]="collapsed ? '' : panelContentClass"
     [@openClosePanel]="collapsed ? 'close' : 'open'">
    <ng-content></ng-content>
</div>

重构前的组件类文件

export class CollapsePanelComponent implements OnInit {  
    @Input() name = '';
    @Input() initCollapsed = false;
    @Input() toggleIcon?: TemplateRef<any>;
    @Input() headerClickable = false;
    @Output() collapsedChange = new EventEmitter<boolean>(false);
    @Input() headerExtra: TemplateRef<any> | null = null;
    @Input() headerExtraContext?: ({ $implicit?: any } & Record<string, any>) | null;
    @Input() panelContentClass: string[] = [];
    collapsed = false;

    constructor() { }
    
    toggleCollapsed(collapsed?: boolean) {
        const cache = this.collapsed;
        this.collapsed = collapsed !== undefined ? collapsed : !this.collapsed;
        if (cache !== this.collapsed) this.collapsedChange.next(this.collapsed);
    }

    ngOnInit() {
        this.collapsed = this.initCollapsed || false;
    }
}

重构后的组件模版文件,只是把 collapsed 改成了 collapsed()

重构后的组件类文件

export class CollapsePanelComponent {  
    @Input() name = '';
    @Input() toggleIcon?: TemplateRef<any>;
    @Input() headerClickable = false;
    @Output() collapsedChange = new EventEmitter<boolean>(false);
    @Input() headerExtra: TemplateRef<any> | null = null;
    @Input() headerExtraContext?: ({ $implicit?: any } & Record<string, any>) | null;
    @Input() panelContentClass: string[] = [];
    collapsed = model(false);

    constructor() {
        effect(() => {
            this.collapsedChange.next(this.collapsed());
        });
    }

    toggleCollapsed() {
        this.collapsed.update(x => !x);
    }
}

这里使用 mode 没有使用 input, 是因为组件内部需要更新 collapsed 的值,如果没有这个需要,都由父组件传值,那就用 input

重构中也用到了 effect,只需要在 toggleCollapsed 方法中更新 collapsed 的值,将其他逻辑移到了 effect 的回调函数中处理。

参考: