2017-02-17 7 views
0

Я использую поле ng-boostrap datepicker в одной из своих форм, и я бы хотел инициализировать его значением по умолчанию.Установка значения по умолчанию для поля ng-bootstrap (ngModels не могут использовать getter/setter)

В «стандартной» форме, я могу использовать либо formControlName или ngModel и она отлично работает:

<!-- formControlName --> 
<input ngbDatepicker #d="ngbDatepicker" formControlName="dateFieldName"> 
<!-- ngModel --> 
<input ngbDatepicker #d="ngbDatepicker" [(ngModel)]="myDateProp"> 

Моя проблема я завернул DatePicker в моей пользовательской области, то есть пользовательский компонент, реализующий ControlValueAccessor ,

В этом случае датапикер больше не знает свою родительскую форму и соответствующую модель. Итак, как я могу инициализировать поле datepicker?

  • formControlName не является вариантом (мой пользовательский компонент поля не имеет и не должен иметь доступ к модели его родительской формы).
  • ngModel было проще, поскольку это просто привязка к локальному свойству, но вся моя форма зависает, как только я добавляю директиву [(ngModel)] в поле datepicker.

Вот код для моего пользовательского компонента поля до сих пор:

@Component({ 
    selector: 'field-datetime', 
    template: ` 
    <div class="input-group"> 
     <input class="form-control" ngbDatepicker #d="ngbDatepicker"> 
     <div class="input-group-addon" (click)="d.toggle()" > 
     <i class="fa fa-calendar" style="cursor: pointer;"></i> 
     </div> 
    </div>`, 
    providers: [ 
    { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => FieldDateTimeComponent), multi: true } 
    ] 
}) 
export class FieldDateTimeComponent implements ControlValueAccessor { 
    private _dateTimeObj: DateTime; // Date is stored as an object internally 

    propagateChange = (_: any) => {}; 

    // Write a new value to the element (from the form model into the view). 
    writeValue(timestamp: number) { 
    // Parse the timestamp to store it as an object internally. 
    this._dateTimeObj = moment(timestamp).toObject(); 
    this.propagateChange(this._dateTimeObj); 
    } 

    registerOnChange(fn: any) { 
    this.propagateChange = fn; 
    } 

    registerOnTouched(fn: any) {} 

    // This is the property I'd like to bind to the field, i.e.: 
    // <input ngbDatepicker [(ngModel)]="dateObj"> 
    // THIS IS WRONG. DO NOT USE A GETTER FOR A NGMODEL. SEE ANSWER BELOW. 
    get dateObj() { 
    return {day: this._dateTimeObj.date, month: this._dateTimeObj.months, year: this._dateTimeObj.years}; 
    } 

} 
+0

[(ngModel)] - правильное решение. Не могли бы вы добавить код? Как дикая догадка, я могу представить бесконечный цикл где-то в вашей реализации ControlValueAccessor, например writeValue, вызывающий сеттер, который вызывает writeValue, или что-то в этом роде. –

+0

@VincentV. Спасибо за ваше время. Я обновил вопрос с помощью некоторого кода. – AngularChef

ответ

1

Тьфу ... Проблема заключалась в использовании геттер/сеттер для ngModel (и не имеет ничего общего с ng-bootstrap).

Другими словами, при этом поле в шаблоне:

<input name="foo" [(ngModel)]="foo"> 

Свойство foo не может использовать геттер/сеттер в классе:

export class MyComp() { 
    private _foo; 
    get foo() { 
    return this._foo; 
    } 
    set foo(val) { 
    this._foo = val; 
    } 
} 

Но для того, чтобы по-прежнему быть в состоянии реагировать на изменение модели (в первую очередь это было использование геттера/сеттера), мне пришлось переписать директиву ngModel для использования ее развернутой формы:

<input name="foo" [ngModel]="foo" (ngModelChange)="onFooChanged($event)"> 

Теперь onFooChanged() позволяет мне propagateChange из моего пользовательского поля в родительскую форму (как и сеттер, позволил бы мне).

(В моем исходном примере проблема была get dateObj(). Заменяя ее стандартным свойством класса +, используя синтаксис, исправленный выше.)

1

Правильное решение, очевидно, использование [(ngModel)]. Ваша настоящая проблема - это замораживание, когда вы пытаетесь ее использовать.

Я думаю, проблема заключается в вызове propagateChanges внутри writeValue, который запускает бесконечный цикл. Попробуйте следующие:

HTML:

<input class="form-control" ngbDatepicker #d="ngbDatepicker" (change)="handleChange()" [(ngModel)]="_dateTimeObj"> 

ц:

writeValue(timestamp: number) { 
    // Parse the timestamp to store it as an object internally. 
    this._dateTimeObj = moment(timestamp).toObject(); 
} 
handleChange($event) { 
    // manage sending the value back from the input to your model using $event.target.value 
    this.propagateChange(this._dateTimeObj); 
} 
+0

Оказывается, проблема заключалась в использовании геттера для ngModel. Я заменил его стандартным свойством класса, и он работает. Спасибо, что указал мне в правильном направлении. Без вашего комментария я бы отказался от ngModel. – AngularChef

+0

Вы можете почти выразить свою благодарность с помощью upvote, а затем: p –