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]