Refactor: Modernize budget viewer state with Signals#172
Open
ahmednule wants to merge 8 commits intoitalanta:mainfrom
Open
Refactor: Modernize budget viewer state with Signals#172ahmednule wants to merge 8 commits intoitalanta:mainfrom
ahmednule wants to merge 8 commits intoitalanta:mainfrom
Conversation
…e the apps/kujali/src/app with the 2 files as specified in the readme.md
….ts and environment.prod.ts left them as excecuatble data only such that I wont get errors during run time
Removed constructor-based DI and switched to inject() Converted observable streams (overview$, sharedBudgets$, allBudgets$) into Angular signals and updated component logic accordingly Replaced manual subscriptions with a declarative signals approach using effect() and computed() Updated template to pass signals correctly to the child component
Updated inputs to accept signals instead of observables Removed subscription logic and replaced it with signal-reactive patterns
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
How I Approached the Assignment
I began by analyzing the existing RxJS-based implementation in both SelectBudgetPageComponent (parent) and BudgetTableComponent (child). I identified all observable streams (overview$, sharedBudgets$, allBudgets$) and subscription logic (including SubSink and manual subscribe() calls). Then I:
Replaced constructor-based dependency injection with the modern inject() function.
Converted Observables to Signals using toSignal() from @angular/core/rxjs-interop.
Replaced imperative subscription side effects with declarative effect() blocks.
Used computed() to derive transformed state (e.g., adding endYear to budgets).
Updated the child component to accept plain data inputs instead of Observables, removing all subscription logic.
Modernized the template with @if control flow instead of *ngIf for better signals integration.
The refactor preserved all original functionality while aligning with Angular’s reactive future.
Key Benefits of Signals Over RxJS in This Scenario
Simpler mental model: No need to manage subscriptions, unsubscribing, or async pipes.
Automatic cleanup: Signals and effects clean up automatically when components are destroyed, zero risk of memory leaks.
Fine-grained reactivity: Only the exact parts of the UI that depend on changed signals are updated.
Better performance: Enables zoneless change detection, reducing unnecessary checks.
Type safety & readability: Signals are just functions that return values, no chaining operators or stream composition needed for simple state.
In this UI state management use case (loading and displaying budget lists), Signals are overkill for RxJS, RxJS shines in complex event streams (e.g., autocomplete with debouncing), but is excessive for static or slowly-changing data.
Difference in Coding Styles
RxJS (Reactive/Imperative):
You push data through streams, chain operators (map, switchMap), and imperatively subscribe. You must manually manage lifecycle and cleanup.
Signals (Reactive/Declarative):
You pull values from reactive primitives. State is derived declaratively (computed), and side effects are declared once (effect). Angular handles reactivity automatically.
RxJS = “Tell me when something happens, and I’ll react.”
Signals = “This value changes, automatically update anything that uses it.”
How Signals and Observables Work, Fundamental Difference
Observables (RxJS) are push-based:
They emit values over time to multiple observers. You must subscribe to receive data. They’re lazy, multi-cast (by default), and great for events, HTTP streams, or user input.
Signals (Angular) are pull-based:
They hold a current value that can be read at any time. Consumers pull the value, and Angular tracks dependencies automatically. They’re synchronous, single-value (though they can change), and optimized for component state.
Fundamental difference:
Observables are event streams; Signals are reactive values.
They solve different problems, and can coexist (via toSignal() / toObservable()).
Another Paradigm I’ve Used: React + Redux (with Hooks)
In past projects, I used React with Redux Toolkit + useSelector/useDispatch.
Better:
Centralized store made global state predictable. Time-travel debugging was powerful. Middleware (e.g., for logging or async) was clean.
Worse:
Boilerplate-heavy (actions, reducers, types). Overkill for local component state. Required explicit memoization (useMemo, useCallback) to avoid re-renders.
Compared to Angular Signals:
Signals are more granular, no global store needed for local UI state.
Less boilerplate, no action creators or reducers.
Automatic dependency tracking vs manual selector memoization.
Redux is great for complex cross-cutting state; Signals excel at local and derived component state, which is exactly what this budget table needed.