Skip to content

refactor(igx-ts): migrate templates to Angular signals and inject() function#1586

Open
Copilot wants to merge 8 commits intomasterfrom
copilot/migrate-to-angular-signals
Open

refactor(igx-ts): migrate templates to Angular signals and inject() function#1586
Copilot wants to merge 8 commits intomasterfrom
copilot/migrate-to-angular-signals

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 31, 2026

  • Explore repository structure and understand the CLI
  • List all available igx-ts templates (45 templates)
  • Create igx-ts project and add all 45 component templates
  • Install dependencies and build the Angular project
  • Fix build errors found during testing:
    • awesome-grid: unwrap viewChild signal in createAbsoluteOverlaySettings() call
    • fintech-grid: fix null safety for groupsRecords optional chain
    • fintech-grid: fix data null assertion in guarded block
  • Verify build succeeds with all 45 templates
  • All 379 existing tests pass (9 pre-existing failures)
  • Lint passes
  • Update CHANGELOG.md with signals/inject migration entry
  • Post test results in PR

End-to-End Template Test Results

Test Setup

  • Created a fresh igx-ts project using ig new TestProject --framework=angular --type=igx-ts --theme=default
  • Added all 45 available component templates using ig add

Template Scaffolding: All 45/45 templates scaffolded successfully ✅

accordion, autocomplete, enhanced-autocomplete, bullet-graph, calendar, carousel, category-chart, chip, combo, date-picker, dialog, dock-manager, dropdown, financial-chart, grid, grid-batch-editing, custom-grid, grid-summaries, grid-multi-column-headers, hierarchical-grid, hierarchical-grid-batch-editing, hierarchical-grid-custom, hierarchical-grid-summaries, input-group, linear-gauge, list, geographic-map, pivot-grid, radial-gauge, select, select-groups, select-in-form, stepper, bottom-nav, tabs, time-picker, tooltip, tree, custom-tree-grid, awesome-grid, crm-grid, fintech-grid, fintech-tree-grid, login, weather-forecast

Angular Build: ✅ Application bundle generation complete (53s)

  • Zero TypeScript errors
  • Zero Angular compiler errors
  • Only pre-existing warnings (Sass deprecations, ESM module warnings from third-party deps)

Existing Test Suite: ✅ All 379 specs pass

  • Same 9 pre-existing failures (unrelated ./output/ directory issues)
  • No new test failures

Bug Fixes Applied During Testing

  1. awesome-grid: Fixed this.grid1this.grid1() as any in createAbsoluteOverlaySettings() — signal was not unwrapped
  2. fintech-grid: Fixed null safety for groupsRecords optional chain and data property access with non-null assertions where values are guarded by if checks

Copilot AI and others added 2 commits March 31, 2026 21:58
…d inject() function

Agent-Logs-Url: https://github.com/IgniteUI/igniteui-cli/sessions/f864bfd5-94a9-4116-b42a-29df85870377

Co-authored-by: damyanpetev <3198469+damyanpetev@users.noreply.github.com>
@coveralls
Copy link
Copy Markdown

coveralls commented Apr 2, 2026

Coverage Status

coverage: 86.103%. remained the same — copilot/migrate-to-angular-signals into master

@damyanpetev damyanpetev force-pushed the copilot/migrate-to-angular-signals branch from 1978764 to 45e91a1 Compare April 2, 2026 09:39
@damyanpetev damyanpetev marked this pull request as ready for review April 14, 2026 15:30
Copilot AI review requested due to automatic review settings April 14, 2026 15:30
@damyanpetev damyanpetev changed the title Migrate igx-ts templates to Angular signals and inject() function reafactor(igx-ts): migrate templates to Angular signals and inject() function Apr 14, 2026
@damyanpetev damyanpetev changed the title reafactor(igx-ts): migrate templates to Angular signals and inject() function refactor(igx-ts): migrate templates to Angular signals and inject() function Apr 14, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Migrates igx-ts Angular templates to newer Angular patterns by adopting signal-based view queries, the inject() function for DI, and signal-based outputs.

Changes:

  • Replaced @ViewChild queries with viewChild() / viewChild.required() and updated call sites to dereference signals via ().
  • Replaced @Output + EventEmitter with output().
  • Replaced constructor parameter injection with inject() across templates and services; updated CHANGELOG entry.

Reviewed changes

Copilot reviewed 35 out of 35 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/igx-templates/igx-ts/tree/default/files/src/app/path/filePrefix.ts Switches service DI to inject() in the tree template.
packages/igx-templates/igx-ts/select/select-in-form/files/src/app/path/filePrefix.ts Migrates @ViewChild to viewChild.required() and updates toast usage.
packages/igx-templates/igx-ts/radial-gauge/default/files/src/app/path/filePrefix.ts Migrates radial gauge @ViewChild to signal query and unwraps usages.
packages/igx-templates/igx-ts/projects/side-nav/files/src/app/app.ts Migrates nav drawer query to viewChild.required() and router DI to inject().
packages/igx-templates/igx-ts/projects/side-nav-auth/files/src/app/authentication/services/user-store.ts Migrates service DI to inject().
packages/igx-templates/igx-ts/projects/side-nav-auth/files/src/app/authentication/services/local-storage.ts Migrates PLATFORM_ID injection to inject().
packages/igx-templates/igx-ts/projects/side-nav-auth/files/src/app/authentication/services/jwt.interceptor.ts Migrates interceptor DI to inject().
packages/igx-templates/igx-ts/projects/side-nav-auth/files/src/app/authentication/services/fake-backend.ts Migrates backend interceptor DI to inject().
packages/igx-templates/igx-ts/projects/side-nav-auth/files/src/app/authentication/services/external-auth.ts Migrates multi-service DI to inject() fields.
packages/igx-templates/igx-ts/projects/side-nav-auth/files/src/app/authentication/services/authentication.ts Migrates HttpClient DI to inject().
packages/igx-templates/igx-ts/projects/side-nav-auth/files/src/app/authentication/register/register.ts Migrates outputs to output() and DI to inject().
packages/igx-templates/igx-ts/projects/side-nav-auth/files/src/app/authentication/redirect/redirect.ts Migrates route/router/auth DI to inject().
packages/igx-templates/igx-ts/projects/side-nav-auth/files/src/app/authentication/profile/profile.ts Migrates UserStore DI to inject().
packages/igx-templates/igx-ts/projects/side-nav-auth/files/src/app/authentication/login/login.ts Migrates outputs to output() and DI to inject().
packages/igx-templates/igx-ts/projects/side-nav-auth/files/src/app/authentication/login-dialog/login-dialog.ts Migrates dialog @ViewChild to viewChild.required() and unwraps usage.
packages/igx-templates/igx-ts/projects/side-nav-auth/files/src/app/authentication/login-dialog/login-dialog.spec.ts Updates test component outputs to output() and unwraps view query usage in assertions/spies.
packages/igx-templates/igx-ts/projects/side-nav-auth/files/src/app/authentication/login-bar/login-bar.ts Migrates view queries to viewChild.required() and DI to inject().
packages/igx-templates/igx-ts/projects/side-nav-auth/files/src/app/authentication/auth.guard.ts Migrates guard DI to inject().
packages/igx-templates/igx-ts/projects/side-nav-auth/files/src/app/app.ts Migrates nav drawer query to viewChild.required() and router DI to inject().
packages/igx-templates/igx-ts/map/default/files/src/app/path/filePrefix.ts Migrates map @ViewChild to signal query and unwraps usage.
packages/igx-templates/igx-ts/linear-gauge/default/files/src/app/path/filePrefix.ts Migrates linear gauge @ViewChild to signal query and unwraps usages.
packages/igx-templates/igx-ts/input-group/default/files/src/app/path/filePrefix.ts Migrates FormBuilder DI to inject().
packages/igx-templates/igx-ts/hierarchical-grid/hierarchical-grid-batch-editing/files/src/app/path/filePrefix.ts Migrates multiple @ViewChild queries to signal queries and unwraps usages.
packages/igx-templates/igx-ts/grid/grid-summaries/files/src/app/path/filePrefix.ts Migrates grid @ViewChild to signal query and unwraps usage.
packages/igx-templates/igx-ts/grid/grid-batch-editing/files/src/app/path/filePrefix.ts Migrates grid/dialog queries to signal queries and unwraps usage.
packages/igx-templates/igx-ts/custom-templates/weather-forecast/files/src/app/path/filePrefix.ts Migrates panel @ViewChild to viewChild.required() and unwraps usage.
packages/igx-templates/igx-ts/custom-templates/login/files/src/app/path/filePrefix.ts Migrates FormBuilder DI to inject() and initializes forms via injected builder.
packages/igx-templates/igx-ts/custom-templates/fintech-tree-grid/files/src/app/path/filePrefix.ts Migrates multiple view queries to viewChild.required() and DI to inject().
packages/igx-templates/igx-ts/custom-templates/fintech-grid/files/src/app/path/filePrefix.ts Migrates multiple view queries to viewChild.required() and DI to inject(), plus null-safety adjustments.
packages/igx-templates/igx-ts/custom-templates/crm-grid/files/src/app/path/filePrefix.ts Migrates main grid query to viewChild.required() and exporter DI to inject().
packages/igx-templates/igx-ts/custom-templates/awesome-grid/files/src/app/path/filePrefix.ts Migrates view queries to signal queries and overlay DI to inject().
packages/igx-templates/igx-ts/chip/default/files/src/app/path/filePrefix.ts Migrates view queries to signal queries and CDR DI to inject().
packages/igx-templates/igx-ts/bullet-graph/default/files/src/app/path/filePrefix.ts Migrates bullet graph @ViewChild to signal query and unwraps usages.
packages/igx-templates/igx-ts/autocomplete/autocomplete-extended/files/src/app/path/filePrefix.ts Migrates toast @ViewChild to viewChild.required() and unwraps usage.
CHANGELOG.md Adds a changelog entry describing the signals/inject migration.
Comments suppressed due to low confidence (2)

packages/igx-templates/igx-ts/custom-templates/fintech-tree-grid/files/src/app/path/filePrefix.ts:156

  • ngOnInit subscribes to this.volumeSlider().valueChange, but volumeSlider is a viewChild.required signal. Set up this subscription only after the view query resolves (e.g., in ngAfterViewInit/afterNextRender). If you move it, ensure ngOnDestroy doesn’t unsubscribe() an uninitialized subscription.
    this.volumeChanged = this.volumeSlider().valueChange.pipe(debounce(() => timer(200)));
    this.volumeChanged.subscribe(
      () => {
        this.localData.getData(this.volume);
      },

packages/igx-templates/igx-ts/custom-templates/fintech-grid/files/src/app/path/filePrefix.ts:170

  • ngOnInit also dereferences this.volumeSlider() to subscribe to valueChange. Since volumeSlider is a viewChild.required signal, set up this subscription only after the view query resolves (e.g., in ngAfterViewInit/afterNextRender). If you move it, ensure ngOnDestroy doesn’t unsubscribe an uninitialized subscription.
    this.volumeChanged = this.volumeSlider().valueChange.pipe(debounce(() => timer(200)));
    this.volumeChanged.subscribe(
      () => {
        this.localData.getData(this.volume);
      },

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +185 to +186
if (this.grid1().groupsRecords[0].groups && this.grid1().groupsRecords[0]?.groups?.[0]?.groups) {
const recordsToBeSelected = this.grid1().selectionService.getRowIDs(this.grid1().groupsRecords[0].groups![0].groups![0].records);
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The groupsRecords guard still assumes groupsRecords[0] exists (this.grid1().groupsRecords[0].groups ...). If the grouped records array is empty (e.g., before grouping completes), this will throw. Add a length/optional-chain check before indexing and remove the non-null assertions by guarding the nested structure.

Suggested change
if (this.grid1().groupsRecords[0].groups && this.grid1().groupsRecords[0]?.groups?.[0]?.groups) {
const recordsToBeSelected = this.grid1().selectionService.getRowIDs(this.grid1().groupsRecords[0].groups![0].groups![0].records);
const groupsRecords = this.grid1().groupsRecords;
const records = groupsRecords.length > 0 ? groupsRecords[0]?.groups?.[0]?.groups?.[0]?.records : undefined;
if (records) {
const recordsToBeSelected = this.grid1().selectionService.getRowIDs(records);

Copilot uses AI. Check for mistakes.
Comment on lines +82 to +86
public toggleRefHiding = viewChild<IgxToggleDirective>('toggleRefHiding');
public toggleRefPinning = viewChild<IgxToggleDirective>('toggleRefPinning');

@ViewChild('hidingButton') public hidingButton!: ElementRef;
@ViewChild('pinningButton') public pinningButton!: ElementRef;
public hidingButton = viewChild<ElementRef>('hidingButton');
public pinningButton = viewChild<ElementRef>('pinningButton');
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

toggleRefHiding/toggleRefPinning and the button ElementRefs are declared as optional (viewChild(...)) but later used with non-null assertions. If these queries are required for the template, prefer viewChild.required(...) so the types match actual usage and you can remove the !; otherwise add guards before dereferencing.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll delete those in a separate PR as they are not used, but out of scope for the current migration.

Comment on lines 53 to 56
public ngOnInit(): void {
this.transactionsData = this.transactions.getAggregatedChanges(true);
this.grid.rendered$.subscribe(() => {
this.grid().rendered$.subscribe(() => {
this.transactions.onStateUpdate?.subscribe(() => {
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

viewChild.required(...) queries are not guaranteed to be resolved during ngOnInit. Calling this.grid() (and this.transactions, which depends on it) in ngOnInit can throw at runtime. Move the logic that touches the grid (including the rendered$ subscription and getAggregatedChanges(...) call) to ngAfterViewInit (or afterNextRender) so the view query is definitely available before dereferencing the signal.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-.-
Sure it is, it's the replacement of { static: true } which was:

By setting static: true, you guarantee to Angular that the target of this query is always present and is not conditionally rendered. This makes the result available earlier, in the ngOnInit lifecycle method.

https://angular.dev/guide/components/queries#static-queries

And it's always defined by:

If a required query does not find a matching result, Angular reports an error. Because this guarantees that a result is available, required queries do not automatically include undefined in the signal's value type.

https://angular.dev/guide/components/queries#required-queries

Heck, the compiler even complains if this is used earlier (say in constructor):

  constructor() {
    console.log('ctor', this.saveButton())
  }

causes

✘ [ERROR] NG8118: saveButton is a required viewChild and does not have a value in this context. [plugin angular-compiler]

So this passing build is also a solid sign it will have a value alright.

@damyanpetev damyanpetev requested a review from ChronosSF April 15, 2026 16:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants