Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular Material <mat-error> not showing in Custom Components

So I've been doing lots of research but I just can't figure it out.

I want to make a Textbox component using Angular material form controls

Following this tutorial, I've implemented it as follows

textbox.component.html

<mat-form-field>
    <input matInput type="text" [placeholder]="placeholder" [(ngModel)]="value" />
    <mat-error>This field is required</mat-error>
</mat-form-field>

textbox.component.ts

import { Component, Input, forwardRef, OnDestroy, ElementRef } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor, FormControl, NgControl } from '@angular/forms';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { MatFormFieldControl } from '@angular/material';
import { Subject } from 'rxjs';
import { FocusMonitor } from '@angular/cdk/a11y';

@Component({
    selector: 'text-box',
    templateUrl: './text-box.component.html',
    styleUrls: ['./text-box.component.scss'],
    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => TextBoxComponent),
            multi: true
        },
        {
            provide: MatFormFieldControl,
            useExisting: TextBoxComponent
        }
    ]
})
export class TextBoxComponent implements ControlValueAccessor, MatFormFieldControl<any>, OnDestroy {
    static nextId = 0;

    stateChanges: Subject<void> = new Subject<void>();
    id: string = `text-box-${TextBoxComponent.nextId++}`;
    ngControl: NgControl = null;
    focused: boolean = false;
    empty: boolean;
    shouldLabelFloat: boolean;
    disabled: boolean = false;
    errorState: boolean = false;
    controlType?: string = 'text-box';
    autofilled?: boolean;
    describedBy: string = '';

    @Input()
    get placeholder(): string {
        return this._placeholder;
    }
    set placeholder(value: string) {
        this._placeholder = value;
        this.stateChanges.next();
    }
    private _placeholder: string;

    @Input()
    get required(): boolean {
        return this._required;
    }
    set required(value: boolean) {
        this._required = coerceBooleanProperty(value);
        this.stateChanges.next();
    }
    private _required = false;

    @Input() name: string;

    onChange: any = () => {};
    onTouched: any = () => {};

    isDisabled: boolean = false;

    @Input('value') val: string;
    get value(): any {
        return this.val;
    }
    set value(val: any) {
        this.val = val;
        this.errorState = !val;
        this.onChange(val);
        this.onTouched();
        this.stateChanges.next();
    }

    constructor(private fm: FocusMonitor, private elRef: ElementRef<HTMLElement>) {
        fm.monitor(elRef, true).subscribe(origin => {
            this.focused = !!origin;
            this.stateChanges.next();
        });
    }

    writeValue(value: any): void {
        if (value) {
            this.value = value;
        }
    }
    registerOnChange(fn: any): void {
        this.onChange = fn;
    }
    registerOnTouched(fn: any): void {
        this.onTouched = fn;
    }
    setDisabledState?(isDisabled: boolean): void {
        this.isDisabled = isDisabled;
    }

    setDescribedByIds(ids: string[]): void {
        this.describedBy = ids.join(' ');
    }
    onContainerClick(event: MouseEvent): void {
        if ((event.target as Element).tagName.toLowerCase() != 'input') {
            this.elRef.nativeElement.querySelector('input')!.focus();
        }
    }

    ngOnDestroy(): void {
        this.stateChanges.complete();
        this.fm.stopMonitoring(this.elRef);
    }
}

and Basically use it in a form like that:

<form [formGroup]="someForm" (ngSubmit)="onSubmit()">
    <acs-text-box formControlName="username" [placeholder]="'Form control'"> </acs-text-box>
    <my-button [type]="'submit'">Submit</my-button>
</form>

So here is my question, I'm trying to make render inside the textbox.component.html template but it doesn't work

I've tried several things like setting errorState = true in textbox.component.ts but nothing happens.

I've tried this

<mat-form-field>
    <acs-text-box formControlName="username" [placeholder]="'Form control'"> </acs-text-box>
    <mat-error>This field is required</mat-error>
</mat-form-field>

It works fine with setting errorState = true, but When I take mat-error back inside of the textbox component template it doesn't work.

Is there a way to make render inside the textbox component template? or is it just not doable with angular material and i shall implement it my own way?

Thanks in advance!!

like image 216
Willy Avatar asked Apr 20 '19 17:04

Willy


People also ask

How do I import components from another module in angular?

There's an easy solution for that: Wrap all your Web Components with Angular Components and declare those Angular Components in their own module using CUSTOM_ELEMENTS_SCHEMA. Then, import that wrapper module into your application, thtat MUST NOT use CUSTOM_ELEMENTS_SCHEMA.

Where to find the source code for angular material error?

The source code is available at GitHub Angular Material Error and Details Pages – Source Code We strongly recommend reading our Angular Series prior to reading this article, if you want to restore your knowledge about that topic or to learn Angular development overall. So, let’s start.

How to use custom_elements_schema with angular together with Web Components?

If you're using Angular together with Web Components, you need to add CUSTOM_ELEMENTS_SCHEMA to your module import, like this: But, you're like to use it the wrong way. Using CUSTOM_ELEMENTS_SCHEMA opts out of a lot of useful Angular template checking features.

How to resolve template parse errors 'mat-toolbar is not a known element'?

Template parse errors:'mat-toolbar' is not a known element: 1. If 'mat-toolbar' is an Angular component, then verify that it is part of this module. 2. If 'mat-toolbar' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.


1 Answers

Try to do like this :

<mat-form-field>
    <input matInput type="text" [placeholder]="placeholder" [(ngModel)]="value" required #inputValue="ngModel" />
    <mat-error *ngIf="(inputValue.touched && inputValue.invalid)">This field is required</mat-error>
</mat-form-field>
like image 127
Tushar Avatar answered Nov 15 '22 07:11

Tushar