1

Material Designコンポーネントを使用して仮のドロップダウンメニューを作成しようとしていますが、なぜ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元の例外: 'シフト'の値アクセサーなし

私は何を欠席しましたか?

+0

ディレクティブ:[...] 'に' MdSelect'を追加しましたか? –

+0

ええ、 'MdSelect'と' MdOption'の両方です。私はplnkrを作成しようとしていましたが、 'MdSelect'を' MdOption'に注入するのに問題がありました(http://plnkr.co/edit/RfbSN1xMtD9a7PdCWRXY参照)現時点ではアイデアがありません。 –

+0

私が知っている限り、これは動作するはずがありません。 親要素ではなくホスト要素を挿入する方法があります。 –

答えて

1

MdInputから@angular2-materialにMDテキストエリアコントロールとチェックを実装している理由がわかりました。

現在、NG_VALUE_ACCCESSORおよびControlValueAccessor,@angular/commonおよび@angular/formsを含む2つのバレルがあります。私は新しいフォームと彼らは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 
}; 
+0

こんにちは " " MDテキストエリアコントロールを共有する予定はありますか? –

+0

これは、Material2チームが公式のテキストエリアコントロールを思い付くまでの一時的な解決策です。 –

関連する問題