Oct 25

Use C# 14 extension properties only where your own code reads them

C# 14 lets you add a property to a type you don't own, and at the call site it looks like a real one. It compiles to a static method, so EF Core can't use it in a query (the compiler refuses it with CS9296), System.Text.Json doesn't write it, and reflection on the type doesn't list it. Keep them for reads in your own code, and give queries an expression instead.

dotnet · patterns · 5 min
→
Oct 19

An Angular upload progress bar that shows the server is still working

An upload bar that says 100% and then sits still while the server works looks frozen. Switch to a processing state when the last byte leaves the browser, then draw it however you like: six designs on one signal state.

angular · patterns · 4 min
→
Oct 18

Return a C# 15 union type instead of a Result class

C# 15 adds union types: a closed set of case types, implicit conversions from each one, and a switch the compiler checks for missing cases. For a service method with a few outcomes it reads better than a hand-written Result class. Keep it out of JSON for now: RC1 writes no case marker, and a plain read can't tell two object cases apart.

dotnet · patterns · 5 min
→
Oct 06

How to validate conditional fields in Angular Signal Forms

Signal Forms skip the validators on hidden and disabled fields, so a form with a conditional section can report valid() while a required field in the model is empty, or still holds an address the user backed out of. Build the object you send from the form state and leave the hidden sections out of it.

angular · patterns · 5 min
→
Sep 30

Deterministic cleanup in TypeScript with await using and DisposableStack

using and await using close a resource when the block ends, whether it ends normally or by throwing, and they close several resources in reverse order of opening. Three nested try/finally blocks become three lines, DisposableStack covers the case where the count is only known at run time, and a cleanup that throws gives you a SuppressedError whose two fields are easy to read backwards.

typescript · patterns · 7 min
→
Sep 21

Wrap Tom Select in an Angular signal form without the change-event loop

Angular 22 asks a custom form control for one thing, a value model signal. Wrapping Tom Select in that contract takes about twenty lines, and then the widget starts answering the writes your own code makes. The change event fires for programmatic writes and user picks alike, silent suppresses it, and item_add is not covered by silent at all.

angular · patterns · 7 min
→
Sep 13

linkedSignal: a derived value you can also write to

A computed signal is read-only, so a derived value the user can also change gets copied into a plain signal with an effect to reset it. linkedSignal is the writable one, and the rule for when your write survives is worth knowing exactly: it stands until something the computation reads changes, and it is dropped the moment that happens, which you observe on the next read.

angular · patterns · 6 min
→
Sep 01

Use satisfies to check a config object without widening its types

Annotating a config object checks it and then replaces what you wrote with the type you checked against. satisfies runs the same check and leaves your types alone — so ink is still a string, the colour is still a tuple, and a typo in a key is still an error.

typescript · patterns · 5 min
→
Aug 29

What track $index actually costs in Angular's @for

track $index is usually described as the slow option. Measured, it is often the cheap one — a prepend costs it the same single DOM insertion, and a shuffle costs it zero structural DOM work. What it costs is correctness: every row's half-typed input and component state slides onto the row next door.

angular · patterns · 5 min
→
Aug 11

Replace isLoading, error and data with one discriminated union

isLoading, error, data — three flags, eight combinations, half of them lies. Discriminated unions make the impossible states impossible, and the compiler enforces it.

angular · patterns · 5 min
→