igniteui-angular icon indicating copy to clipboard operation
igniteui-angular copied to clipboard

Theme issue when having an OnPush component inside igx-tabs or igx-stepper

Open pmoleri opened this issue 2 years ago • 5 comments

Description

Theme is always Material having an OnPush component inside igx-tabs or igx-stepper.

  • igniteui-angular version: 17.1.0
  • browser: all

Steps to reproduce

  1. Set Theme to Fluent
  2. Create an OnPush form with igx-inputs inside
  3. In the main view add a tabs component with a Form instance in each tab
  4. Run

Result

The Form inputs get rendered as Material theme. After clicking one of them they change to fluent.

Note: If I add [selected]="true" to the first tab, then it fixes for it but it still happens on the 2nd tab.

Expected result

The inputs should render as Fluent from the beginning.

Attachments

tab-theme-form

https://stackblitz.com/edit/vn4fjr?file=src%2Fapp%2Ftabs-sample-3%2Ftabs-sample-3.component.html

pmoleri avatar Feb 26 '24 16:02 pmoleri

Honestly, the only viable solution I can think of right now (after numerous ugly attempts to magically update the theme from within the input group) is to just manually call detectChanges() in ngAfterViewChecked. Since the theme is read from the --theme CSS variable, it's difficult to read the CSS prop from within the input group component when it's inside another component with OnPush strategy set without resorting to ugly stuff that try to circumvent the angular railguards.

export class MyFormComponent implements AfterViewChecked {
    constructor(private cdr: ChangeDetectorRef) {}

    public ngAfterViewChecked() {
        this.cdr.detectChanges();
    }
}

The other approach (if you are concerned about performance) is to set the theme property directly on each igx-input-group.

<igx-input-group type="line" theme="fluent">
    <input igxInput name="fullName1" type="text" />
    <label igxLabel for="fullName1">Full Name</label>
    <igx-suffix>
        <igx-icon>person</igx-icon>
    </igx-suffix>
</igx-input-group>

simeonoff avatar Mar 19 '24 08:03 simeonoff

@simeonoff

    public ngAfterViewChecked() {
        this.cdr.detectChanges();
    }

I believe this will perpetually check for changes and not just for this component but for everything up in the tree.

This is how I believe it should be done, on inputgroup component which is the one that's making the assumption that has access to the css props.

export class InputGroupComponent implements DoCheck, {
    #lastIsConnected: boolean | undefined;

    constructor(
        private elementRef: ElementRef<unknown>,
        private cdr: ChangeDetectorRef,
    ) { }
    
    ngDoCheck() {
        const isConnected = this.elementRef.nativeElement?.isConnected;
        if (this.#lastIsConnected === false && isConnected) {
            // Trigger change detection when changing from disconnected to connected state
            // To force the component to re-render once they get attached in OnPush context.
            // https://github.com/IgniteUI/igniteui-angular/issues/13958
            this.cdr.markForCheck();
        }
    
        this.#lastIsConnected = isConnected;
    }
}

pmoleri avatar Mar 19 '24 22:03 pmoleri

Thanks, will try this to see if it resolves to problem.

simeonoff avatar Mar 20 '24 07:03 simeonoff

It seems that despite the documentation ngDoCheck is not called inside an OnPush context that is not being checked.

I tried a different approach instead, it's a workaround in user land:

Created a directive:

import {
  Directive,
  ChangeDetectorRef,
  DoCheck,
  ElementRef,
} from '@angular/core';

@Directive({
  selector: '[checkOnConnect]',
})
export class CheckOnConnectDirective implements DoCheck {
  private lastIsConnected: boolean | undefined;

  constructor(
    private elementRef: ElementRef<HTMLElement>,
    private cdr: ChangeDetectorRef
  ) {}

  ngDoCheck() {
    const isConnected = this.elementRef.nativeElement?.isConnected;
    if (this.lastIsConnected === false && isConnected) {
      // Trigger change detection when changing from disconnected to connected state
      // To force the component to re-render once they get attached in OnPush context.
      // https://github.com/IgniteUI/igniteui-angular/issues/13958
      this.cdr.markForCheck();
    }

    this.lastIsConnected = isConnected;
  }
}

And then used it in the tab content attaching it to the OnPush component like this:

    <igx-tab-content>
      Form 2
      <app-my-form checkOnConnect></app-my-form>
    </igx-tab-content>

Full example here: https://stackblitz.com/edit/vn4fjr-ndwbqf?file=src%2Fapp%2Ftabs-sample-3%2Ftabs-sample-3.component.html

pmoleri avatar Mar 20 '24 14:03 pmoleri

There has been no recent activity and this issue has been marked inactive.

github-actions[bot] avatar May 20 '24 00:05 github-actions[bot]

This should be resolved by https://github.com/IgniteUI/igniteui-angular/pull/14035/files#diff-3a8eee910c2ea04cd03aa2cb41b21e94c0844017e6743d7721cabea136010cdb

simeonoff avatar Jul 08 '24 07:07 simeonoff