Search code examples
angulardata-binding

Date not binding properly to Angular input property


I've made a calendar in my Angular app and I'm trying to pass a specific date that the user clicks on to a child component. The child component is declared like below:

<app-day-view [date]="dateToPass | date:'yyyy-MM-dd'"></app-day-view>

And the child component is set up to take in the input property correctly too:

import { Component, OnInit, Input, Output } from '@angular/core';
import { DateHandlerService } from './../../_services/date-handler.service';

declare var $: any;

@Component({
  selector: 'app-day-view',
  templateUrl: './day-view.component.html',
  styleUrls: ['./day-view.component.css']
})
export class DayViewComponent implements OnInit {

  @Input() date;
  dateString: string;
  constructor(public dateHandler: DateHandlerService) { }

  ngOnInit() {
    $('#dayModal').on('shown.bs.modal', this.getDateString);
  }

  getDateString() {
    //this.dateString = this.dateHandler.convertToOutDate(this.date);
  }
}

The intended behaviour is that I click one of the days in my calendar and <app-day-view>, which is a modal, opens, takes dateToPass, which I've confirmed already stores the correct date, and sends it to the child component. This template doesn't throw any errors, but by the time I check the date inside the DayViewComponent, it's undefined.

I assumed it was an issue caused by the child component existing in the parent component before being opened. The date input property would technically be undefined until I clicked on a day in the calendar, after which it would suddenly become defined, but I'm not sure if Angular input properties work like this. To get around this, I used JQuery to check the modal's shown.bs.modal event, and I only try to use the date after that event has fired, but the date still seems to come out as undefined.


Solution

  • If your child calendar component exists in the DOM before you actually have a value in dateToPass, then undefined will go to your input.

    I'll admit to not playing with the pipes much, but will that not attempt to turn your date input into a string input? If you have a Date as an input, just give it the Date object as-is instead of trying to format it (unless the component actually wants a string input).

    Aside from that, at the time you attempt to see the value, in your ngOnInit method, you haven't passed any value to it, unless you're wrapping that component in an *ngIf.

    Any subsequent change to the value of dateToPass won't cause you to hit the ngOnInit method again - that's literally only hit once at component initialisation, which has already been hit.

    You can either hook into Angular's change detection, or switch your input to be a setter...

    @Input() public set date(value: Date) {
      this.onDateChanged(value);
    }
    
    private onDateChanged(date: Date): void {
      console.log(`Date value changed to: ${date}`);
    }
    

    If you want to ensure that your component has an input value, then hide it (actually remove it from the dom, not just have it 'hidden') with an *ngIf:

    <div *ngIf="dateToPass">
      <app-day-view [date]="dateToPass"></app-day-view>
    </div>
    

    This will make Angular only render (and initialise) your component once you have actively set a value - thus guaranteed to have a value available to you on ngOnInit for you to check out.