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

推荐订阅源

小众软件
小众软件
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
T
Troy Hunt's Blog
Cloudbric
Cloudbric
Security Archives - TechRepublic
Security Archives - TechRepublic
WordPress大学
WordPress大学
Recent Commits to openclaw:main
Recent Commits to openclaw:main
W
WeLiveSecurity
GbyAI
GbyAI
V2EX - 技术
V2EX - 技术
Latest news
Latest news
Engineering at Meta
Engineering at Meta
TaoSecurity Blog
TaoSecurity Blog
K
Kaspersky official blog
PCI Perspectives
PCI Perspectives
Hacker News - Newest:
Hacker News - Newest: "LLM"
博客园 - 叶小钗
I
InfoQ
Y
Y Combinator Blog
罗磊的独立博客
大猫的无限游戏
大猫的无限游戏
The Register - Security
The Register - Security
U
Unit 42
F
Full Disclosure
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
D
DataBreaches.Net
博客园 - 聂微东
Recorded Future
Recorded Future
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
S
Secure Thoughts
博客园 - Franky
M
MIT News - Artificial intelligence
爱范儿
爱范儿
N
Netflix TechBlog - Medium
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
S
SegmentFault 最新的问题
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
T
Threat Research - Cisco Blogs
腾讯CDC
S
Securelist
P
Proofpoint News Feed
酷 壳 – CoolShell
酷 壳 – CoolShell
AI
AI
Simon Willison's Weblog
Simon Willison's Weblog
Google Online Security Blog
Google Online Security Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
C
Cybersecurity and Infrastructure Security Agency CISA
Recent Announcements
Recent Announcements
H
Hackread – Cybersecurity News, Data Breaches, AI and More

zodream梦想开源/个人编程日记

文件解析笔记-zodream梦想开源/个人编程日记 密码本开发笔记之读写与保存-zodream梦想开源/个人编程日记 SkiaSharp 把 pixel byte[] 转成 SKBitmap-zodream梦想开源/个人编程日记 nas 使用 Docker 安装 gogs-zodream梦想开源/个人编程日记 复制 android 手机中的文件到电脑-zodream梦想开源/个人编程日记 周报:寻找优质的周刊-zodream梦想开源/个人编程日记 开发日志:对Markdown的代码块新增引用来源支持-zodream梦想开源/个人编程日记 周报:怎么写技术类的教程文章-zodream梦想开源/个人编程日记 css display:flex 布局尺寸超出问题-zodream梦想开源/个人编程日记 周报:SEO优化的思考-zodream梦想开源/个人编程日记 Edge 浏览器不适用 Edge Image Viewer 打开图片 -zodream梦想开源/个人编程日记 SEO 学习笔记(一) 内容来源-zodream梦想开源/个人编程日记 PHP 实现双因素身份认证(2FA)-zodream梦想开源/个人编程日记 WPF MVVM 获取List 多选数据-zodream梦想开源/个人编程日记 Burp Suite 抓包-zodream梦想开源/个人编程日记 使用 indexnow 注意事项-zodream梦想开源/个人编程日记 Godot 使用字体图标 例如: Iconfont、FontAwesome-zodream梦想开源/个人编程日记 angular 15 对指定页面进行访问限制-zodream梦想开源/个人编程日记 CSS 使用 column-count 实现瀑布流出现内容分割的解决办法-zodream梦想开源/个人编程日记 input 确认按键事件在手机端不生效-zodream梦想开源/个人编程日记 C# 使用socket 进行通讯-zodream梦想开源/个人编程日记 Maui开发中Windows应用开启管理员权限-zodream梦想开源/个人编程日记 Maui 中自定义控件-zodream梦想开源/个人编程日记 angular 14 使用 ng-template 实现tree 结构显示-zodream梦想开源/个人编程日记 c# 动态安装和卸载dll-zodream梦想开源/个人编程日记 慎用 CompositionTarget.Rendering-zodream梦想开源/个人编程日记 c# 重写 c++ 程序笔记:数据初始化-zodream梦想开源/个人编程日记 源码编译 aseprite-zodream梦想开源/个人编程日记 记录一下字符串分隔split各语言之间的不同-zodream梦想开源/个人编程日记 c# Gzip解码无头内容-zodream梦想开源/个人编程日记 Windows 10 查看内存占用-zodream梦想开源/个人编程日记 UWP 使用 win2d:加阴影-zodream梦想开源/个人编程日记 清除 PowerShell 历史记录-zodream梦想开源/个人编程日记 c# 重写 c++ 程序笔记:遍历-zodream梦想开源/个人编程日记 Net Core 与 UWP 共用类开发-zodream梦想开源/个人编程日记 hashcat(二)找回rar解压密码-zodream梦想开源/个人编程日记 Godot 学习笔记(一)-zodream梦想开源/个人编程日记 angular 12 显示数学公式-zodream梦想开源/个人编程日记 js 监听按键事件-zodream梦想开源/个人编程日记 angular 12 ng-deep 使用注意事项-zodream梦想开源/个人编程日记 angular 12 动画执行完成事件-zodream梦想开源/个人编程日记 angular 12 中单例 Service 的使用-zodream梦想开源/个人编程日记 js 实现一个正则替换-zodream梦想开源/个人编程日记 uwp win2d 使用-zodream梦想开源/个人编程日记 UWP 读取应用内资源-zodream梦想开源/个人编程日记 gin 使用笔记(二)出错点-zodream梦想开源/个人编程日记 gin 使用笔记(一)基础-zodream梦想开源/个人编程日记 angular 关于自定义组件事件传递-zodream梦想开源/个人编程日记 apache 使用gzip 压缩 js、css-zodream梦想开源/个人编程日记 关于内容中的 @用户 加 话题 的一些想法-zodream梦想开源/个人编程日记 Github Host 更改-zodream梦想开源/个人编程日记 OBS-Studio 等录屏软件录制显示器内容的黑屏的解决方法-zodream梦想开源/个人编程日记 angular 11 ngrx/effects 使用理解-zodream梦想开源/个人编程日记 angular 11 ngrx/store 使用理解-zodream梦想开源/个人编程日记 angular 10 直接获取表单值-zodream梦想开源/个人编程日记 htaccess 搭配 angular 10 放在二级目录-zodream梦想开源/个人编程日记 微信小程序跨页面传值-zodream梦想开源/个人编程日记 js 对 FileList 进行文件过滤上传-zodream梦想开源/个人编程日记 angular自定义表单组件支持 formControlName-zodream梦想开源/个人编程日记 flutter CupertinoPicker 使用不显示-zodream梦想开源/个人编程日记 CC协议-zodream梦想开源/个人编程日记 flutter margin 负值实现-zodream梦想开源/个人编程日记 win10添加删除开机自启项-zodream梦想开源/个人编程日记 微信小程序开发记录(一)真机无法进入页面-zodream梦想开源/个人编程日记 flutter 跳转页面操作上一页-zodream梦想开源/个人编程日记 Regex Generator 使用指南-zodream梦想开源/个人编程日记 go init函数-zodream梦想开源/个人编程日记 angular 9 升级 angular 10-zodream梦想开源/个人编程日记 kotlin AndroidManifest 注意事项-zodream梦想开源/个人编程日记 对于zodream 框架的优化的思考-zodream梦想开源/个人编程日记 flutter 页面滚动条-zodream梦想开源/个人编程日记 flutter swiper 使用-zodream梦想开源/个人编程日记 flutter API请求-zodream梦想开源/个人编程日记 网站体检之xss攻击-zodream梦想开源/个人编程日记 GitHub 下载慢怎么办-zodream梦想开源/个人编程日记 小程序商城开发总结-zodream梦想开源/个人编程日记 gulp-vue2mini 更新日志-zodream梦想开源/个人编程日记 UWP TextBlock 换行-zodream梦想开源/个人编程日记 js 数组排序 sort-zodream梦想开源/个人编程日记 抛弃create.js 改用pixi.js-zodream梦想开源/个人编程日记 后台列表页进阶之路-zodream梦想开源/个人编程日记 局域网文件传输工具-zodream梦想开源/个人编程日记 angular9教程之PullToRefresh-zodream梦想开源/个人编程日记 c# 添加防火墙例外端口-zodream梦想开源/个人编程日记 c# 获取局域网ip-zodream梦想开源/个人编程日记 angular9教程之自定义部件及获取页面元素-zodream梦想开源/个人编程日记 netcore 依赖注入 AddTransient、AddScoped、AddSingleton-zodream梦想开源/个人编程日记 createjs 实现封装 drawImage-zodream梦想开源/个人编程日记 笔记(200521)游戏及玩家分类-zodream梦想开源/个人编程日记 关于 MasterDetail 模式页面收集-zodream梦想开源/个人编程日记 怎么写日记(一)格式规范-zodream梦想开源/个人编程日记 angular9教程之路由-zodream梦想开源/个人编程日记 PHP 常用框架-zodream梦想开源/个人编程日记 angular9教程之监听子路由变化-zodream梦想开源/个人编程日记 angular9教程之添加启动加载动画-zodream梦想开源/个人编程日记 angular9教程之分模块开发-zodream梦想开源/个人编程日记 angular9教程之使用-zodream梦想开源/个人编程日记 create.js 开发小游戏(二)蒙版-zodream梦想开源/个人编程日记 createjs 游戏结束后重新开始 Tween 补间动画无效-zodream梦想开源/个人编程日记 SEO(二)关键词优化试验-zodream梦想开源/个人编程日记
angular 21 升级使用 signals 方案笔记-zodream梦想开源/个人编程日记
zodream · 2025-12-30 · via zodream梦想开源/个人编程日记

angular 21 升级使用 signals 方案笔记

signals 提供新的数据绑定和变更检测机制。从 angular 17 就开始引入了,

优点:

  1. 更精细的响应检测。

缺点:

  1. 变化太大,

signal

基本的绑定,可读可写。

angular 17 之前:

@Component({
    standalone: false,
    selector: 'app-example',
    template: `{{ text }}`,
    styleUrls: ['./example.component.scss']
})
export class ExampleComponent {
    public text = 'hi';

    public tap() {
        this.text = 'hello';
    }
}

12345678910111213

angular 17 之后:

@Component({
    standalone: false,
    selector: 'app-example',
    template: `{{ text() }}`,
    styleUrls: ['./example.component.scss']
})
export class ExampleComponent {
    public readonly text = signal<string>('hi');

    public tap() {
        this.text.set('hello');
        this.text.update(v => {
            return 'hello';
        });
    }
}

12345678910111213141516

注意

  1. 使用 setupdate 更新值。
  2. 当值是对象时,使用 update 更新某个属性,必须生成新的对象,才能有效检测
    
    public readonly data = signal({
     a: 1,
     b: 2
    });
    // 错误示范
    this.data.update(v => {
     v.a = 3
     return v;
    });

    12345678910

// 正确使用 this.data.update(v => { v.a = 3; return {...v}; // 或者 return {...v, a: 3}; });

3. 当值时数组时,使用 `update` 新增或删除某一项
```ts
public readonly items = signal<number[]>([]);
// 错误示范
this.items.update(v => {
    v.push(1);
    v.pop();
    return v;
});

// 正确使用
this.items.update(v => {
    v.push(1);
    return [...v];
    // 或者
    return [...v, 1];
});

1234567891011121314151617

input、output、model

input 相当于 @Input(), 但是可读不可写,增加了自定义转换

output 相当于 @Output()

model 相对于 @Input() + @Output(),可读可写

angular 17 之前:

@Component({
    standalone: false,
    selector: 'app-example',
    template: `{{ text }}`,
    styleUrls: ['./example.component.scss']
})
export class ExampleComponent {
    @Input() public text = 'hi';
    @Output() public textChange = new EventEmitter();
}

12345678910

angular 17 之后:

@Component({
    standalone: false,
    selector: 'app-example',
    template: `{{ text() }}`,
    styleUrls: ['./example.component.scss']
})
export class ExampleComponent {
    public readonly text = input('hi', {tranform: parseInt});
    public readonly textChange = output();

    public readonly text = model('');

}

12345678910111213

form

@Component({
    standalone: false,
    selector: 'app-example',
    template: `<input type="number" [formField]="form.a">`,
    styleUrls: ['./example.component.scss']
})
export class ExampleComponent {
    public readonly form = form(sinal({
        a: 1
    }), schemaPath => {
        required(schemaPath.a);
    })

}

123456789101112131415

使用 [formField] 精选表单绑定,不能自定义 name 属性,

注意

  1. 表单 name 属性自动生成 类似于 [项目名].form1.a,如果介意 name,则推荐使用 原本 FormBuilder 方式
  2. 一些值限制的变化, select 的值为 stringinput type=checkboxboolean, type=numbernumber, 其他则必须为 string

effect

检测 值的变化,替代 ngOnChanges

@Component({
    standalone: false,
    selector: 'app-example',
    template: `{{ text() }}`,
    styleUrls: ['./example.component.scss']
})
export class ExampleComponent {
    public readonly text = model('');

    constructor() {
        let previousText = '';
        effect(() => {
            this.text();
            // 当 text 发生变化时,TODO
            previousText = this.text(); // 使用此方法实现值的前后变化检测
        });
    }
}

123456789101112131415161718

computed

关联变化,提供值变化前后对比

angular 17 之前:

@Component({
    standalone: false,
    selector: 'app-example',
    template: `{{ text }} {{ twoText }}`,
    styleUrls: ['./example.component.scss']
})
export class ExampleComponent {
    public text = 'hi';

    public get twoText() {
        return this.text + ', two';
    }

    public tap() {
        this.text = 'hello';
    }
}

1234567891011121314151617

angular 17 之后:

@Component({
    standalone: false,
    selector: 'app-example',
    template: `{{ text() }} {{ twoText() }}`,
    styleUrls: ['./example.component.scss']
})
export class ExampleComponent {
    public readonly text = signal<string>('hi');

    public readonly twoText = computed(() => {
        return this.text() + ', two';
    });

    public tap() {
        this.text.set('hello');
        this.text.update(v => {
            return 'hello';
        });
    }
}

1234567891011121314151617181920

与 signals 使用时失效的旧方法

@HostBinding 无法与 signal 等使用

@HostBinding('class.open') // 无效
public readonly toggle = model(false);

12

可以使用 effect 同步或


@Component({
    standalone: false,
    selector: 'app-example',
    template: ``,
    styleUrls: ['./example.component.scss'],
    host: {
        '[class.open]': 'toggle()'
    }
})
export class ExampleComponent {
    public readonly toggle = model(false);


}

123456789101112131415

转载请保留原文链接: https://zodream.cn/blog/id/270.html