如何动态地将mat-error添加到mat-input-field?

时间:2019-05-01 08:39:26

标签: angular typescript angular-material

我想通过将动态添加到DOM来在用户超过maxLength时显示错误。

我已经有一个属性指令来限制输入字段的最大长度。我将其作为指令,因为这已应用于项目中不同文件之间的许多输入字段。但是现在的问题是,当用户超出限制时,我必须显示 mat-error 。我不想自己在所有文件的每个输入字段下添加,而是想要一个 modular 解决方案。可以使用现有指令本身来完成此操作吗?

<mat-form-field floatLabel="auto">
      <input [formControl]="displayNameControl"
        mongoIndexLimit
        [charLength]="charLength"
        matInput
        name="displayName"
        placeholder="Stack Name"
        autocomplete="off"
        required />
    </mat-form-field>

这是我的指令

import { Directive, OnInit, NgModule, ElementRef, OnChanges, Input, SimpleChanges, Renderer2 } from '@angular/core';

@Directive({
  selector: '[mongoIndexLimit]'
})
export class MongoIndexLimitDirective implements OnInit, OnChanges {
  @Input() public charLength?: number;
  private maxLength = 5;
  constructor(
    private el: ElementRef<HTMLElement>,
    private renderer: Renderer2
  ) { }

  public ngOnInit() {
    this.el.nativeElement.setAttribute('maxLength', this.maxLength.toString());
  }

  public ngOnChanges(changes: SimpleChanges) {
    if (changes.charLength.currentValue >= 5) {
      const child = document.createElement('mat-error');
      this.renderer.appendChild(this.el.nativeElement.parentElement.parentElement.parentElement, child);
    }
  }

}

当我尝试上述操作时,我可以将元素附加到DOM,但是angular不会将其视为编译器 angular材料。它只是一个虚拟的,而不是重要的组成部分。

我希望结果是设置了maxLength的输入组件,并且动态生成的mat-error会显示超出限制的时间,就像下面的示例中一样。

https://material.angular.io/components/input/examples (标题为带有自定义错误状态匹配器的输入)

对不起,我的英语不好。

3 个答案:

答案 0 :(得分:2)

当然,您可以在逻辑上添加一个垫错误。 NetBasal中有一个amazing article

我在stackblitz中输入了一个简单的版本。在这次堆叠闪电战中,我将指令附加到mat-form-field并进行变通,以附加新组件mat-error-component。这使我可以使用CSS和动画。

关键是使用ViewContainerRef通过ComponentFactoryResolver动态添加组件

好,指令的代码:

export class MongoIndexLimitDirective implements AfterViewInit {
  ref: ComponentRef<MatErrorComponent>;
  constructor(
    private vcr: ViewContainerRef,
    private resolver: ComponentFactoryResolver,
    private formField:MatFormField
  ) { }

  public ngAfterViewInit()
  {
    this.formField._control.ngControl.statusChanges.subscribe(res=>this.onChange(res))

  }

  public onChange(res) {
    if (this.formField._control.ngControl.invalid)
    {
      this.setError('error')
    }      
    else
      this.setError('')
  }
  setError(text: string) {
    if (!this.ref) {
     const factory = this.resolver.resolveComponentFactory(MatErrorComponent);
     this.formField._elementRef
     this.ref = this.vcr.createComponent(factory);
   }
   this.ref.instance.error=text;
}

MatErrorComponent(为方便起见,我称其为。要小心地将其放入主模块的entryComponents中)看起来比实际要复杂,这是因为“动画”,但本质上是<mat-error>{{message}}</mat-error>

@Component({
  selector: 'custom-error',
  template:`
  <div [@animation]="_state" style="margin-top:-1rem;font-size:.75rem">
      <mat-error >
      {{message}}
    </mat-error>
    </div>
  `,
  animations: [
    trigger('animation', [
      state('show', style({
        opacity: 1,
      })),
      state('hide',   style({
        opacity: 0,
        transform: 'translateY(-1rem)'
      })),
      transition('show => hide', animate('200ms ease-out')),
      transition('* => show', animate('200ms ease-in'))

    ]),
  ]

})
export class MatErrorComponent{
  _error:any
  _state:any
  message;

  @Input() 
  set error(value)
  {
    if (value && !this.message)
    {
      this.message=value;
      this._state='hide'
      setTimeout(()=>
      {
        this._state='show'
      })
    }
    else{
    this._error=value;
    this._state=value?'show':'hide'
    }
  }

已更新是对mat-error-component更好的理解。

我们可以考虑差异错误,并像这样改善过渡

@Component({
  selector: '[custom-error]',
  template: `
  <div [@animation]="increment" *ngIf="show" style="margin-top:-1rem;font-size:.75rem">
      <mat-error >
      {{message}}
    </mat-error>
    </div>
  `,
  animations: [
    trigger('animation', [
      transition(':increment', [
        style({ opacity: 0}),
        animate('200ms ease-in', style({ opacity: 1 })),
      ]),
      transition(':enter', [
        style({ opacity: 0, transform: 'translateY(-1rem)' }),
        animate('200ms ease-in', style({ opacity: 1, transform: 'translateY(0)' })),
      ]),
      transition(':leave', [
        animate('200ms ease-out', style({ opacity: 0, transform: 'translateY(-1rem)' }))
      ])])
  ]

})
export class MatErrorComponent {
  show: boolean
  message: string;
  increment:number=0;

  @Input()
  set error(value) {
    if (value)
    {
      if (this.message!=value)
        this.increment++;

      this.message = value;
    }

    this.show = value ? true : false;
  }
} 

这是因为当消息错误更改时,会出现一个新的动画-在这种情况下,例如,将不透明度从0更改为1。在我们的指令中,将函数onChange更改为

  public onChange(res) {
    if (this.control.invalid)
    {
      if (this.control.errors.required)
        this.setError(this.formField._control.placeholder+' required')
      else
        this.setError(this.formField._control.placeholder+' incorrect')
    }      
    else
      this.setError('')
  }

请参见improve stackblitz

答案 1 :(得分:0)

您不会动态添加Could not find symbol '&shuffle',只需将其放在此处即可。它严格与mat-error合作,并且如果mat-form-field处于matInput状态,它将变为可见。

将其视为错误消息的容器。您所要做的就是调整消息(以防您可能有多个验证规则,并且想要为所有验证规则定制消息)。

Angular Material文档中的代码

invalid

答案 2 :(得分:0)

是的,您可以为matInput动态插入错误

<mat-form-field>
    <mat-label>{{item.label}}</mat-label>
    <input type="text" matInput [formControlName]="item.name">
    <mat-error *ngIf="form.get(item.name).invalid">{{getErrorMessage(form.get(item.name))}}</mat-error>
</mat-form-field>