【问题标题】:Angular Material - Custom Autocomplete componentAngular Material - 自定义自动完成组件
【发布时间】:2018-02-26 18:02:17
【问题描述】:

我正在尝试创建自己的自定义角度材质组件,该组件能够与mat-form-field 控件一起使用。

除此之外,我希望控件使用mat-autocomplete 指令。

我的目标只是创建一个更好看的mat-autocomplete 组件,其中集成了clear-button 和自定义css 箭头,如下图所示。我已经通过使用标准组件成功获得它并添加了我想要的但现在我想将它导出到通用组件中。

我正在使用官方的 angular material 文档来创建我自己的表单字段控件以及另一个关于它的 SO 帖子,这已经对我有很大帮助:

我目前面临几个我认为相关的问题:

  • 即使正确选择了值,我的表单也无效。
  • 选择选项后占位符设置不正确。
  • 自动完成过滤选项根本不起作用
  • 如果我不专门点击输入,焦点不会正确触发。

我相信我的前三个问题是由未正确链接到我的反应表单的自动完成值引起的。


这里是项目的个人公共存储库的直接链接(因为这里显示的问题有点大):Git Repository : https://github.com/Tenmak/material


基本上,这个想法是改变这个:

  <mat-form-field>
    <div fxLayout="row">
      <input matInput placeholder="Thématique" [matAutocomplete]="thematicAutoComplete" formControlName="thematique" tabindex="1">

      <div class="mat-select-arrow-wrapper">
        <div class="mat-select-arrow" [ngClass]="{'mat-select-arrow-down': !thematicAutoComplete.isOpen, 'mat-select-arrow-up': thematicAutoComplete.isOpen}"></div>
      </div>
    </div>
    <button mat-button *ngIf="formDossier.get('thematique').value" matSuffix mat-icon-button aria-label="Clear" (click)="formDossier.get('thematique').setValue('')">
      <mat-icon>close</mat-icon>
    </button>

    <mat-hint class="material-hint-error" *ngIf="!formDossier.get('thematique').hasError('required') && formDossier.get('thematique').touched && formDossier.get('thematique').hasError('thematiqueNotFound')">
      <strong>
        Veuillez sélectionner un des choix parmi les options possibles.
      </strong>
    </mat-hint>
  </mat-form-field>

  <mat-autocomplete #thematicAutoComplete="matAutocomplete" [displayWith]="displayThematique">
    <mat-option *ngFor="let thematique of filteredThematiques | async" [value]="thematique">
      <span> {{thematique.code}} </span>
      <span> - </span>
      <span> {{thematique.libelle}} </span>
    </mat-option>
  </mat-autocomplete>

进入这个:

  <mat-form-field>
    <siga-auto-complete placeholder="Thématique" [tabIndex]="1" [autoCompleteControl]="thematicAutoComplete" formControlName="thematique">
    </siga-auto-complete>

    <mat-hint class="material-hint-error" *ngIf="!formDossier.get('thematique').hasError('required') && formDossier.get('thematique').touched && formDossier.get('thematique').hasError('thematiqueNotFound')">
      <strong>
        Veuillez sélectionner un des choix parmi les options possibles.
      </strong>
    </mat-hint>
  </mat-form-field>

  <mat-autocomplete #thematicAutoComplete="matAutocomplete" [displayWith]="displayThematique">
    <mat-option *ngFor="let thematique of filteredThematiques | async" [value]="thematique">
      <span> {{thematique.code}} </span>
      <span> - </span>
      <span> {{thematique.libelle}} </span>
    </mat-option>
  </mat-autocomplete>

我目前正在“档案”文件夹中工作,该文件夹显示了我的初始反应表单。我在这个表单中直接使用我的自定义组件autocomplete.component.ts 来替换第一个字段。

这是我对通用组件代码的尝试(简化):

class AutoCompleteInput {
    constructor(public testValue: string) {
    }
}

@Component({
    selector: 'siga-auto-complete',
    templateUrl: './autocomplete.component.html',
    styleUrls: ['./autocomplete.component.scss'],
    providers: [
        {
            provide: MatFormFieldControl,
            useExisting: SigaAutoCompleteComponent
        },
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => SigaAutoCompleteComponent),
            multi: true
        }
    ],
})
export class SigaAutoCompleteComponent implements MatFormFieldControl<AutoCompleteInput>, AfterViewInit, OnDestroy, ControlValueAccessor {
    ...
    parts: FormGroup;
    ngControl = null;

    ...

    @Input()
    get value(): AutoCompleteInput | null {
        const n = this.parts.value as AutoCompleteInput;
        if (n.testValue) {
            return new AutoCompleteInput(n.testValue);
        }
        return null;
    }
    set value(value: AutoCompleteInput | null) {
        // Should set the value in the form through this.writeValue() ??
        console.log(value);
        this.writeValue(value.testValue);
        this.stateChanges.next();
    }

    @Input()
    set formControlName(formName) {
        this._formControlName = formName;
    }
    private _formControlName: string;

    // ADDITIONNAL
    @Input() autoCompleteControl: MatAutocomplete;
    @Input() tabIndex: string;

    private subs: Subscription[] = [];

    constructor(fb: FormBuilder, private fm: FocusMonitor, private elRef: ElementRef) {
        this.subs.push(
            fm.monitor(elRef.nativeElement, true).subscribe((origin) => {
                this.focused = !!origin;
                this.stateChanges.next();
            })
        );

        this.parts = fb.group({
            'singleValue': '',
        });

        this.subs.push(this.parts.valueChanges.subscribe((value: string) => {
            this.propagateChange(value);
        }));
    }

    ngAfterViewInit() {
        // Wrong approach but some idea ?
        console.log(this.autoCompleteControl);
        this.autoCompleteControl.optionSelected.subscribe((event: MatAutocompleteSelectedEvent) => {
            console.log(event.option.value);
            this.value = event.option.value;
        })
    }

    ngOnDestroy() {
        this.stateChanges.complete();
        this.subs.forEach(s => s.unsubscribe());
        this.fm.stopMonitoring(this.elRef.nativeElement);
    }

    ...

    // CONTROL VALUE ACCESSOR
    private propagateChange = (_: any) => { };

    public writeValue(a: string) {
        console.log('wtf');

        if (a && a !== '') {
            console.log('value => ', a);
            this.parts.setValue({
                'singleValue': a
            });
        }
    }
    public registerOnChange(fn: any) {
        this.propagateChange = fn;
    }

    public registerOnTouched(fn: any): void {
        return;
    }

    public setDisabledState?(isDisabled: boolean): void {
        this.disabled = isDisabled;
    }
}

【问题讨论】:

    标签: angular autocomplete angular-material


    【解决方案1】:

    终于解决了!!!

    1. 这里的问题是在子[SigaAutoCompleteComponent]中创建输入字段时,父必须知道 填写子项[CreateDossierComponent]的值, 那部分是 缺少这就是它无法变为有效的原因,因为它认为 输入字段未触及 [保持无效] - 通过发出 值,然后根据需要操作表单控件。
    2. 拆分 mat-form-field 和 input 导致问题 - 通过将 mat-form-field 元素移动到 child 来解决,其他代码保持不变 这解决了占位符重叠和单击箭头图标以显示
    3. 这可以做到——[一种重新设计的方法],通过将服务注入子组件并执行自动完成功能 那边[我还没有实现这个,但这会起作用,因为它只是部门字段的副本]

      在 create-doiser.component.html 中

            <!-- </div> -->
            <mat-autocomplete #thematicAutoComplete="matAutocomplete" [displayWith]="displayThematique">
              <mat-option *ngFor="let thematique of filteredThematiques | async" [value]="thematique">
                <span> {{thematique.code}} </span>
                <span> - </span>
                <span> {{thematique.libelle}} </span>
              </mat-option>
            </mat-autocomplete>
      

      在 autocomplete.component.html 中

      <mat-form-field style="display:block;transition:none ">
      <div fxLayout="row">
        <input  matInput   placeholder="Thématique" [matAutocomplete]="autoCompleteControl" (optionSelected)="test($event)" tabindex="{{tabIndex}}">
        <div class="mat-select-arrow-wrapper" (click)="focus()">
          <div class="mat-select-arrow" [ngClass]="{'mat-select-arrow-down': !autoCompleteControl.isOpen, 'mat-select-arrow-up': autoCompleteControl.isOpen}"></div>
        </div>
      </div>
      
      </mat-form-field>
      

      在 autocomplete.component.ts 中

      in set value emit the value to parent
      this.em.emit(value);
      

      创建-dosier.component.ts

        this.thematique = new FormControl( ['', [Validators.required, this.thematiqueValidator]]
      
          ); 
      
      this.formDossier.addControl('thematique',this.thematique);
      call(event){
      
          this.thematique.setValue(event);
          this.thematique.validator=this.thematiqueValidator();
          this.thematique.markAsTouched();
          this.thematique.markAsDirty();
      
        }
      }
      

      这将解决所有问题,如果您希望我推送到 github,请告诉我 .. 希望这可以帮助 !!!! 谢谢!!

      更新: 自动完成,提示现在一切正常..

      我了解您不希望 input 和 mat-form-field 放在一起

      但如果只是为了动态显示 mat-h​​int [这取决于 在 formcontrol values],我们可以将表单控件从父级传递给子级 这甚至消除了从 孩子对父母 在父组件中设置值,[mat-h​​int 字段留在父组件本身]

    【讨论】:

    • 非常感谢您的帮助,我会尽快调查 :)
    • 也谢谢你:),我从中学到了一些东西
    • 感谢您对 emit 部分的洞察力,我相信这确实是传递值所缺少的。我对您的解决方案的问题是,它是一个非常顽固的自定义组件,我的意思是我希望这个自定义组件不依赖于 material-form-field,因为它应该从外部与之交互。目的是能够动态添加材料提示,以及其他依赖于material-form-field 的 HTML 对象。例如,如何使用此自定义组件管理 mat-hint 的显示?
    • 是的,我明白,您只想在子项中使用输入,而在父项中使用其他与垫相关的项目..我正在尝试这样做..将尽快将更改推送到 git 谢谢! !
    • hi 试图在提到的 ..unauthorized 的 git repo 下创建分支,在 github.com/HariDongli/material.git 下创建分支,这包含将表单控件传递给子级的更改,并涵盖了您提到的所有场景。谢谢!!!
    猜你喜欢
    • 2016-10-11
    • 2021-10-04
    • 2020-09-18
    • 1970-01-01
    • 2020-01-11
    • 1970-01-01
    • 2020-11-27
    • 2021-02-03
    • 2020-03-27
    相关资源
    最近更新 更多