2017-02-17 11 views
0

我在我的一個表單中使用了ng-boostrap datepicker字段,我想用默認值初始化它。設置ng-bootstrap字段的默認值(ngModels不能使用getter/setter)

在「標準」的形式,我可以使用formControlNamengModel,它工作正常:

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

我的問題是我一直包在我自己的自定義字段中的日期選擇器,即自定義組件實現ControlValueAccessor

在這種情況下,datepicker不再知道其父窗體和相應的模型。那麼我怎樣才能初始化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的setter,或者類似的東西。 –

+0

@VincentV。謝謝你的時間。我用一些代碼更新了這個問題。 – AngularChef

回答

1

啊...問題是使用的getter/setter的ngModel的(並沒有什麼關係與ng-bootstrap)。

換句話說,在模板中給出的這個領域:

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

foo屬性不能在課堂上使用的getter/setter:

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

但爲了仍然能夠對模型變化作出反應(這是首先使用getter/setter的全部重點),我不得不重寫ngModel指令以使用其擴展形式:

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

現在onFooChanged()讓我propagateChange從我的自定義字段到父窗體(就像setter會讓我)。

(在我最初的例子中,get dateObj()是個問題,用一個標準的類屬性+用上面的語法替換它解決了它。)

1

正確的解決方案顯然是使用[(ngModel)]。當你嘗試使用它時,你的真正問題是凍結。

我認爲問題是在writeValue中調用propagateChanges,這會觸發無限循環。 嘗試以下操作:

HTML:

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

TS:

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使用了一個getter。我用一個標準的類屬性替換它,它可以工作。儘管如此,感謝您指引我朝着正確的方向發展。沒有你的評論,我會放棄ngModel。 – AngularChef

+0

你幾乎可以用upvote表達你的感激之情,那麼:p –