This might be one of the more frustrating issues I've coped with in a while. Dates and -- in particular -- NgbDatepicker are a bit of a bear to deal with in Angular, in general.
I'm implementing NgbDatepicker against reactive forms in Angular 8, and the gist of my problem is that I can set the initial value of my date control, but that initial value doesn't show-up (visually) on the form. I can log my form to the console and see that the value is getting set, but the form field itself doesn't update. If I select a date from the picker itself then my selection reflects in the form field. I had thought it would be as simple as setting the [startDate]
property of the ngbDatepicker input, but apparently no. I've tried a lot of different iterations on this; here's where things stand, code-wise:
My HTML:
<input #licenseExpiration="ngbDatepicker"
class="form-control"
fromControlName="licenseExpiration"
[startDate]="startDate"
ngbDatepicker />
<button class="btn btn-info zero-spacing pt-1 pr-1 pl-1"
(click)="licenseExpiration.toggle()"
type="button">
The relevant bits of my component:
@Component({
selector: 'app-cert',
templateUrl: './cert.component.html',
providers: [
{ provide: NgbDateParserFormatter, useClass: CustomDateParserFormatter }
]
})
export class CertComponent implements OnInit {
certForm: CertForm = new CertForm();
constructor(
public readonly formatter: CustomDateParserFormatter) { }
ngOnInit() {
this.certForm.setModel(...modelObjectFromOtherService...);
}
get startDate() {
return { year: 2020, month: 6, day: 13 };
}
}
The above HTML resides in a formGroup that is spec'd by that CertForm type. That form contains the control for the date and looks (in part) like this:
export class CertForm extends FormGroup {
public dateParser: CustomDateParserFormatter;
constructor(
cert: ICertModel = new CertModel()) {
super({
..
licenseExpiration: new FormControl("", Validators.required),
..
});
}
setModel(cert: ICertModel) {
this.patchValue({
licenseExpiration
});
}
getModel(): ICertModel {
const cert = new CertModel();
...
cert.licenseExpiration = this.get("licenseExpiration").value;
...
return cert;
}
}
From there I do have the custom parser/formatter set-up and it looks just like the one that is spec'd on the NGB examples pages:
@Injectable({
providedIn: "root"
})
export class CustomDateParserFormatter extends NgbDateParserFormatter {
readonly DELIMITER = '/';
parse(value: string): NgbDateStruct | null {
if (value) {
let date = value.split(this.DELIMITER);
return {
day: parseInt(date[0], 10),
month: parseInt(date[1], 10),
year: parseInt(date[2], 10)
};
}
return null;
}
format(date: NgbDateStruct | null): string {
return date ? date.month + this.DELIMITER + date.day + this.DELIMITER + date.year : '';
}
}
And, again, this just won't seem to let me initiate that date field with a visible start-up value. As you can see, at this point I'm just literally hard-coding a return value for that start date. And that date does get properly set -- when you open the picker, that's the date it lands on, but it doesn't actually show-up in the display field when you land on the page. That's just blank until you actually select a value.
I have attempted so many different combinations of ideas and nothing is working. It's incredibly frustrating. I see that variations/fragments of this question do exist already, but I can't find examples of the question being asked with quite this combination of technologies. Most people seem to be relying on templated forms, not reactive forms, when using NgbDatepicker.