Aman-Mittal opened a new pull request, #185:
URL: https://github.com/apache/fineract-backoffice-ui/pull/185

   Closes #184.
   
   Converts every field `scripts/audit-async-state.mjs` flagged — **429 fields 
across 184
   components** — plus the last decorator-based inputs and outputs. The audit 
now reports **0**, and
   `eslint-suppressions.json` drops from **187 violations to empty**.
   
   These fields were assigned from a `subscribe` callback and read by a 
template. Under Angular 22,
   where `OnPush` is the default strategy, a plain assignment changes the 
rendered value without
   marking the view dirty, so the update lands only if something else happens 
to trigger a check.
   That is the empty-dropdown class of bug, and the source of the `NG0100` 
reports in e2e.
   
   ### Two commits
   
   **1. `refactor(shared)` — finish the shared conversion and unblock dialogs**
   
   The dialogs were the blocker. Ionic delivers `componentProps` with 
`Object.assign` unless
   `useSetInputAPI` is set, which would overwrite an `InputSignal` with the raw 
value and leave the
   template calling a plain object. Enabling it is safe here because 
`DialogService.open` only ever
   passes `data`, and every dialog declares it as an input. 
`provideIonicTesting` sets it too, so a
   broken signal input on a dialog cannot pass in a test and fail in the app.
   
   That surfaced a live bug. `ViewPayloadDialogComponent` derived its text in 
the constructor, where
   an input is not yet populated: the read threw, the `catch` threw again on 
the same undefined
   input, and the error escaped the constructor — so the Audit Logs and Checker 
Inbox "view payload"
   dialog never opened. It is a `computed` now, with the spec the component 
never had.
   
   Also in this commit: `tooltip` (82 consumers), `status-badge` and 
`help-icon` to `input()` /
   `computed()`; `search-filter` and `client-search` to `input()` / `output()`, 
with
   `takeUntilDestroyed` replacing hand-held `Subscription`s and an `effect` 
replacing the last
   `ngOnChanges`; `payment-credit-allocation-editor` to `model()`, since the 
component writes its own
   allocation lists and the parent binds the matching `Change` outputs; 
`donut-chart` to an input
   `transform` in place of the setter-into-private-signal pair.
   
   **2. `refactor` — the bulk pass**
   
   Driven by `scripts/codemod-signals.mjs`, with three follow-ups the codemod 
cannot do itself:
   
   - **57 `[(ngModel)]` bindings split** into `[ngModel]` + `(ngModelChange)`, 
since a signal cannot
     be a two-way target. Where the element already carried a change handler 
the `.set()` is chained
     in front of it rather than duplicating the attribute.
   - **Reads hoisted into locals where a guard used to narrow.** `if 
(this.x())` narrows nothing — a
     second call is a separate expression to the compiler.
   - **Fields with no initialiser given one**, so there was a declaration to 
convert.
   
   The codemod's declaration matcher is fixed too: it excluded `;` from the 
type annotation, so an
   inline object type such as `opts: { id: number; value: string }[]` silently 
failed to match. The
   field was then reported as undeclared and left as a plain field while the 
rest of the file moved
   to signals — a quiet half-conversion. Four components were affected.
   
   With the fields converted, all 147 remaining `prefer-signals` violations 
were the `readonly`
   marker, so the suppressions baseline goes to empty rather than merely 
shrinking.
   
   Specs follow the same change: direct writes become `.set()` or 
`componentRef.setInput`, and reads
   gain their call. A sweep confirmed no un-called signal reads remain in 
assertions — a signal is a
   function, so `expect(component.foo).toBeTruthy()` would otherwise pass no 
matter what the value is.
   
   ### Verification
   
   | Check | Result |
   |---|---|
   | `node scripts/audit-async-state.mjs` | **429 → 0** fields, 184 → 0 
components |
   | `eslint-suppressions.json` | **187 → 0** violations, 76 → 0 files |
   | `npm run lint` / `lint:prune` | clean, nothing suppressed |
   | `tsc --noEmit` (app + spec) | 0 errors |
   | `npm run build` | passes — the only check that type-checks templates |
   | Unit tests | **690 passing** (687 + 3 new) |
   | Mocked Playwright project | **192/192 passing** |
   | `format:check`, `i18n:check`, `check:icons`, `check-license.sh` | all 
clean |
   
   Remaining decorator inputs/outputs outside the generated client: **0**. 
Remaining `ngOnChanges`:
   **0**.
   
   ### Note for reviewers
   
   `useSetInputAPI: true` is a runtime behaviour change for every modal, which 
is why the mocked e2e
   run matters more than usual here — it exercises the confirm, 
datatable-entry, client-action,
   transaction-detail and view-payload dialogs.
   
   This is the pre-condition for `provideZonelessChangeDetection()`: zone.js is 
currently providing a
   second, accidental trigger that was masking these fields, and removing it 
before this landed would
   have broken all 429 at once.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to