taiga-family / ng-polymorpheus

Polymorpheus is a tiny library for polymorphic templates in Angular.

Home Page:https://stackblitz.com/edit/polymorpheus-demo

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[FEATURE] : use formcontrolName in input, combo, etc

xavierclotet opened this issue Β· comments

commented

πŸš€ Feature request

Is your feature request related to a problem?

Would be interesting to use those components in a formgroup.

Describe the solution you'd like

Use like

<app-input formControlname="firstName">

Hi @xavierclotet. Thank you for a first issue on this repository :) Components that you see in the demo project are just sketches to demonstrate library capabilities. Indeed, in our real life cases they implement ControlValueAccessor interface and able to work with forms and ngModel, however here they serve a specific role. Adding extra code to make them closer to what you could actually use in your applications would dilute what they are meant to show.

That said, here's the code for app-input to use it with forms. If you need something else, you can use it as basis. One day, I hope, we might release a polymorpheus based UI components library, but that's just a dream at this point.

import {
    ChangeDetectionStrategy,
    ChangeDetectorRef,
    Component,
    forwardRef,
    Input,
} from '@angular/core';
import {PolymorpheusContent} from '@tinkoff/ng-polymorpheus';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';

@Component({
    selector: 'app-input',
    templateUrl: './input.template.html',
    styleUrls: ['./input.style.less'],
    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => InputComponent),
            multi: true,
        },
    ],
    changeDetection: ChangeDetectionStrategy.OnPush,
})
export class InputComponent implements ControlValueAccessor {
    @Input()
    content: PolymorpheusContent<never> | null = null;

    @Input()
    placeholder = '';

    value = '';

    disabled = false;

    onChange: Function = () => {};

    onTouched: Function = () => {};

    constructor(private readonly changeDetectorRef: ChangeDetectorRef) {}

    writeValue(value: any) {
        this.value = value ? String(value) : '';
        this.changeDetectorRef.markForCheck();
    }

    setDisabledState(disabled: boolean) {
        this.disabled = disabled;
        this.changeDetectorRef.markForCheck();
    }

    registerOnTouched(onTouched: Function) {
        this.onTouched = onTouched;
    }

    registerOnChange(onChange: Function) {
        this.onChange = onChange;
    }

    onMouseDown(event: MouseEvent, input: HTMLInputElement) {
        event.preventDefault();
        input.focus();
    }

    onBlur() {
        this.onTouched();
    }

    onValueChange(value: string) {
        this.value = value;
        this.onChange(value);
    }
}
<input
    #input
    type="text"
    class="input"
    [placeholder]="placeholder"
    [disabled]="disabled"
    [ngModel]="value"
    (ngModelChange)="onValueChange($event)"
    (blur)="onBlur()"
/>
<polymorpheus-outlet
    class="outlet"
    [content]="content"
    (mousedown)="onMouseDown($event, input)"
>
    <ng-template let-icon><div [innerHTML]="icon"></div></ng-template>
</polymorpheus-outlet>

Note that to keep things declarative we still have internal value property and do not access native input element directly. That's why we need changeDetectorRef.markForCheck() to trigger change detection when control value has been changed externally.

commented

Ok thanks! but this is not supporting formControlname, you need an @input() group: FormGroup

That is a proper way to implement custom control. You can use it like that:

<form [formGroup]="group">
  <app-input formControlName="name"/>
</form>

<app-input [formControl]="control"/>

<app-input [(ngModel)]="value"/>
commented

Ok thank you very much!