ドロップダウンメニューのデータをngModel
でバインドしようとしています。アプリの読み込み時に受け取るエラーがあり、それは理にかなっています。
browser_adapter.js:84 EXCEPTION: No value accessor for ''
これにより、エラーは、アプリの読み込み時にngModel
が最初はデータにバインドされていないという事実に起因していると思われます。
私はObservablesを使うのが得意ではありません...だから注意してください。
htmlドロップダウンの一部
<p-dropdown [options]="actionsToTake" (onChange)="onToggleCreateActionInput()"
[(ngModel)]="action"></p-dropdown>
関連するTypeScript(インポートを除く)
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
};
}
そのため、アプリが最初に読み込まれるとき、action
は空です。 ngModel
にバインドされた空の値はアプリを壊さないと思いますが、エラーを誤って解釈している可能性があります。最終的には、選択したアイテムをバインドする必要があります。このエラーを乗り越えると、その時点に到達できると思います。
非推奨のAngularフォームと新しいAngularフォームを使用することの中間にいたことがわかりました。PrimeNGを使用するには、新しいフォームにアップグレードして、これは、アプリケーションのブートストラップにあります。
_import {disableDeprecatedForms, provideForms} from '@angular/forms';
bootstrap(App, [
disableDeprecatedForms(),
provideForms()
]);
_
次に、フォームをインスタンス化する親コンポーネントで、インポートを次の場所から変更する必要がありました。
_import {NgForm, FORM_DIRECTIVES, CORE_DIRECTIVES} from '@angular/common';
_
に
_import {NgForm, FORM_DIRECTIVES, NgModel} from '@angular/forms';
import {CORE_DIRECTIVES} from '@angular/common';
_
子コンポーネントでは、これらのインポートは必要ありません。 [(ngModel)]
がどこにあっても、_[ngModelOptions]="{standalone: true}"
_を含める必要があります。だから、私の場合:
_<p-dropdown [(ngModel)]="actions" [ngModelOptions]="{standalone: true}"></p-dropdown>
_
まだテストしていませんが、読んだところによると、新しいフォームを使用するにはアプリを更新し、古い非推奨バージョンを無効にする必要があるようです。
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(),
]);
関連リソース https://github.com/primefaces/primeng/issues/549#issuecomment-2303054
http://forum.primefaces.org/viewtopic.php?f=35&t=46115&p=144059&hilit=no+value+accessor+for#p144059
編集!
ネストされたコンポーネントの場合、親コンポーネントが子コンポーネントを制御できるようにするには、子コンポーネント内に制御値アクセサーを実装する必要があります。カスタムトグルコンポーネントを実装する例がありますが、このコンポーネントはプライミングトグルコンポーネントも使用しています。
子コンポーネントは次のようになります。
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;
}
}
そして、これが親コンポーネント内での使用方法です
<custom-toggle [(ngModel)]="myToggle" ></custom-toggle>
MyToggleは単なるブール変数です。
制御値アクセサーの実装方法の詳細: http://almerosteyn.com/2016/04/linkup-custom-control-to-ngcontrol-ngmodel