3

I'm trying to create a custom component (a customized autocomplete field) but I want to work for both reactive forms and template forms So sometimes the value will come through [(ngModel)] and sometimes i want to provide formControlName

Until now I've had 2 different templates, but everywhere i look it seems as if NG_VALUE_ACCESSOR is handling it by itself, there's just something missing in my implementation.

To make my question clearer If you use a PrimeNg component or any framework, the same component can take an [(ngModel)] or a formControlName and it behaves as a normal component in both cases without special treatment, this is what i want to do

Similar question with same issue :

How to wrap a primeng component like autocomplete using reactive forms?

7
  • must implements ControlValueAccessor, there are full plenty SO, e.g. stackoverflow.com/questions/40009149/… Commented Jul 28, 2020 at 9:14
  • Yes of course I already implemented ControlValueAccessor, It's just having one template for both template and reactive form that's the issue. Commented Jul 28, 2020 at 10:14
  • @DanyY you can emit Output value from your custom component and set that value to form control in other component. Commented Jul 28, 2020 at 10:22
  • I don't know if my question is clear, but for example if you use a primeng component, the same component can take an [(ngModel)] or a formControlName and it behaves as a normal component in both cases without special treatment, this is what i want to do. Commented Jul 28, 2020 at 10:23
  • 1
    Danny, all component that implements ControlValueAccesor create a coponent that you can use in a ReactiveForm and in template Driven From. you has severals examples, another e.g. stackblitz.com/edit/…. See that you need has as provider of the component some like providers: [{provide:NG_VALUE_ACCESSOR,useExisting: forwardRef(() =>YourCustomComponent),multi: true}] Really I don't know how help you. If you create a stackblitz or edite your question adding the code you use... Commented Jul 28, 2020 at 16:41

1 Answer 1

0

This is an old question, but I was struggling with the same problem. In case anybody stumbles across this, here's how I interpret what is being asked, and my solution.

My component needs to be able to receive a [value] and emit some (output), while ALSO being able to simply receive a formControlName. I too needed to solve this problem. Use the following example:

<my-button-toggle formControlName="toggleValue"></my-button-toggle>

OR

<my-button-toggle
  value="A"
  (toggleChange)="handleChange($event)"
></my-button-toggle>

I fought with this for a while, but after looking at this repo it finally clicked for me.

Here's the html structure of my wrapped button-toggle component

<mat-button-toggle-group
  [disabled]="disabled"
  [multiple]="multiple"
  [vertical]="vertical"
  [value]="value"
  (change)="doChange($event)">
...

And in the ts, we allow for formControlName as well as value setting:

export interface ToggleItem {
  disabled?: boolean
  value?: any
  label: string
}

@Component({
  selector: 'my-button-toggle',
  templateUrl: './button-toggle.component.html',
  styleUrls: ['./button-toggle.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: ButtonToggleComponent,
      multi: true,
    },
  ],
})
export class ButtonToggleComponent implements ControlValueAccessor {
  @Input() value: any = ''
  @Input() disabled = false
  @Input() multiple = false
  @Input() vertical = false
  @Input() toggles: ToggleItem[] = []
  @Output() toggleChange = new EventEmitter<any>()

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

  writeValue(value: string): void {
    this.value = value
  }

  registerOnChange(fn: any): void {
    this.onChange = fn
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn
  }

  setDisabledState?(isDisabled: boolean): void {
    this.disabled = isDisabled
  }

  doChange = (x: MatButtonToggleChange) => {
    this.value = x.value
    this.onChange(x.value)
    this.toggleChange.emit(x.value)
  }
}

So, what's happening is that when the button-toggle's (change) event is fired, it calls the this.onChange internally, which is handled by the ControlValueAccessor, but then I also tell my custom output to emit the value, so that any other type of non-reactive-form use case can still talk to the component.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.