The Adapter Pattern: Mapping DTOs with Pure Functions and computed() In short: A pure function turns one DTO into one presentation model. A caches the whole list. Call a mapping method from a template instead and you rebuild every object on every check, which defeats the default you were relying on. We have a presentation model. Now: where does the mapping live, and how do we do it without wrecking rendering performance? The template method trap Here is the version almost everyone writes first. It is readable, it works, and it is slow in a way that does not show up until the list gets long. The user clicks "Toggle sidebar". That changes , which marks this component dirty, so Angular re-evaluates its template. is a template expression, so it runs again — once per row. With 50 deliveries that is 50 executions, for a menu toggle. Worse: each call returns a fresh object literal. receives a new reference, so from its point of view the input changed, and all 50 cards re-render. The default from Chapter 1 gave you the fast path and this pattern hands it straight back. Angular's own guidance is to keep templates simple and avoid complex logic in them; the rule exists for exactly this. The fix: a pure adapter Move the mapping into a plain function, outside any component: A plain exported function, not a class with static methods. It is easier to tree-shake, easier to test, and easier to import one of. Note the is constructed once at module scope. Constructing one per row is a real cost that is easy to miss. Wire it with Now the sidebar toggle re-evaluates the template, reads , and gets the same array with the same object references back, because did not change. Zero mapping runs. Zero cards re-render. 50 deliveries, one sidebar toggle Method in template --- --- --- Mapping executions 50 0 New objects allocated 50 0 re-renders 50 0 Track on something stable One detail in that template deserves attention: . needs a value that is unique and stable across renders . A title is neither guaranteed. Two tankers could share a display string, and a title changes when the data changes, which makes Angular destroy and recreate the row instead of updating it — losing focus and scroll position. Carry the identity through the model: An field is not business context — it is an opaque string the component never interprets. It just needs to exist so tracking and callbacks work. The proof: a second application The farmer portal writes its own adapter, into the same shape: Twenty lines. Same component, completely different backend, no library change, no branch anywhere. The lookups are worth copying as a habit: because the key type comes from the DTO's own union, adding a payment status to the backend makes both maps fail to compile until you decide what it looks like. The compiler will not let you forget a state. Testing adapters Pure functions are the cheapest thing in the codebase to test — no , no rendering: This is where your business rules now get tested — and they are testable precisely because they left the template. "But I thought signals made all of this automatic" This is an easy conclusion to reach after Chapter 1. Signals track dependencies, is the default, Angular only re-checks what changed. So surely the framework handles it? Signals handle when a component is checked . They cannot handle what a template expression does once the check happens . Walk it through. changes, so is marked dirty — that part is signals working perfectly, and only this component is dirty. But being checked means every binding in its template is re-evaluated, and is a binding. Angular has no way to know the function is pure, no way to know its arguments have not changed, and no way to cache a plain method call. So the granularity signals gave you gets thrown away inside the component that was legitimately dirty. is the piece that closes the gap. It is a signal, so Angular knows its dependencies; it caches, so an unchanged dependency means an unchanged result; and it returns the identical reference, so child inputs genuinely have not changed. What it controls What it cannot do --- --- --- Signals + Which components get checked Stop a checked template re-running a method Whether derived work re-runs Nothing — this is the missing half Whether DOM rows are reused Help if the tracked value is unstable The rule that falls out and is worth keeping: a template expression should read state, never derive it. Reading is fine. Calling is derivation, and derivation belongs in a . Cheat sheet Do Don't --- --- A pure exported function per DTO A method on the component to derive the list Call the mapper from the template Construct formatters at module scope One per row a stable from the model a title or for status maps A chain of ternaries Test adapters as plain functions Test business rules through the DOM Enable Rely on spotting it in review Recap A method call in a template runs once per binding per check and returns a new object every time. New references de…