角材料-自定义自动完成组件 [英] Angular Material - Custom Autocomplete component

查看:114
本文介绍了角材料-自定义自动完成组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to create my own custom angular material component that would be able to work with a mat-form-field control.

此外,我希望控件使用mat-autocomplete指令.

Added to that, I'd like the control to use the mat-autocomplete directive.

我的目的仅仅是创建一个外观更好的mat-autocomplete组件,该组件具有集成的清除按钮和自定义的 css箭头,如下图所示.我已经通过使用标准组件成功获得了它,并添加了我想要的东西,但是现在我想将其导出到通用组件中.

My aim is simply to create a better-looking mat-autocomplete component with an integrated clear-button and custom css arrow like the following image. I have succesfully obtained it by using the standard component and added what I wanted but now I want to export it into a generic component.

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

I'm using the official angular material documentation to create my own form field control and also another SO post about it which already helped me a lot :

我目前面临着一些我认为与之相关的问题:

I am currently facing several problems which I believe are linked :

  • 即使正确选择了值,我的表单也无效.
  • 在选择一个选项后,占位符未正确设置自身.
  • 自动完成过滤器选项根本不起作用
  • 如果我没有具体单击输入,焦点将无法正确触发.
  • My form is not valid even when the value is selected correctly.
  • The placeholder is not setting itself correctly after an option is selected.
  • The auto-complete filter option doesn't work at all
  • The focus does not trigger correctly if I don't click on the input specifically.

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

I believe my first three issues are caused by the auto-complete value that is not linked to my reactive form correctly.

这里是与该项目的个人公共存储库的直接链接(因为此问题在此处显示很大):

Here is a direct link to a personnal public repository with the project (since the issue is a bit big to be displayed here) : Git Repository : https://github.com/Tenmak/material.

基本上,这个想法是对它进行改造:

Basically, the idea is to transform this :

  <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>

我目前正在"dossiers"文件夹中工作,该文件夹显示了我的初始反应形式.我直接在此表单中使用我的自定义组件autocomplete.component.ts来替换第一个字段.

I'm currently working in the "dossiers" folder which displays my initial reactive form. And I'm using my custom component autocomplete.component.ts inside this form directly to replace the first field.

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

Here is my attempt at the code of the generic component (simplified):

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;
    }
}

推荐答案

终于解决了!

Finally solved !!!

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

  1. The problem here is when creating the input field in the child[SigaAutoCompleteComponent], the parent has to know about the value filled in the child [CreateDossierComponent], that part is missing that is the reason it cant turn to valid as it thinks that input field is not touched [stays invalid] -- solved by emitting the value to parent then manipulating the form control as needed.
  2. Spiltting up the mat-form-field and input caused the issue -- solved by moving mat-form-field element to child , other code stays intact and this solves both the placeholder overlapping and clicking on arrow icon to display
  3. This can be done-[one way of doing by redesigning] , by injecting the service into child component and doing the autocomplete feature over there [i haven't implemented this, but this will work as it just copy of department field]

在create-doiser.component.html

in 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

in 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 autocomplete.component.ts

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

create-dosier.component.ts

create-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,请让我知道. 希望这可以帮助 !!!! 谢谢!!

this will resolve all the issues, Please let me know if you want me to push to github .. Hope this helps !!!! Thanks !!

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

UPDATE: Auto complete , mat-hint now everything is working..

我了解您不希望输入和mat-form-field在一起

I understand that you don't want input and mat-form-field to be together

但是如果只是让mat-h​​int动态显示[取决于 在 formcontrol values],我们可以将formcontrol从父级传递到子级 甚至消除了从中发出输入值的必要性 孩子对父母 在父组件中设置值,[mat-h​​int字段保留在父组件本身中]

but if it is just for mat-hint to show dynamically[which is depending on formcontrol values], we can pass the formcontrol from parent to child which even eliminates the necessity of emitting the input value from child to parent setting the value in parent component and [mat-hint field stays in the parent component itself]

这篇关于角材料-自定义自动完成组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆