Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PrimeNG dropdown selected item data binding

Tags:

I am simply trying to bind data from a dropdown menu with ngModel. There is an error I receive when the app loads that makes some sense.

browser_adapter.js:84 EXCEPTION: No value accessor for ''

This leads me to believe that the error is stemming from the fact that ngModel is not initially bound with any data when the app loads.

I'm not the best at using Observables... so beware.

Partial of the html dropdown

<p-dropdown [options]="actionsToTake" (onChange)="onToggleCreateActionInput()" 
  [(ngModel)]="action"></p-dropdown>

Relevant TypeScript (excluded imports)

export class ActionView {
  public actionsToTake: SelectItem[] = [];
  public action: Action = new Action();

  constructor (private actionCreateService: ActionCreateService) {
    // populate dropdown list (actionsToTake) with data from service call
    this.actionCreateService.getActionFields().subscribe((resp) => {
      for (let i = 0; i < resp.data.data.actionElm.length; i++) {
        this.actionsToTake.push({label: resp.data.data.actionElm[i].name,
          value: resp.data.data.actionElm[i].name});
      }
    });
  }

  public onToggleCreateActionInput = (action): void => {
    // test if something in "action" exists, and then do something based on that
  };
}

So, when the app initially loads, action is empty. I would expect that an empty value bound to ngModel wouldn't break the app, but maybe I am misinterpreting the error. Ultimately I want the selected item to be bound, and I think getting past this error will get me to that point.

like image 762
BrianRT Avatar asked Jul 06 '16 15:07

BrianRT


1 Answers

I haven't test it yet, but from what I've read it seems that you'll have to update your app to use the new forms and disable the old deprecated version.

import {bootstrap} from '@angular/platform-browser-dynamic';
import {provide} from '@angular/core';
import {AppComponent} from './app.component'
import {disableDeprecatedForms, provideForms} from '@angular/forms';

bootstrap(AppComponent, [
  disableDeprecatedForms(),
  provideForms(),
]);

Related resources https://github.com/primefaces/primeng/issues/549#issuecomment-230305403

http://forum.primefaces.org/viewtopic.php?f=35&t=46115&p=144059&hilit=no+value+accessor+for#p144059

EDIT!

For nested components you'll have to implement a Control Value Accessor inside the child component in order to allow the parent component to take control over it. I have an example where I implement a custom toggle component, but this component also uses prime ng toggle component.

This is how the child component looks like:

import {Component,Input, Provider, forwardRef} from '@angular/core'
import {ControlValueAccessor, NG_VALUE_ACCESSOR, CORE_DIRECTIVES} from '@angular/common';

import {ToggleButton} from 'primeng/primeng'

const noop = () => {};

const CUSTOM_VALUE_ACCESSOR = new Provider(
    NG_VALUE_ACCESSOR, {
        useExisting: forwardRef(() => CustomToggle),
        multi: true
    });

@Component({
    selector: 'custom-toggle',
    template: `<span>
                  <p-toggleButton
                     [(ngModel)]="value"   >
                  </p-toggleButton>
               </span>`,
    directives: [CORE_DIRECTIVES,ToggleButton],
    providers: [CUSTOM_VALUE_ACCESSOR]
})

export class CustomToggle implements ControlValueAccessor {           
           
    private _value: string;
    
    private _onTouchedCallback: (_:any) => void = noop;
    private _onChangeCallback: (_:any) => void = noop;
    
    get value(): any { return this._value};
    
    set value(v: any) {
        if (v !== this._value) {
            this._value = v;
            this._onChangeCallback(v);
        }
    }
    
    onTouched() {
        this._onChangeCallback;
    }
    
    writeValue(value: any) {
        this._value = value;
    }
    
    registerOnChange(fn: any) {
        this._onChangeCallback = fn;
    }
    
    registerOnTouched(fn: any) {
        this._onTouchedCallback = fn;
    }
    
}

And this is how it can be used inside parent components

<custom-toggle [(ngModel)]="myToggle"  ></custom-toggle>

Where myToggle is just a boolean variable.

More on how to implement a control value accessor: http://almerosteyn.com/2016/04/linkup-custom-control-to-ngcontrol-ngmodel

like image 62
Daniel Pliscki Avatar answered Sep 28 '22 01:09

Daniel Pliscki