question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

form-field: support swapping various child components with ngIf

See original GitHub issue

Bug, feature request, or proposal:

I recently ran into a bug that mat-hint threw an error when its corresponding matInput is inserted conditionally. E.g. take the following template:

<mat-form-field>
    <input *ngIf="isFile()" matInput placeholder="Enter file name" formControlName="filename">
    <input *ngIf="isDirectory()" matInput placeholder="Enter directory name" formControlName="filename">
    <mat-hint>
      Some hint.
    </mat-hint>
</mat-form-field>

Note that the only reason inputs are inserted conditionally here is because the placeholder text is different depending on the condition.

This will throw the following error:

app.component.html:7 ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'null'. Current value: 'mat-hint-0'.
    at new d (VM8254 zone.min.js:1)
    at viewDebugError (VM7606 core.umd.js:8466)
    at expressionChangedAfterItHasBeenCheckedError (VM7606 core.umd.js:8444)
    at checkBindingNoChanges (VM7606 core.umd.js:8608)
    at checkNoChangesNodeDynamic (VM7606 core.umd.js:12515)
    at checkNoChangesNode (VM7606 core.umd.js:12464)
    at debugCheckNoChangesNode (VM7606 core.umd.js:13241)
    at debugCheckRenderNodeFn (VM7606 core.umd.js:13181)
    at Object.eval [as updateRenderer] (VM7993 AppComponent.ngfactory.js:78)
    at Object.debugUpdateRenderer [as updateRenderer] (VM7606 core.umd.js:13163)
    at checkNoChangesView (VM7606 core.umd.js:12283)
    at callViewAction (VM7606 core.umd.js:12650)
    at execEmbeddedViewsAction (VM7606 core.umd.js:12628)
    at checkNoChangesView (VM7606 core.umd.js:12282)
    at callViewAction (VM7606 core.umd.js:12650)

This is not a problem when the input is not inserted conditionally but rather placed in the template right away. I’m not sure if that is by design or not but it caused me some hours of debugging until I realised it was the combination of mat-hint inside a mat-form-field whom\s matInput is rendered using *ngIf.

What is the expected behavior?

I expected this to just work, but again, maybe this is by design but then we should document somewhere that matInputs aren’t allowed to be inserted using structural directives.

What is the current behavior?

Described above.

What are the steps to reproduce?

Here’s a plunk that reproduces the error: http://plnkr.co/edit/MrUvFVKIW1BoX9d96eeu?p=preview

Simply check the console.

What is the use-case or motivation for changing an existing behavior?

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

I’m using @angular/material version 2.0.0-beta.11 but the plunk uses the latest version. Unfortunately I can’t tell if this error has been thrown in older versions as well.

Is there anything else we should know?

Yeah, you’re all doing an amazing job. Thanks for that.

Issue Analytics

  • State:open
  • Created 6 years ago
  • Reactions:14
  • Comments:11 (1 by maintainers)

github_iconTop GitHub Comments

6reactions
csbenjamincommented, Nov 3, 2017

I am having this issue too, but it is with mat-error instead of mat-hint:

<input *ngIf="!structure[key].options" matInput [formControlName]="key" 
    [placeholder]="structure[key].label">

<mat-select *ngIf="structure[key].options" 
    [placeholder]="structure[key].label" [formControlName]="key">
        <mat-option *ngFor="let value of structure[key].options" 
            [value]="value.value">{{value.label}}</mat-option>
</mat-select>
4reactions
keegocommented, Oct 9, 2018

Hitting the same issue with mat-hint and ngSwitch:

<mat-form-field
  *ngFor="let field of schema.fields"
  floatLabel="always"
  [hintLabel]="field.hint">

  <ng-container [ngSwitch]="field.type || 'text'">

    <input matInput
      *ngSwitchCase="'text'"
      [placeholder]="field.placeholder"
      [formControlName]="field.name"
      [errorStateMatcher]="matcher"
      [required]="field.required">

    <mat-select
      *ngSwitchCase="'select'"
      [placeholder]="field.placeholder"
      [formControlName]="field.name"
      [errorStateMatcher]="matcher"
      [required]="field.required">

    </mat-select>

  </ng-container>

</mat-form-field>

Tried switching to using <mat-hint> element instead to no avail:

<mat-form-field
  *ngFor="let field of schema.fields"
  floatLabel="always">

  <mat-hint
    align="start">
    {{ field.hint }}
  </mat-hint>

  <ng-container [ngSwitch]="field.type || 'text'">

    <input matInput
      *ngSwitchCase="'text'"
      [placeholder]="field.placeholder"
      [formControlName]="field.name"
      [errorStateMatcher]="matcher"
      [required]="field.required">

    <mat-select
      *ngSwitchCase="'select'"
      [placeholder]="field.placeholder"
      [formControlName]="field.name"
      [errorStateMatcher]="matcher"
      [required]="field.required">

    </mat-select>

  </ng-container>

</mat-form-field>

Would definitely love to see “more swappable form-field pieces” 🙏

P.S. thanks to the Angular team for the good work they’ve done so far ❤️ 👏

Update: was able to workaround by grouping the all form components together into a single conditional render – basically render the whole mat-form-field and its children together (not DRY but it works):

<ng-container *ngFor="let field of schema.fields">

  <ng-container [ngSwitch]="field.type || 'text'">
    <ng-container *ngSwitchCase="'text'">

      <mat-form-field floatLabel="always">

        <input matInput
          [placeholder]="field.placeholder"
          [formControlName]="field.name"
          [errorStateMatcher]="matcher"
          [required]="field.required">

        <mat-hint>
          {{ field.hint }}
        </mat-hint>

      </mat-form-field>

    </ng-container>
    <ng-container *ngSwitchCase="'select'">

      <mat-form-field floatLabel="always">

        <mat-select
          [placeholder]="field.placeholder"
          [formControlName]="field.name"
          [errorStateMatcher]="matcher"
          [required]="field.required">

        </mat-select>

        <mat-hint>
          {{ field.hint }}
        </mat-hint>

      </mat-form-field>

    </ng-container>
  </ng-container>

</ng-container>
Read more comments on GitHub >

github_iconTop Results From Across the Web

Inserting ng-template into child component not rendering the ...
When i dont pass any templates to the child components they render just fine. Switching between login and sign up works as expected....
Read more >
NgIf & NgSwitch • Angular - codecraft.tv
Know how to conditionally add or remove elements from the DOM using the NgSwitch directive. NgIf. The NgIf directive is used when you...
Read more >
Document List component - Alfresco Builder Network
The Document List component supports default presets for all the custom sources mentioned earlier. If you don't provide any custom column definition with ......
Read more >
The React Handbook – Learn React for Beginners
Changing state on a Component will never affect its parent, or its siblings, or any other Component in the application: just its children....
Read more >
Using Angular Forms with ag-Grid
We create the form controls by iterating over all rows and columns, and for each cell create a FormControl which we'll add to...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found