chore(ui): migrate the remaining templates to the built-in control flow (schematic)

Merged
EV
14d ago
+3832-3269

Summary

This PR runs Angular's own control-flow schematic (@angular/core:control-flow, Angular 22.2.2) on the four places that still had *ngIf / *ngFor / *ngSwitch templates. Every template change comes from the schematic. Prettier was then run on the files it touched, so the re-indented blocks use the repo's tabs. Signal inputs/outputs/queries and inject() are not touched, as the owner asked.

Part of #9552 (the issue stays open for the manual signal migrations).

What changed, by path (one commit each)

PathCommandFiles keptExcluded
packages/plugins/docs-uinx g @angular/core:control-flow --path=packages/plugins/docs-ui56 of 57components/detail/docs-detail-panel.component.html (see below)
packages/plugins/legal-ui... --path=packages/plugins/legal-ui2 of 2none
packages/ui-core/shared... --path=packages/ui-core/shared4 of 4none
apps/gauzy... --path=apps/gauzy2 of 3pages/goals/goals.component.ts (see below)

All runs used the default options (--format=true). The schematic also removed CommonModule from standalone components whose templates no longer use anything from it: 15 components and one spec test host in docs-ui, plus EmailVerificationNoticeComponent in ui-core/shared. The strict template check below confirms that nothing they still use came from CommonModule. Nothing was removed by hand.

One small hand-written follow-up commit, test(docs-ui): read the thumbnail bindings in the @if syntax, is kept separate from the generated commits. Two layout specs read the component source and looked for the old *ngIf ... as thumbnail string, so they now look for the @if (...; as thumbnail) form. What they check has not changed: the image binds the sanitizing accessor, never the raw thumbUrl.

Templates left as they were (the schematic got them wrong)

  • packages/plugins/docs-ui/src/lib/components/detail/docs-detail-panel.component.html: the schematic moved <ng-template #taxonomyReadonly> into an @else block and deleted it. The surrounding *ngxPermissionsOnly="docsPermissions.update; else taxonomyReadonly" still points at that template, so ngc fails with TS2339 (taxonomyReadonly does not exist on the component). This file keeps its 40 lines of *ngIf / *ngFor. It needs a small manual migration that keeps the template for ngxPermissionsOnly.
  • apps/gauzy/src/app/pages/goals/goals.component.ts: its template was already migrated. The schematic rewrote the imports array only to drop CommonModule. The rewrite also deleted the `[nbPopover]` drives the Add Objective / Group By / Filter menus comment inside the array and re-indented it with spaces. That file was not one of the two templates in scope and the rewrite loses a comment, so it is unchanged.

No template was refused outright: the schematic reported no migration errors on any path.

Overlap with open PRs

None of the changed files is touched by #11588, #11590 or #11592. #11588 changes packages/ui-core/shared/src/lib/contact-selector/contact-selector.component.ts, but this PR does not touch that component: its only *ngFor is inside an HTML comment, and the schematic skips it.

Before / after

git grep -c -E '\*ng(If|For|Switch)', lines per path:

PathBeforeAfterWhat is left
packages/plugins/docs-ui389 lines in 63 files66 lines in 19 files40 lines are docs-detail-panel.component.html (excluded above). The other 26 are comments, JSDoc and spec text that mention the old directives, left as written (for example menu-editor-rebinding.spec.ts still says "under *ngIf", and its host template now uses @if; the test still passes).
packages/plugins/legal-ui7 lines in 2 files0none
packages/ui-core/shared43 lines in 7 files4 lines in 4 filesAll four are comments (one is the commented-out <nb-select> in contact-selector).
apps/gauzy16 lines in 2 files0none

ESLint on every changed file, compared with the base commit: @angular-eslint/template/prefer-control-flow goes from 308 to 0. Every other rule has the same count before and after (the existing prefer-inject, prefer-standalone, a11y and no-restricted-imports findings are not part of this change).

Tests and type-checks

All run locally on Node 26.10.0 in this branch.

  • docs-ui jest: npx jest -c packages/plugins/docs-ui/jest.config.ts --rootDir packages/plugins/docs-ui: 45 suites, 613 tests passed. Before the spec follow-up, the two source-string assertions above were the only failures.
  • docs-ui templates (strictTemplates): ngc -p on packages/plugins/docs-ui/tsconfig.lib.json with noEmit, plus types: ["node", "google.maps"] because the path-mapped ui-core sources need those types. Result: 0 diagnostics in docs-ui. This check is what caught the docs-detail-panel breakage. The run also lists existing strict-mode errors inside ui-core sources, which ui-core itself compiles with strictTemplates: false.
  • legal-ui jest: npx jest -c packages/plugins/legal-ui/jest.config.ts --rootDir packages/plugins/legal-ui: 1 suite, 19 tests passed. The same ngc check on its tsconfig.lib.json (strictTemplates) gives 0 diagnostics in legal-ui.
  • ui-core shared jest: npx jest -c packages/ui-core/jest.config.ts --rootDir packages/ui-core shared/: 65 suites, 485 tests passed.
  • ui-core shared templates: ngc on the shared entry point (packages/ui-core/shared/src/index.ts, using ui-core's tsconfig.lib.json), run before and after the migration:
    • with ui-core's own settings (strictTemplates: false): 0 errors before, 0 after;
    • with strictTemplates: true: the same 128 diagnostics before and after (all existing), and none new.
  • apps/gauzy: neither migrated component has a spec, and no other spec refers to them. Instead, I type-checked the whole app with the AOT compiler: ngc -p on apps/gauzy/tsconfig.app.json with noEmit. This compiles every lazy route, including the docs-ui and ui-core sources changed here.
    • With the app's own settings: exit 0, no errors. The only output is 6 NG8011 warnings in ai-chat-react-ui, a file this PR does not touch.
    • With strictTemplates: true added: 0 diagnostics in billing.component.html or edit-employee-documents.component.html.
  • nx builds (ng-packagr):
    • nx build ui-core passes, and so do all 8 entry points, @gauzy/ui-core/shared among them.
    • nx build plugin-legal-ui passes.
    • nx build plugin-docs-ui stops on 11 TS2883 errors in +state/documents.actions.ts: "the inferred type ... cannot be named without a reference to ... ts-action". This PR does not touch that file. The build fails the same way, with the same 11 errors, when the docs-ui sources are put back to the base commit. On this machine node_modules is a junction into another checkout, which is probably what makes ts-action "not portable". So I could not get a clean ng-packagr build of docs-ui here; the strict ngc check above covers its templates.
  • Not run: the webpack nx build gauzy, and no check of the UI in a browser.

Risk and rollback

These are template-syntax changes only. @for uses the existing trackBy functions where a template had one (track trackById($index, item)), and otherwise tracks by identity (track item), as *ngFor did by default. Whitespace can differ slightly where the schematic put a block on its own line (Angular collapses it, the same way it does for the old ng-container wrappers). Each path is its own commit, so one area can be reverted without touching the others.

šŸ¤– Generated with Claude Code