1

マテリアル デザイン コンポーネントを使用して暫定的なドロップダウン メニューを作成しようとしていますが、機能しない理由がわかりControlValueAccessorません。コードの関連部分は次のとおりです。

import {
    AfterViewInit,
    Component,
    ElementRef,
    forwardRef,
    Input,
    OnInit,
    Provider,
    ViewChild
} from '@angular/core';

import {
    NG_VALUE_ACCESSOR,
    ControlValueAccessor,
    CORE_DIRECTIVES
} from '@angular/common';

import { MdCard } from '@angular2-material/card';

import {
    MdInput,
    MD_INPUT_DIRECTIVES
} from '@angular2-material/input';

import { MD_LIST_DIRECTIVES } from '@angular2-material/list';

declare var module: {
    id: string;
};

export const MD_SELECT_VALUE_ACCESSOR = new Provider(
    NG_VALUE_ACCESSOR, { useExisting: forwardRef(() => MdSelect), multi: true });

const noop = () => {};

@Component({
    selector: 'md-select',
    moduleId: module.id,
    template: `
        <div>
            <md-input readOnly type="text" [placeholder]="placeholder" (click)="selectClick()">
                <i md-suffix class="fa fa-sort-desc"></i>
            </md-input>
            <md-card [ngClass]="{ visible: menuVisible }" (blur)="menuBlur()">
                <md-list>
                    <md-list-item class="md-option" *ngFor="let option of options" (click)="optionClick(option)" [ngClass]="{ 'selected': option.selected }">
                        {{option.name}}
                    </md-list-item>            
                </md-list>
            </md-card>
        </div>
    `,
    styleUrls: [
        'md-select.component.css'
    ],
    directives: [
        CORE_DIRECTIVES,
        MdCard,
        MdInput,
        MD_INPUT_DIRECTIVES,
        MD_LIST_DIRECTIVES
    ],
    providers: [MD_SELECT_VALUE_ACCESSOR]
})
export class MdSelect implements ControlValueAccessor {
    @Input() multiple: boolean;
    @Input() placeholder: string;
    private _value: string;
    onChanged: (_: any) => void = noop;
    onTouched: () => void = noop;
    options: MdOption[] = [];
    menuVisible: boolean = false;
    selectedOption: MdOption;
    private _selectedOptions: MdOption[] = [];
    addOption(option: MdOption) {
        this.options.push(option);
        if (option.selected && (!this.selectedOption || this.multiple)) {
            this.selectedOption = option;
            this.value = this.selectedOption.name;
        }
    }
    selectClick() {
        if (!this.menuVisible) {
            this.menuVisible = true;
        }
    }
    optionClick(option: MdOption) {
        if (option) {
            if (this.multiple) {
                option.selected = !option.selected;
            } else {
                this.options.filter(option => option.selected).forEach(option => option.selected = false);
                option.selected = true;
            }
            this.onChanged('value');
        }
        this.menuBlur();
    }
    menuBlur() {
        this.menuVisible = false;
    }
    get value(): string {
        return this.options.filter(option => option.selected).map(option => option.name).join(', ')
    }
    set value(value: string) {
        if (value !== this._value) {
            this._value = value; // TODO
            this.onChanged('value');
        }
    }
    writeValue(value: any): void {
        console.log('writeValue("' + value + '")')
        this.value = value;
    }
    registerOnChange(fn: (_: any) => void): void {
        this.onChanged = (_: any) => { console.log('onChange("' + _ + '")'); fn(_); };
    }
    registerOnTouched(fn: () => void): void {
        this.onTouched = () => { console.log('onTouched()'); fn(); }
    }
}

@Component({
    selector: 'md-option',
    template: `
        <div #wrapper>
            <ng-content></ng-content>
        </div>    
    `
})
export class MdOption implements AfterViewInit {
    @ViewChild('wrapper') wrapper: ElementRef;
    @Input() disabled: boolean;
    name: string;
    @Input() selected: boolean;
    @Input() value: string;
    constructor(private select: MdSelect) { }
    ngAfterViewInit() {
        if (this.wrapper) {
            let name = this.wrapper.nativeElement.innerHTML;
            this.name = name ? name.trim() : 'EMPTY';
        }
        this.select.addOption(this);
    }
}

これは、それが使用されるテンプレートの一部です

<div class="md-form-control">
    <md-select placeholder="Shift" class="shift" formControlName="shift">
        <md-option *ngFor="let s of shifts" [value]="s.id" [ngValue]="s.id"
            [selected]="s.id === shift.id">
            {{s.name}}
        </md-option>  
    </md-select>
</div>

これがフォームのセットアップです

private initForm() {
    ...
    this.form = this.formBuilder.group({
        shift: [this.shift.name],
        ...
    })
}

このコードを実行しようとすると、

platform-b​​rowser.umd.js:1900 元の例外: 'shift' の値アクセサーがありません

私は何を取りこぼしたか?

4

1 に答える 1

1

MD textarea コントロールを実装し、MdInputfromで確認しているときに理由を見つけました@angular2-material

現在、NG_VALUE_ACCCESSORControlValueAccessor、 、を含む 2 つのバレルが@angular/commonあり@angular/formsます。私は、彼らがアクセサのものを移動した新しいフォームと、まだ切り替えていない人のためformsに古い実装が残っていると思います。common

ただし、誤ったコンポーネントをインポートした場合でも、警告は表示されません。

したがって、新しいフォームモジュールを使用している場合、解決策は変更することです

import {
    NG_VALUE_ACCESSOR,
    ControlValueAccessor,
    DefaultValueAccessor
} from '@angular/common'; 

import {
    NG_VALUE_ACCESSOR,
    ControlValueAccessor,
    DefaultValueAccessor
} from '@angular/forms';

また、変更する必要がある場合があります

export const YOUR_CUSTOM_CONTROL_ACCESSOR = new Provider(
    NG_VALUE_ACCESSOR, { useExisting: forwardRef(() => YourCustomControlAccessor), multi: true });

export const YOUR_CUSTOM_CONTROL_ACCESSOR: any = {
    provide: NG_VALUE_ACCESSOR, 
    useExisting: forwardRef(() => YourCustomControlAccessor), 
    multi: true 
};
于 2016-07-17T05:41:07.450 に答える