angular / components

Component infrastructure and Material Design components for Angular
https://material.angular.io
MIT License
24.37k stars 6.75k forks source link

Input's FormControl setValue does not work when mat-form-field has chip-list and input is linked to autocomplete #10968

Open ffredsh opened 6 years ago

ffredsh commented 6 years ago

Bug, feature request, or proposal:

Bug

What is the expected behavior?

Input's FormControl's setValue should work for an input that is inside a mat-form-field that has both autocomplete and chip-list.

What is the current behavior?

FormControl setValue does not work when the following iff a mat-form-field has an input that has matAutocomplete AND the mat-form-field has a mat-chip-list.

SetValue works if one removes the matAutocomplete directive from the input or removes the mat-chip-list element.

What are the steps to reproduce?

Demo should have the input incrementing a number every second, which doesn't work:

https://stackblitz.com/edit/angular-ay3yts?file=app%2Fautocomplete-simple-example.html

Deleting mat-chip-list or removing the matAutocomplete directive fixes the issue.

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

I want to be able to programmatically add a chip then clear the input when an option in the autocomplete has been selected.

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

Angular: 5.2.4 Material: 5.2.4

Is there anything else we should know?

Nope :)

mmalerba commented 6 years ago

The stackblitz does not appear to be set up correctly. The <input> needs a matChipInputFor directive rather than a matInput directive (see: https://material.angular.io/components/chips/overview). I also don't think setting a formControl on the input will add chips to the chipList

@tinayuangao is there a way to put a formControl on the mat-chip-list itself so they can update the chips by changing the formControl value?

ffredsh commented 6 years ago

Oops, how embarrassing! I had to recreate the stackblitz after accidentally navigating away from the first one. I've updated the stackblitz.

I'm not looking to add chips through form control, I just want to be able to have the input respond to FormControl's setValue calls. Right now, if you have both a mat-chip-list AND autocomplete, the input view will not respond to setValue. Try commenting out the mat-chip-list OR the autocomplete, you'll see that the input will once again respond to setValue and show a counter going up.

mmalerba commented 6 years ago

Hmm interesting. I made an expanded example that shows this working with other combinations of input and autocomplete but failing for chips + autocomplete. It must be some kind of bad interaction between the two components.

@crisbeto as well, as owner of autocomplete

bhaumikpandhi commented 6 years ago

I'm doing same, chips + autocomplete + Input. The purpose of a combination is allowing a user to select multiple options with searching using APIs (remote data).

Searching will populate autocomplete options and after selecting autocomplete option, I'm populating chip list selected items.

After selection of autocomplete item, value clearance is not working.

<mat-form-field class="demo-chip-list">
    <mat-chip-list #tolist>
        <mat-chip *ngFor="let user of selectedUser" [selectable]="true" [removable]="true">
            {{user.name}}
            <mat-icon matChipRemove>cancel</mat-icon>
        </mat-chip>
        <input matInput placeholder="New user..." type="text" 
        name="to" formControlName="to"
        [matChipInputFor]="tolist"
        (matChipInputTokenEnd)="add($event)" />
    </mat-chip-list>
    <mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event)" [displayWith]="displayFn">
        <mat-option *ngFor="let user of users" [value]="user">
            {{ user.name }}
        </mat-option>
    </mat-autocomplete>
</mat-form-field>
selected(event){
    this.selectedUser.push(event.option.value);
    this.composeForm.controls['to'].setValue('abc');
}
marcantoinebouchard commented 6 years ago

Hi,

I have the same issue. This causes the following behavior when using the chips with autocomplete example (https://stackblitz.com/angular/beoyjxqloag?file=app%2Fchips-autocomplete-example.ts):

  1. Type "L"
  2. Select a suggestion (ex: "Lemon")
  3. Type "L" again
  4. The suggestions are not shown. I guess it's because the valueChanges was not triggered (the field value is still "L")

Thanks, Marc

ausmurp commented 6 years ago

As a temporary fox for this you can do:

  <mat-form-field *ngIf="formGroup.get('categories') as control">
    <mat-chip-list #categoriesChipList>
      <mat-chip *ngFor="let selected of control.controls; let i = index;" removable="true"
        (removed)="removeCategory(i)">
...
      <input #categoryInput matInput formControlName="categoryInput"
...
    <mat-hint *ngIf="formGroup.get('categoryInput').touched && control.invalid; else showLabel">
      <mat-error>At least 1 category is required</mat-error>
    </mat-hint>
    <ng-template #showLabel>
      <mat-hint>Categories</mat-hint>
    </ng-template>

This will show the error over the hint at least. The control is not underlined in red though..

njifares commented 5 years ago

Having the same issue here. Combining autocomplete and chips on a formField and using control.setValue(...) won't do anything.

Interestingly enough, valueChanges got triggered with the value that is used in control.setValue(...) (but is not displayed)

nikagar4 commented 5 years ago

Having the same issue here. Combining autocomplete and chips on a formField and using control.setValue(...) won't do anything.

Interestingly enough, valueChanges got triggered with the value that is used in control.setValue(...) (but is not displayed)

Check your displayFn, if not send more code maybe I can help, I've been searching for bug for hours and finally found it maybe I can help you too

tim-kuteev commented 5 years ago

Same issue here. Any solution?

The only workaround I found is to set the value manually:

<mat-form-field>
  <mat-chip-list #chipList>
    <mat-chip *ngFor="let value of selected">{{value}}</mat-chip>
    <input #inputRef
           placeholder="Input Name"
           [formControl]="inputFormControl"
           [matAutocomplete]="autocomplete"
           [matChipInputFor]="chipList"
           (matChipInputTokenEnd)="add($event.value)">
  </mat-chip-list>
  <mat-autocomplete #autocomplete (optionSelected)="add($event.option.viewValue)">
    <mat-option *ngFor="let value of values" value="{{value}}">{{value}}</mat-option>
  </mat-autocomplete>
</mat-form-field>
  inputFormControl = new FormControl('');
  @ViewChild('inputRef', {static: true}) inputRef: ElementRef<HTMLInputElement>;

  add(value: string): void {
    this.selected.push(value);
    this.inputControl.setValue('');
    this.inputRef.nativeElement.value = ''; // Won't work without this line
  }
frankrc85 commented 4 years ago

@tim-kuteev thank you it works. I had initially just put some javascript like (HTMLInputElement document.getElementById('id')).value = ' '; (the tag characters <> were not showing the htmlinputelement so I had to remove them in the comment).

But you do it the right way with viewchild in this case. Thanks ! :)

philmtd commented 4 years ago

I'm also encountering this issue. Generally it feels like the chip list and autocomplete do not work together perfectly. For example selected chips do not work in this scenario. Even if the chip has [selected]="true" it will simply not display as selected.

twisha commented 4 years ago

Any update on this issue?

towith commented 4 years ago

I got the same issue , here's my code

<mat-form-field class="example-chip-list">
                        <mat-chip-list #chipList>
                            <mat-chip
                                    *ngFor="let primer of element.selectedPrimers"
                                    [selectable]="true"
                                    [removable]="true"
                                    (removed)="removePrimer(element,primer)">
                                {{primer.name}}
                                <mat-icon matChipRemove *ngIf="true">cancel</mat-icon>
                            </mat-chip>
                            <input
                                    placeholder="选择载体..."
                                    [formControl]="primeSearchControl"
                                    [matAutocomplete]="auto"
                                    [matChipInputFor]="chipList"
                                    [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
                                    [matChipInputAddOnBlur]="false"
                            />

                            <mat-icon matSuffix (click)="endSelectPrimer(inplace)">done_all</mat-icon>
                        </mat-chip-list>
                        <mat-autocomplete #auto="matAutocomplete" (optionSelected)="primerSelected(element,
                        $event)">
                            <mat-option *ngFor="let primer of primerOptions" [value]="primer">
                                {{primer.name}}
                            </mat-option>
                        </mat-autocomplete>
                    </mat-form-field>

the

        this.primeSearchControl.setValue('', {emitEvent: false})

in primerSelected method not work

mlewe commented 3 years ago

As a workaround it is possible to define your own copy of the [matAutoComplete] directive, which doesn't provide NG_VALUE_ACCESSOR.

import { Directive, Input } from "@angular/core";
import { MatAutocomplete, MatAutocompleteTrigger } from "@angular/material/autocomplete";

/**
 * This hack is necessary because `MatAutocompleteTrigger` provides `NG_VALUE_ACCESSOR`,
 * which doesn't make any sense when used in combination with `MatChipInput`.
 */
@Directive({
  selector: `input[matChipAutocomplete], textarea[matChipAutocomplete]`,
  host: {
    'class': 'mat-autocomplete-trigger',
    '[attr.autocomplete]': 'autocompleteAttribute',
    '[attr.role]': 'autocompleteDisabled ? null : "combobox"',
    '[attr.aria-autocomplete]': 'autocompleteDisabled ? null : "list"',
    '[attr.aria-activedescendant]': '(panelOpen && activeOption) ? activeOption.id : null',
    '[attr.aria-expanded]': 'autocompleteDisabled ? null : panelOpen.toString()',
    '[attr.aria-owns]': '(autocompleteDisabled || !panelOpen) ? null : autocomplete?.id',
    '[attr.aria-haspopup]': '!autocompleteDisabled',
    // Note: we use `focusin`, as opposed to `focus`, in order to open the panel
    // a little earlier. This avoids issues where IE delays the focusing of the input.
    '(focusin)': '_handleFocus()',
    '(blur)': '_onTouched()',
    '(input)': '_handleInput($event)',
    '(keydown)': '_handleKeydown($event)',
  },
  exportAs: 'matAutocompleteTrigger'
})
export class MatChipAutocompleteTrigger extends MatAutocompleteTrigger {
  @Input('matChipAutocomplete')
  public set matChipAutocomplete(value: MatAutocomplete) {
    this.autocomplete = value;
  }
}
ansaripwd commented 3 years ago

HI, I am facing the same issue, if i apply validator as "required" even when i enter fields it say formcontrol status as INVALID and value as null,

{{fruit}} cancel {{ fruit }} {{fruit}} cancel {{ fruit1 }}

<button (click)="onSubmit()">submit

import {COMMA, ENTER} from '@angular/cdk/keycodes'; import {Component, ElementRef, ViewChild} from '@angular/core'; import {FormControl} from '@angular/forms'; import {MatAutocompleteSelectedEvent, MatChipInputEvent} from '@angular/material'; import {Observable} from 'rxjs'; import {map, startWith} from 'rxjs/operators';

/**

bskp commented 2 years ago

@mlewe Thank you so much! I was facing the same issue and danced around for hours until I found your workaround, which works perfectly for me.

marc-wilson commented 1 year ago

This issue is still present in Angular v14.

ivorobioff commented 5 months ago

The issue is still there in angular and material 18.

RSBlek commented 5 months ago

Not fixed after 6 years...