今日已更新 35 条资讯 | 累计 37284 条内容
关于我们

标签:#signals

找到 4 篇相关文章

AI 资讯

validateHttp() Has No Async Machinery: A Trace From Signal Forms Down to fetch() 🔍🚀

Let's be honest: async validation is the part of any forms library where you brace yourself. Debouncing, cancelling the request the user just invalidated by typing another character, keeping a "checking..." spinner honest, not letting a slow response overwrite a fast one. Every library that has ever done this has grown a pile of bespoke machinery for it. So when Signal Forms shipped validateHttp() and it just worked, I wanted to see the pile. I opened the source expecting a few hundred lines of async bookkeeping, and instead found a function whose entire body is a single call to something else. That turned into a trace all the way down, from a form field to the line where bytes actually leave the browser. Six layers, and only two of them add anything you could call new async machinery. ✅ Availability: validateHttp() is @publicApi 22.0 , stable. Every source reference in this article is pinned to the v22.1.1 tag , so the line numbers stay valid even as main moves. 🧩 The View From Outside The usage is unremarkable, which is the point. You declare that a field validates against an endpoint, and you're done: const schema = form ( this . model , ( path ) => { validateHttp ( path . username , { request : ({ value }) => `/api/username-available?u= ${ value ()} ` , debounce : 300 , onError : () => ({ kind : ' server-unreachable ' }), onSuccess : ( res : { available : boolean }) => res . available ? undefined : { kind : ' username-taken ' }, }); }); Sync validators run first, the request waits until they pass, field().pending() is true while it's in flight, and typing again cancels the previous call. If you've read Part 3 of my Signal Forms series , that's the behaviour contract you already know. The question here is who implements it. 🔍 Layer 1: validateHttp() Is a Delegation Here is the whole function, from validate_http.ts : export function validateHttp ( path , opts ) { validateAsync ( path , { params : opts . request , debounce : opts . debounce , factory : ( request )

2026-08-27 原文 →
开发者

I built a Signals-first toolkit for Angular. Here is the problem I could not stop hitting.

Every Angular application I have worked on in the last few years had the same three kinds of state: URL state — the page number, the active filter, the selected tab. Client state — what the user typed, what is expanded, what is selected. Server state — the thing you fetched, and everything that can go wrong while fetching it. And every application handled them three completely different ways. ActivatedRoute and a Router.navigate call for the first. Signals or a store for the second. A service returning an Observable , plus a loading boolean, plus an error field, plus a subscribe somewhere, for the third. None of that is wrong. It is just that the glue between them is written by hand, in every app, every time. And the glue is where the bugs live. This article is about the specific piece of that problem I could not let go of, and about the toolkit I ended up building around it. It is called craft-ng , it is in beta, and I would genuinely rather have your objections than your stars. The code I kept running into Here is the shape. I should be honest: I did not write much of it myself — I had a drawer of RxJS helpers that hid most of it. But I have read it in a lot of codebases, reviewed it in a lot of pull requests, and inherited it in a lot of projects. That turned out to matter more, because a helper that only I understand is not a solution to anything. @ Injectable () export class TaskListService { private http = inject ( HttpClient ); tasks = signal < Task [] > ([]); isLoading = signal ( false ); error = signal < string | null > ( null ); load ( done : boolean ) { this . isLoading . set ( true ); this . error . set ( null ); this . http . get < Task [] > ( `/api/tasks?done= ${ done } ` ). subscribe ({ next : ( tasks ) => { this . tasks . set ( tasks ); this . isLoading . set ( false ); }, error : ( err ) => { this . error . set ( ' Something went wrong ' ); this . isLoading . set ( false ); }, }); } } Four fields, one method, and roughly six ways to get it subtly wr

2026-08-11 原文 →
AI 资讯

Why I Stopped Writing tap() Inside rxResource Streams

There's a pattern I see a lot in Angular codebases that adopted Signals early: a developer discovers rxResource , loves that it handles loading and error state automatically, and then immediately reaches for tap() to write a signal inside the stream. private readonly resource = rxResource ({ params : () => this . paramsSignal (), stream : ({ params }) => this . api . fetch ( params ). pipe ( tap ( data => this . sideSignal . set ( data . meta )) // 💥 ) }); This looks harmless. It runs in development without complaint in zone-based Angular. Then you enable zoneless — or Angular tightens its reactive graph enforcement — and you get NG0600: Writing to signals is not allowed in a reactive context . The rxResource stream runs inside Angular's reactive scheduler. Signal writes there aren't just discouraged — they're illegal by design. The scheduler assumes computed signals and reactive contexts are read-only during evaluation. A write mid-computation breaks the glitch-free guarantee Angular's signal graph is built on. The fix I landed on: make the stream return everything it needs to return, as a single typed value. interface ResourceValue { readonly sections : Section []; readonly meta : Meta ; } private readonly resource = rxResource < ResourceValue , Params > ({ stream : ({ params }) => this . api . fetch ( params ). pipe ( map ( data => ({ sections : transform ( data ), meta : data . meta })) ) }); No tap . No side signal. Everything the rest of the store needs lives in resource.value() and can be read via computed . The lesson isn't "don't use tap". The lesson is that rxResource has a contract: it is a read primitive . Its stream is for fetching and transforming. If you're writing signals inside it, you're treating it as a command bus — and that's a different tool. Originally published on ysndmr.com .

2026-07-08 原文 →
AI 资讯

Signal Forms vs. Reactive Forms: When Should You Upgrade Your Forms? (Angular 22 Guide)

TL;DR — Angular 22 promoted Signal Forms from experimental to stable. This is not "Reactive Forms are dead." It's a real architectural trade-off, and this post walks through both APIs in full, with production-realistic code, so you can decide feature-by-feature instead of framework-war-by-framework-war. Table of Contents Why This Matters Now The Core Question Reactive Forms: Why It Became the Standard Full Example: Reactive Forms Login Where Reactive Forms Still Excel Signal Forms: What Actually Changed in Angular 22 Full Example: Signal Forms Login Where Signal Forms Shine Side-by-Side: Core Concepts Mapped Deep Dive: Validation Synchronous Validation Cross-Field Validation Conditional Validation with when() Async Validation Deep Dive: Dynamic and Nested Forms Nested Form Groups Dynamic Collections (FormArray-style) Deep Dive: Form State — Dirty, Touched, Errors, Submission Developer Experience and Testing Performance Considerations Interop: Migrating Without a Big-Bang Rewrite Migration Strategy for Enterprise Teams When NOT to Migrate Decision Framework FAQ Closing Thoughts Why This Matters Now With Angular 22 (released June 3, 2026), Signal Forms left experimental status and became part of the stable, supported API — alongside resource() and httpResource() . That's a meaningful milestone: it means the Angular team ran extensive internal case studies across real form-heavy applications at Google before committing to stability, and the interop story with Reactive Forms has matured enough that a big-bang rewrite is no longer the only migration path. At the same time, Angular 22 also flips two important defaults: components now use OnPush change detection by default, and zoneless change detection continues its push toward becoming the standard. Signal Forms is part of that same story — Angular's reactivity model finally speaking one dialect end-to-end, from component state to form state to async data. None of this makes Reactive Forms obsolete. It changes what "the

2026-07-07 原文 →