Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add replaceControls method to FormArray class #34186

Open
skrtheboss opened this issue Dec 2, 2019 · 3 comments 路 May be fixed by #34210
Open

Add replaceControls method to FormArray class #34186

skrtheboss opened this issue Dec 2, 2019 · 3 comments 路 May be fixed by #34210
Labels
Milestone

Comments

@skrtheboss
Copy link

@skrtheboss skrtheboss commented Dec 2, 2019

馃殌 feature request

Relevant Package

This feature request is for the FormArray class in @angular/form.,

Description

Currently if i use a FormArray in a custom ControlValueAccessor i have to manually remove/add controls based on the writeValues input.

Here is an example:

import { Component, forwardRef, OnDestroy } from '@angular/core';
import { ControlValueAccessor, FormArray, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Observable, ReplaySubject, Subscription } from 'rxjs';
import { filter, map, takeUntil } from 'rxjs/operators';

@Component({
    template: `custom-value-accessor-template`,
    selector: 'my-custom-value-accessor',
    providers: [{
        provide: NG_VALUE_ACCESSOR,
        useExisting: forwardRef(() => MyCustomControlValueAccessor),
        multi: true
    }]
})
export class MyCustomControlValueAccessor<TValue extends unknown = unknown> implements ControlValueAccessor,
    OnDestroy {

    protected readonly control = new FormArray([]);
    protected readonly userValueChanges$: Observable<TValue[]>;
    protected readonly destroyed = new ReplaySubject<void>(1);

    private changesSubscription: Subscription | undefined;
    private pauseChanges = false;
    private _changeFn: (change: TValue[]) => void;
    private _touchedFn: () => void;

    constructor() {
        this.userValueChanges$ = this.control.valueChanges.pipe(
            filter(() => !this.pauseChanges),
            map(() => this.control.getRawValue())
        );
    }

    public writeValue(value: TValue[]): void {
        this.pauseChanges = true;

        while (this.control.length > 0) {
            this.control.removeAt(0);
        }

        value.forEach(item => {
            this.control.push(new FormControl(item));
        });

        this.pauseChanges = false;
    }

    public ngOnDestroy(): void {
        this.destroyed.next();
        this.destroyed.complete();
    }

    public registerOnChange(fn: (change: TValue[]) => void): void {
        this._changeFn = fn;

        if (!this.changesSubscription) {
            this.userValueChanges$.pipe(
                takeUntil(this.destroyed)
            ).subscribe(
                val => this.change(val)
            );
        }
    }

    public registerOnTouched(fn: any): void {
        this.touched = fn;
    }

    public change(value: TValue[]): void {
        if (this._changeFn) this._changeFn(value);
    }

    public touched(): void {
        if (this._touchedFn) this._touchedFn();
    }
}

Describe the solution you'd like

It would be nice if the FormArray would implement a replaceControls method:

    /**
     * Replace the current controls with new ones.
     *
     * @usageNotes
     * ### Replace controls of a FormArray
     *
     * ```typescript
     * const arr = new FormArray([
     *   new FormControl(0),
     *   new FormControl(1),
     *   new FormControl(2)
     * ]);
     *
     * console.log(arr.value);   // [1, 2, 0]
     *
     * arr.replaceControls([new FormControl(2)]);
     *
     * console.log(arr.value);   // [2]
     * ```
     *
     * @param controls The new controls which will replace the old ones
     */
    public replaceControls(controls: AbstractControl[]): void {
        if(this.controls.length) {
            this.controls.forEach(control => (control as any)._registerOnCollectionChange(() => undefined));
            this.controls.splice(0);
        }

        controls.forEach(control => {
            this.controls.push(control);
            (this as any)._registerControl(control);
        });

        this.updateValueAndValidity();
        (this as any)._onCollectionChange();
    }
@ngfelixl

This comment has been minimized.

Copy link
Contributor

@ngfelixl ngfelixl commented Dec 2, 2019

Thanks, @skrtheboss. I would like to go for it.

@ngbot ngbot bot added this to the needsTriage milestone Dec 3, 2019
ngfelixl added a commit to ngfelixl/angular that referenced this issue Dec 3, 2019
Implement replaceControls(controls: AbstractControl[]) memberfunction
for FormArray. It replaces all existing FormControls, unregisters them,
then adds the new form controls with registration. In the end it runs
updateValueAndValidity and _onCollectionChange.

Closes angular#34186
@ngfelixl ngfelixl linked a pull request that will close this issue Dec 3, 2019
4 of 14 tasks complete
ngfelixl added a commit to ngfelixl/angular that referenced this issue Dec 3, 2019
Implement replaceControls(controls: AbstractControl[]) memberfunction
for FormArray. It replaces all existing FormControls, unregisters them,
then adds the new form controls with registration. In the end it runs
updateValueAndValidity and _onCollectionChange.

Closes angular#34186
ngfelixl added a commit to ngfelixl/angular that referenced this issue Dec 3, 2019
Implement replaceControls(controls: AbstractControl[]) memberfunction
for FormArray. It replaces all existing FormControls, unregisters them,
then adds the new form controls with registration. In the end it runs
updateValueAndValidity and _onCollectionChange.

Closes angular#34186
@CodeEpiphany

This comment has been minimized.

Copy link

@CodeEpiphany CodeEpiphany commented Dec 3, 2019

Glad I'm not the only one wanting this. ha!

@jsachica

This comment has been minimized.

Copy link

@jsachica jsachica commented Dec 7, 2019

This would be very very handy indeed!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
5 participants
You can鈥檛 perform that action at this time.