javascript - Angular: custom input with ControlValueAccessor - Stack Overflow

I'm not sure how can I use a custom ponent if it's wrapper under another ponent.Like:Componen

I'm not sure how can I use a custom ponent if it's wrapper under another ponent.

Like:

ComponentA_withForm
|
--ComponentA1_withWrapperOfCustomInput
  |
  --ComponentA11_withCustomInput

if I have a structure like this:

ComponentA_withForm
|
--ComponentA11_withCustomInput

Everything's fine

But for my case (tons of async data) I need a wrapper... Is it possible somehow to do this?

Here is my fiddle code:

ComponentA:

import { Component } from '@angular/core';
import { FormBuilder } from '@angular/forms';

@Component({
  selector: 'my-app',
  template: `<form [formGroup]="form"><custom-input-wrapper formControlName="someInput"></custom-input-wrapper></form> <p>value is: {{formVal | json}}</p>`
})
export class AppComponent {
  form = this.fb.group({
    someInput: [],
  });

  get formVal() {
    return this.form.getRawValue();
  }

  constructor(private fb: FormBuilder) { }
}

ComponentA1:

import { Component } from '@angular/core';

@Component({
  selector: 'custom-input-wrapper',
  template: '<custom-input></custom-input>',
})
export class CustomInputWrapperComponent {
  constructor() { }
}

ComponentA11:

import { Component, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'custom-input',
  template: `Hey there! <button (click)="inc()">Value: {{ value }}</button>`,
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => CustomInputComponent),
    multi: true,
  }],
})
export class CustomInputComponent implements ControlValueAccessor {

  private value = 0;

  writeValue(value: number): void {
    this.value = value;
  }

  registerOnChange(fn: (_: any) => void): void {
    this.onChangeFn = fn;
  }

  registerOnTouched(fn: any): void {
  }

  inc() {
    this.value = this.value + 1;
    this.onChangeFn(this.value);
  }

  onChangeFn = (_: any) => { };
}

And here I have a working sample:

so: basically removing & refactoring code not to use CustomInputWrapperComponent makes my code working. But I need this wrapper and I'm not sure how to pass formControlName then.

I don't want a dirty solution with passing parent formGroup :)

I'm not sure how can I use a custom ponent if it's wrapper under another ponent.

Like:

ComponentA_withForm
|
--ComponentA1_withWrapperOfCustomInput
  |
  --ComponentA11_withCustomInput

if I have a structure like this:

ComponentA_withForm
|
--ComponentA11_withCustomInput

Everything's fine

But for my case (tons of async data) I need a wrapper... Is it possible somehow to do this?

Here is my fiddle code:

ComponentA:

import { Component } from '@angular/core';
import { FormBuilder } from '@angular/forms';

@Component({
  selector: 'my-app',
  template: `<form [formGroup]="form"><custom-input-wrapper formControlName="someInput"></custom-input-wrapper></form> <p>value is: {{formVal | json}}</p>`
})
export class AppComponent {
  form = this.fb.group({
    someInput: [],
  });

  get formVal() {
    return this.form.getRawValue();
  }

  constructor(private fb: FormBuilder) { }
}

ComponentA1:

import { Component } from '@angular/core';

@Component({
  selector: 'custom-input-wrapper',
  template: '<custom-input></custom-input>',
})
export class CustomInputWrapperComponent {
  constructor() { }
}

ComponentA11:

import { Component, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'custom-input',
  template: `Hey there! <button (click)="inc()">Value: {{ value }}</button>`,
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => CustomInputComponent),
    multi: true,
  }],
})
export class CustomInputComponent implements ControlValueAccessor {

  private value = 0;

  writeValue(value: number): void {
    this.value = value;
  }

  registerOnChange(fn: (_: any) => void): void {
    this.onChangeFn = fn;
  }

  registerOnTouched(fn: any): void {
  }

  inc() {
    this.value = this.value + 1;
    this.onChangeFn(this.value);
  }

  onChangeFn = (_: any) => { };
}

And here I have a working sample: https://stackblitz./edit/angular-qmrj3a

so: basically removing & refactoring code not to use CustomInputWrapperComponent makes my code working. But I need this wrapper and I'm not sure how to pass formControlName then.

I don't want a dirty solution with passing parent formGroup :)

Share Improve this question edited Jun 19, 2019 at 13:41 byCoder asked Jun 19, 2019 at 13:20 byCoderbyCoder 9,18427 gold badges120 silver badges259 bronze badges 3
  • Why not implement ControlValueAccessor in your CustomInputWrapperComponent as well ? – abd995 Commented Jun 19, 2019 at 13:45
  • @abd995I think it's dirty way as well :) – byCoder Commented Jun 19, 2019 at 14:00
  • I think that's most cleanest solution you could possibly have. Passing in the form control or form group is I guess the dirty solution. – abd995 Commented Jun 19, 2019 at 14:02
Add a ment  | 

2 Answers 2

Reset to default 4

Since you don't want a dirty solution ;) , you could just implement ControlValueAccessor in the CustomInputWrapperComponent also. That way any change in the parent will be reflected in the child, any change in the child will be reflected in the parent as well with just few lines of code.

Wrapper Component

@Component({
  selector: 'custom-input-wrapper',
  template: '<custom-input [formControl]="value"></custom-input>',
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => CustomInputWrapperComponent),
    multi: true,
  }]
})
export class CustomInputWrapperComponent implements AfterViewInit, ControlValueAccessor  {
  public value = new FormControl();

  constructor() { }

  ngAfterViewInit() {
    this.value.valueChanges.subscribe((x) => {
      this.onChangeFn(x);
    });
  }

  writeValue(value: number): void {
    this.value.setValue(value);
  }

  registerOnChange(fn: (_: any) => void): void {
    this.onChangeFn = fn;
  }

  registerOnTouched(fn: any): void {
  }

 onChangeFn = (_: any) => { };
}

Parent Template

<form [formGroup]="form"><custom-input-wrapper formControlName="someInput"></custom-input-wrapper></form> <p>value is: {{formVal | json}}</p>

I have made a stackbitz demo here - https://stackblitz./edit/angular-csaxcz

you cannot use formControlName on custom-input-wrapper because it doesn't implement ControlValueAccessor. implementing ControlValueAccessor on custom-input-wrapper might be a solution but it seems to be overkill. Instead pass the control from formGroup to custom-input-wrapper as an @Input() and pass the inputed formControl to custom-input

app.ponent

@Component({
  selector: 'my-app',
  template: `<form [formGroup]="form"><custom-input-wrapper [formCtrl]="form.get('someInput')"></custom-input-wrapper></form> <p>value is: {{formVal | json}}</p>`
})
export class AppComponent {
  form = this.fb.group({
    someInput: [],
  });

  get formVal() {
    return this.form.getRawValue();
  }

  constructor(private fb: FormBuilder) { }
}

custom-input-wrapper.ponent

@Component({
  selector: 'custom-input-wrapper',
  template: '<custom-input [formControl]="formCtrl"></custom-input>',
})
export class CustomInputWrapperComponent {
  @Input() formCtrl: AbstractControl;
  constructor() { }
}

here is a working demo https://stackblitz./edit/angular-3lrfqv

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744188301a4562311.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信