反应形式的 NgbDatepicker 形式:设置初始值

NgbDatepicker in Reactive Forms with formly: Set Initial Value

我正在尝试以 angular 反应形式使用 ngbDatepicker,并希望能够显示初始值。

我的问题是 with the crucial difference, that I am also using formly,一个自动生成表单的 angular 包。由于原始问题的答案是基于生成一个新的 formControl 并将其添加到表单中,在我的例子中,这是通过 formly 完成的,这就是我苦苦挣扎的原因。

“组件”通常在您要输入新值时起作用(因为这会触发我用来更改模型值的事件),但初始值显示不正确。虽然模型总是具有例如值“2020-07-05”如果使用占卜检查,则永远不会显示在输入字段中。如果我将模型中的日期转换为 ngOnInit() 中的 NgbDate ({year, month, day}),这仍然成立。

Formly 调用我的自定义组件(定义如下),它只包含字段并将预先存在的表单控件(包括模型)和字段配置绑定到输入字段。该组件本身并没有做太多事情,它包含 ngbDatepicker 所必需的 HTML 和一个函数,该函数在选择日期时将其从 NgbDate ({year, month, day}) 转换为字符串 ( “yyyy-mm-dd”)并确保将其存储在模型中而不是 NgbDate 中。当您单击 fa-calendar 图标时,它还会打开日期选择器:

//formly-datepicker.component.html

<div class="form-group" [ngClass]="{'d-none': to.hidden}">
    <!-- Heading --> 
    <!-- to = TemplateOptions -->
    <label for="datepicker">
        <strong>
            {{to.label}}
            <span *ngIf="to.required">*</span>
        </strong>
    </label>

    <!-- Input -->
    <div class="input-group">
        <input
        class="form-control" 
        placeholder="yyyy-mm-dd" 
        name="datepicker" 
        [formControl]="formControl" 
        [formlyAttributes]="field" 
        ngbDatepicker 
        #datepickerInput="ngbDatepicker"
        (dateSelect)="inputDateToField($event)"
        >
        <div class="input-group-append">
            <div class="btn btn-outline-light fa fa-calendar" (click)="datepickerInput.toggle()"></div>
        </div>
    </div>
</div>


<ng-template #customDay let-date>
    <div class="datepicker-day btn-light">
        {{ date.day }}
    </div>
</ng-template>



//formly-datepicker.component.ts
import { Component, } from '@angular/core';
import { NgbDate } from '@ng-bootstrap/ng-bootstrap';
import { FieldType } from '@ngx-formly/core';

@Component({
  selector: 'app-formly-datepicker',
  templateUrl: './formly-datepicker.component.html',
  styleUrls: ['./formly-datepicker.component.scss']
})
export class FormlyDatepickerComponent extends FieldType{
  inputDateToField(event: NgbDate){
    this.model.session_date = `${event.year}-${event.month}-${event.day}`;
  }
}

我试过在 ngOnInit() 和 ngAfterViewInit() 中操作模型,我试过设置 startDate,但在这种情况下似乎没有任何帮助。我在这里做什么?

我强烈建议您更新 NgBootstrap 和 Formly 的配置(如果您还没有更新的话):

  1. 不要在组件中使用 inputDateToField 方法,而是创建 NgBootstrap 为 NgbDatePicker 推荐的 two services (parser and adapter) 方法。解析器将维护您在 model 中指定的格式,适配器将再次根据您的自定义显示 UI 中的日期。例如。我使用解析器转换后端提供的 ISO 字符串,以便在我的 model 中将其保存为 'YYYY-MM-DD',并使用适配器将其显示为 'DD-MM-YYYY'
  2. 无论你在哪里导入 FormlyModule 像这样更新配置(如果它不在 AppModule 中,你可能必须使用 forChild):
FormlyModule.forRoot({types: [{ name: 'date', component: FormlyDatepickerComponent, extends: 'input'}]})
  1. 在您的 FormlyDatepickerComponent 模板中,添加 type="text"(可选)。

你可以采取两种方法:

1.-创建表单时使用 NgbDateStruct 作为默认值

//I imagine you has an object "data" with a property "date" 
//in the way yyyy-mm-dd and create the field as
{
      key: 'date',
      type: 'input',
      defaultValue: {
           year:+data.date.substr(0,4),
           month:+data.date.substr(5,2),
           day:+data.date.substr(8,2)
      },
      templateOptions: {
        label: 'First Name (initialized via default value)',
      },
    },

在这种情况下,您总是管理“日期”一个具有属性年月日的对象,是的,FormControl 可以存储一个对象,它不是“extrange”

2.- 使用管理字符串的自定义适配器。这有点复杂。您需要创建两个 class:

CustomAdepter

@Injectable()
export class CustomAdapter extends NgbDateAdapter<string> {

  readonly DELIMITER = '-';

  fromModel(value: string | null): NgbDateStruct | null {
    if (value) {
      let date = value.split(this.DELIMITER);
      return {
        day : parseInt(date[2], 10),
        month : parseInt(date[1], 10),
        year : parseInt(date[0], 10)
      };
    }
    return null;
  }

  toModel(date: NgbDateStruct | null): string | null {
    return date ? date.year+ this.DELIMITER+('00'+date.month).slice(-2) + this.DELIMITER + ('00'+date.day).slice(-2)   : null;
  }
}

和 CustomDateParserFormatter(当您输入时创建 ngbDateStructurt)

@Injectable()
export class CustomDateParserFormatter extends NgbDateParserFormatter {

  readonly DELIMITER = '-';

  parse(value: string): NgbDateStruct | null {
    if (value) {
      const separator=value.indexOf(".")>=0?".":value.indexOf("-")>=0?"-":value.indexOf("/")>=0?"/":null
      if (separator)
      {
      let date = value.split(separator);
      return {
        day : parseInt(date[2], 10),
        month : parseInt(date[1], 10),
        year : parseInt(date[0], 10)
      };
    }
    }
    return null;
  }

  format(date: NgbDateStruct | null): string {
    return date ? date.year+ this.DELIMITER+('00'+date.month).slice(-2) + this.DELIMITER + ('00'+date.day).slice(-2)   : "";
  }
}

然后在你的组件中你需要在你的模块中添加作为提供者(你也可以在你的组件中添加作为提供者)

  providers: [
    {provide: NgbDateAdapter, useClass: CustomAdapter},
    {provide: NgbDateParserFormatter, useClass: CustomDateParserFormatter}
  ]

现在您可以使用,您的模型或 formControl 将是一个字符串

一个forked stackblitz the example in ng-bootstrap