San*_*_36 5 typescript angular-material angular
我想通过将 < mat-error > 动态添加到 DOM 来在用户超过 maxLength 时显示错误。
我已经有一个属性指令来限制输入字段的最大长度。我把它作为一个指令,因为它被应用于项目中不同文件的许多输入字段。但现在的问题是,当用户超过限制时,我必须显示一个mat-error。我不想自己在所有文件的每个输入字段下添加 < mat-error > ,我想要一个模块化的解决方案。这可以通过使用现有指令本身来完成吗?
<mat-form-field floatLabel="auto">
<input [formControl]="displayNameControl"
mongoIndexLimit
[charLength]="charLength"
matInput
name="displayName"
placeholder="Stack Name"
autocomplete="off"
required />
</mat-form-field>
Run Code Online (Sandbox Code Playgroud)
这是我的指示
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
当我尝试上述操作时,我能够将 < mat-error > 元素附加到 DOM,但 angular 不会将其视为编译器 < mat-error > 角度材料。它只是一个虚拟的 < mat-error > 而不是材料组件。
我希望结果是设置了 maxLength 和动态生成的 mat-error 的输入组件,该错误显示何时超出限制,就像下面的示例一样。
https://material.angular.io/components/input/examples (标题为带有自定义错误状态匹配器的输入)
对不起,我的英语不好 。
当然,您可以动态地添加一个 mat-error。NetBasal 中有一篇关于此的精彩文章。
我制作的一个简单版本是在stackblitz 中。在此 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;
}
Run Code Online (Sandbox Code Playgroud)
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'
}
}
Run Code Online (Sandbox Code Playgroud)
更新了 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;
}
}
Run Code Online (Sandbox Code Playgroud)
这允许当消息错误改变时,一个新的动画发生 - 在这种情况下,如果将不透明度从 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('')
}
Run Code Online (Sandbox Code Playgroud)
查看改进堆栈闪电战
更新 2 存在模糊问题。如果最初控件无效,状态不会改变,因此我们需要添加模糊事件。为此,我们使用 renderer2 和 ViewContent 来获取输入
@ContentChild(MatInput,{read:ElementRef}) controlElementRef:ElementRef
Run Code Online (Sandbox Code Playgroud)
并更改 ngAfterViewInit
public ngAfterViewInit()
{
this.control=this.formField._control.ngControl;
this.renderer.listen(this.controlElementRef.nativeElement,'blur',()=>this.onChange(null))
this.control.statusChanges.subscribe(res=>this.onChange(res))
}
Run Code Online (Sandbox Code Playgroud)
如果可以的话,我们可以有一个预定义的错误,最后在自定义错误中添加一个“错误”,这样如果我们的自定义错误返回一些类似{error:'error text'}的错误,我们就可以显示错误。
重要的部分是
export const defaultErrors = {
minlength: ({ requiredLength, actualLength }) =>
`Expect ${requiredLength} but got ${actualLength}`,
email: error=>'The email is incorrect',
error:error=>error,
required: error => `This field is required`
};
Run Code Online (Sandbox Code Playgroud)
并且 OnChnage 变得像
public onChange(res) {
if (this.control.invalid && this.control.touched) {
let error: string = this.formField._control.placeholder + " incorrect";
Object.keys(defaultErrors).forEach(k => {
console.log(k,this.control.hasError(k),this.control.errors[k])
if (this.control.hasError(k)) error = defaultErrors[k](this.control.errors[k]);
});
this.setError(error);
} else this.setError("");
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
9698 次 |
| 最近记录: |