2016-11-11 5 views
0

Я пытаюсь впервые использовать синтаксис форм шаблонов с шаблоном name и [(ngModel)], в пользовательском элементе управления, который использует controlValueAccessor, который я также использую в первый раз.Угловое свойство 2 form.value не определено

Когда я ввожу некоторые слова в мой <input> тогда протоколирует form.value к консоли, я вижу, имя поля формы, которые я добавил, но он до сих пор не определено:

Object {keywords: undefined} 

Если программно установить значение для result.keywords, тогда, когда я заношу form.value в консоль, свойство keywords заполняется. Работает привязка модели к form.value. Связывание с представлением (управление вводом html) с моделью не работает.

ngOnInit() { 
    this.result = new Result(); 
    this.result.keywords = ["aaa"]; <----works 
} 

Вышеупомянутое отобразит [«aaa»] в консоли, но ничего не отобразит в представлении. Как я могу правильно получить свойство ключевых слов формы для заполнения?

Мой код:

Моя форма:

<form class="text-uppercase" (ngSubmit)="onSubmit(findForm.value, findForm.valid)" #findForm="ngForm"> 
     <vepo-input 
      [placeholder]='"keywords (optional)"' 
      [id]='"keywordsInput"' 
      name="keywords" 
      [(ngModel)]="result.keywords"> 
     </vepo-input> 
    </form> 

ввода-component.ts:

import { Component, ViewChild, ElementRef, Input, forwardRef } from '@angular/core'; 
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms'; 

const noop =() => { 
}; 

export const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = { 
    provide: NG_VALUE_ACCESSOR, 
    useExisting: forwardRef(() => InputComponent), 
    multi: true 
}; 


@Component({ 
    selector: 'vepo-input', 
    templateUrl: 'app/shared/subcomponents/input.component.html', 
    styleUrls: ['app/shared/subcomponents/input.component.css'], 
    providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR] 
}) 

export class InputComponent implements ControlValueAccessor { 
    @Input() private placeholder: string; 
    @Input() private id: string; 

    //The internal data model 
    private innerValue: any = ''; 

    //Placeholders for the callbacks which are later providesd 
    //by the Control Value Accessor 
    private onTouchedCallback:() => void = noop; 
    private onChangeCallback: (_: any) => void = noop; 

    //get accessor 
    get value(): any { 
     return this.innerValue; 
    }; 

    //set accessor including call the onchange callback 
    set value(v: any) { 
     if (v !== this.innerValue) { 
      this.innerValue = v; 
      this.onChangeCallback(v); 
     } 
    } 

    //Set touched on blur 
    onBlur() { 
     this.onTouchedCallback(); 
    } 

    //From ControlValueAccessor interface 
    writeValue(value: any) { 
     if (value !== this.innerValue) { 
      this.innerValue = value; 
     } 
    } 

    //From ControlValueAccessor interface 
    registerOnChange(fn: any) { 
     this.onChangeCallback = fn; 
    } 

    //From ControlValueAccessor interface 
    registerOnTouched(fn: any) { 
     this.onTouchedCallback = fn; 
    } 

} 

input.component.html:

<input class="form-item-input" 
      placeholder={{placeholder}} 
      id={{id}} /> 
<label attr.for="{{id}}" 
     class="form-item-right-icon input-icon"> 
</label> 

Моя форма на самом деле намного больше, чем я опубликовал, но я не хочу перегружать всех ненужным кодом. Для полноты картины здесь полный form.ts файл:

import { 
    Component, 
    ViewChild, 
    ElementRef, 
    EventEmitter, 
    Output, 
    OnInit 
} from '@angular/core'; 
import { 
    FormGroup, 
    FormBuilder, 
    Validators, 
    ControlValueAccessor 
} from '@angular/forms'; 
import { ResultService } from '../../../services/result.service'; 
import { Result } from '../../../models/all-models'; 
import { HighlightDirective } from '../../../directives/highlight.directive'; 
import { DistanceUnitsComponent } from './distance-units.component'; 
import { MultiselectComponent } from './multiselect-find-category.component'; 
import { MultiselectFindMealTypeComponent } from 
    './multiselect-find-meal-type.component'; 
import { AreaComponent } from './area-picker.component'; 
import { NumberPickerComponent } from './number-picker.component'; 
import { InputComponent } from '../../../shared/subcomponents/input.component'; 

@Component({ 
    selector: 'find-form', 
    templateUrl: 'app/find-page/subcomponents/find-page/find-form.component.html', 
    styleUrls: ['app/find-page/subcomponents/find-page/find-form.component.css'], 
    providers: [ResultService] 
}) 
export class FindFormComponent implements OnInit { 

    @ViewChild('multiselectFindCategory') 
    private multiselectFindCategory: MultiselectComponent; 
    @ViewChild('multiselectFindMealType') 
    private multiselectFindMealType: MultiselectFindMealTypeComponent; 
    @ViewChild('distanceUnits') private distanceUnits: DistanceUnitsComponent; 
    @ViewChild('numberPicker') private numberPicker: NumberPickerComponent; 
    @ViewChild('areaInput') private areaInput: AreaComponent; 
    @ViewChild('keywordsInput') private keywordsInput: InputComponent; 

    @Output() private onResultsRecieved: 
    EventEmitter<Object> = new EventEmitter<Object>(); 
    @Output() private onSubmitted: EventEmitter<boolean> = 
    new EventEmitter<boolean>(); 

    private categoryError: string = 'hidden'; 
    private mealTypeError: string = 'hidden'; 
    private areaError: string = 'hidden'; 

    private findForm: FormGroup; 
    private submitted: boolean = false; 
    private result: Result; 

    private displayMealCategories: boolean = false; 
    private mealSelected: boolean = false; 
    private place: google.maps.Place; 

    constructor(private resultService: ResultService, 
     private formBuilder: FormBuilder, 
     el: ElementRef) { } 

    ngOnInit() { 
     this.result = new Result(); 
    } 

    private setCategoryErrorVisibility(
     multiselectFindCategory: MultiselectComponent 
    ): void { 
     if (multiselectFindCategory.selectedCategories.length < 1 && 
      !multiselectFindCategory.allSelected && 
      this.submitted) { 
      this.categoryError = 'visible'; 
     } else { 
      this.categoryError = 'hidden'; 
     } 
    } 

    private setMealTypeErrorVisibility(
     multiselectFindMealType: MultiselectFindMealTypeComponent 
    ): void { 
     if (multiselectFindMealType) { 
      if (multiselectFindMealType.selectedCategories.length < 1 && 
       !multiselectFindMealType.allSelected && 
       this.submitted) { 
       this.mealTypeError = 'visible'; 
      } else { 
       this.mealTypeError = 'hidden'; 
      } 
     } 
    } 

    private setAreaErrorVisibility(): void { 
     if (this.areaInput.areaInput.nativeElement.value) { 
      if (!this.areaInput.address) { 
       this.areaError = 'visible'; 
       this.areaInput.areaInput.nativeElement.setCustomValidity("Please select from dropdown or leave blank."); 
      } else { 
       this.areaError = 'hidden'; 
       this.areaInput.areaInput.nativeElement.setCustomValidity(""); 
      } 
     } else { 
      this.areaError = 'hidden'; 
      this.areaInput.areaInput.nativeElement.setCustomValidity(""); 
     } 
    } 

    private onCategoriesChanged(): void { 
     this.setCategoryErrorVisibility(this.multiselectFindCategory); 
     this.mealSelected = this.multiselectFindCategory.mealSelected; 
     if (!this.mealSelected) { 
      this.mealTypeError = 'hidden'; 
     } 
    } 

    private onMealTypesChanged(): void { 
     this.setMealTypeErrorVisibility(this.multiselectFindMealType); 
    } 

    private onAreaChanged(areaEntered: any): void { 
     this.setStateOfDistanceControls(areaEntered.areaEntered); 
     this.areaError = "hidden"; 
     this.areaInput.areaInput.nativeElement.setCustomValidity(""); 
     if (areaEntered.place) { 
      this.place = areaEntered.place; 
     } 
    } 

    private setStateOfDistanceControls(areaEntered: any): void { 
     if (areaEntered.areaEntered) { 
      this.distanceUnits.isEnabled = true; 
      this.numberPicker.isEnabled = true; 
     } else { 
      this.distanceUnits.isEnabled = false; 
      this.numberPicker.isEnabled = false; 
     } 
     this.distanceUnits.setImage(); 
    } 

    private getResults(): void { 
     var results: Result[] = []; 
     results = this.resultService.getResults(); 
     if (results) { 
      this.onResultsRecieved.emit({ 
       recieved: true, 
       results: results, 
       place: this.place 
      }); 
     } 
    } 

    private onSubmit(model: any, isValid: boolean): void { 

     console.log(model, isValid); 


     // this.submitted = true; 
     // this.setCategoryErrorVisibility(this.multiselectFindCategory); 
     // this.setMealTypeErrorVisibility(this.multiselectFindMealType); 
     // this.setAreaErrorVisibility(); 
     // if (this.areaError === "hidden" && 
     // this.categoryError === "hidden" && 
     // this.mealTypeError === "hidden") { 
     // this.onSubmitted.emit(true); 
     // this.getResults(); 
     // } 
    } 
} 

ответ

1

Хорошо, вот мой удар в своем намерении с рабочим plunker для резервного копирования работы:

В вашем input.component. HTML вы должны убедиться, что у вас есть привязки настроенных в ngModel

<input class="form-item-input" [(ngModel)]="value" [placeholder]="placeholder" id="{{id}}" /> 

Помимо этого, там действительно нечего делать.

Здесь находится плункер: http://plnkr.co/edit/HleTVBnvd8ePgMClAZS2?p=preview

Смежные вопросы