semicolon
semicolon

Reputation: 265

Angular material DatepickerRange get value on change

I have DatePickerRange of Angular Material and I want to run a function when changing the value in DatePicker I tried with the function (change) but it did not work I would be happy for your help how to do it. Thank you!

This is my html:

<mat-form-field class="form-field">
    <mat-label>Enter a date range</mat-label>
    <mat-date-range-input [formGroup]="range" [rangePicker]="picker">
        <input matStartDate formControlName="start" placeholder="Start date">
        <input matEndDate formControlName="end" placeholder="End date">
    </mat-date-range-input>
    <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
    <mat-date-range-picker #picker></mat-date-range-picker>

    <mat-error *ngIf="range.controls.start.hasError('matStartDateInvalid')">Invalid start date
    </mat-error>
    <mat-error *ngIf="range.controls.end.hasError('matEndDateInvalid')">Invalid end date

    </mat-error>
    </mat-form-field>

And that's the function I want to activate:

   @Output() newItemEvent = new EventEmitter<bookedDate>();
      addNewDate() {
        this.dateRange.dateStart = this.range.get('start').value;
        this.dateRange.dateEnd = this.range.get('end').value;
        this.newItemEvent.emit(this.dateRange);
      }

Upvotes: 14

Views: 41958

Answers (7)

Marc
Marc

Reputation: 409

The solution I found is to subscribe to the formEnd date control with valueChanges and then recover the value of the formStart date control and check if both are not null before lauch any function. So the code may look like:

this.dateRange.get('end').valueChanges.subscribe( (endDate: Moment) => {
        
      const startDate = this.range.get('start').value;

      if (startDate && endDate)  { 
          this.doSomething() 
      }
})

Upvotes: 0

Joosep Parts
Joosep Parts

Reputation: 6235

Though Matt's answer is a good one, the downside with that approach is that (dateChange) fires only on the endDate input change. E.g if user changes startDate by typing, his event won't fire.

I ended up using Subject's to combine the Observables from both inputs. This lets me listen when each individual input was changed. Either by picker, or by typing. By checking value carried by the Event we can determine when user has finished picking both valid dates.

  <mat-form-field appearance="fill">
          <mat-label>Travel date</mat-label>
            <mat-date-range-input [rangePicker]="picker">
              <input matStartDate placeholder="Start date" (dateChange)="startDatePicker.next($event)">
              <input matEndDate placeholder="End date"  (dateChange)="endDatePicker.next($event)">
            </mat-date-range-input>
            <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
            <mat-date-range-picker #picker></mat-date-range-picker>
        </mat-form-field>

TS:

  startDatePicker = new Subject<MatDatepickerInputEvent<any>>();
  endDatePicker = new Subject<MatDatepickerInputEvent<any>>();

  ngOnInit(): void {
    const dateChange$ = combineLatest([this.startDatePicker, this.endDatePicker]).pipe(
      map(([a$, b$]) => ({
        start: a$,
        end: b$
      }))
    );

    dateChange$.subscribe((data) => {
      if (data.start.value && data.end.value) {
        console.log('User has picked both ranges!');
      }
    });
  }

Upvotes: 3

nilemandal
nilemandal

Reputation: 30

I have used (dateChange)="function()" to trigger the change in date in mat date picker

Upvotes: 1

Andrej Larin
Andrej Larin

Reputation: 49

I faced this problem myself, here is the solution that helped me: component.ts:

    import * as moment from 'moment';
    import { FormControl, FormGroup } from "@angular/forms";
    import { DateAdapter, MAT_DATE_FORMATS,  MatDateFormats } from "@angular/material/core";
    import {  MomentDateAdapter } from '@angular/material-moment-adapter';
        const CUSTOM_DATE_FORMATS: MatDateFormats = {
        parse: {
            dateInput: 'D/MM/YYYY'
        },
        display: {
            dateInput: 'DD/MM/YYYY',
            monthYearLabel: 'MMMM Y',
            dateA11yLabel: 'LL',
            monthYearA11yLabel: 'MMMM Y'
        }
    };
    @Component({
        providers: [
            {provide: MAT_DATE_FORMATS, useValue: CUSTOM_DATE_FORMATS},
            {provide: DateAdapter, useClass: MomentDateAdapter}
        ],
    })
    
        @ViewChild('picker') picker: any;
        dateRange: moment.Moment;
        disabled = false;
        tempDate = new Date().setMonth(new Date().getMonth() - 1);
        range = new FormGroup({
            start: new FormControl(new Date(this.tempDate)),
            end: new FormControl(new Date()),
        });
    
        ngAfterViewInit() {
            this.range.valueChanges.pipe(
                debounceTime(200)
            ).subscribe(event => {
                if (event.start && event.end) {
                    this.onDateChanged(event);
                }
            });
        }
  
    <mat-form-field class="date-range" appearance="fill">
    <mat-label>Enter a date range</mat-label>
    <mat-date-range-input
    [formGroup]="range"
    [rangePicker]="picker">
    <input matStartDate formControlName="start" placeholder="Start date">
    <input matEndDate formControlName="end" placeholder="End date">
    </mat-date-range-input>
    <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
    <mat-date-range-picker #picker></mat-date-range-picker>
    <mat-error *ngIf="range.controls.start.hasError('matStartDateInvalid')">Invalid start date</mat-error>
    <mat-error *ngIf="range.controls.end.hasError('matEndDateInvalid')">Invalid end date</mat-error>
    </mat-form-field>

Upvotes: 4

Matt Saunders
Matt Saunders

Reputation: 4074

My approach was to add template reference variables to the inputs for matStartDate and matEndDate. These are dateRangeStart and dateRangeEnd in the template example below:

// Template

<mat-form-field appearance="standard">
  <mat-label>{{reportField.value.label}}</mat-label>
  <mat-date-range-input [rangePicker]="dateRangePicker">
    <input matStartDate
      placeholder="Start date"
      #dateRangeStart>
    <input matEndDate
      placeholder="End date"
      #dateRangeEnd
      (dateChange)="dateRangeChange(dateRangeStart, dateRangeEnd)">
  </mat-date-range-input>
  <mat-datepicker-toggle matPrefix
    [for]="dateRangePicker">
  </mat-datepicker-toggle>
  <mat-date-range-picker #dateRangePicker></mat-date-range-picker>
</mat-form-field>

The values of these inputs can then be accessed by a function called by the dateChange output on matEndDate.

// Component

dateRangeChange(dateRangeStart: HTMLInputElement, dateRangeEnd: HTMLInputElement) {
  console.log(dateRangeStart.value);
  console.log(dateRangeEnd.value);
}

This avoids the need for a dateChange event on both inputs.

Upvotes: 54

semicolon
semicolon

Reputation: 265

@Kirubel and @Irad Amri Thanks!

It works for me in the end this way:

HTML-

<mat-form-field class="form-field">
    <mat-label>Enter a date range</mat-label>
    <mat-date-range-input [formGroup]="range" [rangePicker]="picker">
        <input **(dateInput)="addEvent('input', $event)" 
(dateChange)="addEvent('change', $event)"** matStartDate
            formControlName="start" placeholder="Start date">
        <input **(dateInput)="addNewDate('input', $event)" 
 (dateChange)="addNewDate('change', $event)"** matEndDate
            formControlName="end" placeholder="End date">
    </mat-date-range-input>
    <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
    <mat-date-range-picker #picker></mat-date-range-picker>

    <mat-error *ngIf="range.controls.start.hasError('matStartDateInvalid')">Invalid 
 start date
    </mat-error>
    <mat-error *ngIf="range.controls.end.hasError('matEndDateInvalid')">Invalid end 
 date

    </mat-error>
</mat-form-field>

TS-

@Output() newItemEvent = new EventEmitter<bookedDate>();

  addNewDate(type: string, event: MatDatepickerInputEvent<Date>) {
    this.dateRange.dateStart = this.range.get('start').value;
    this.dateRange.dateEnd = this.range.get('end').value;
    this.newItemEvent.emit(this.dateRange);
  }

And again, thanks so much for the help!

Upvotes: 0

Irad Amri
Irad Amri

Reputation: 19

i have something similar to your problem it works for me so i hope it can help you or give your a hint about a solution here is my html :

<mat-form-field class="dialogform">
  <mat-label>Duree du Session : </mat-label>
    <input matInput
       required
       placeholder=""
       [satDatepicker]="picker"
       [value]="dateRangeDisp"
       (dateChange)="saveDate($event)"
    >
  <sat-datepicker #picker [rangeMode]="true"></sat-datepicker>
  <sat-datepicker-toggle matSuffix [for]="picker"></sat-datepicker-toggle>
  </mat-form-field>

and the function is :

     saveDate(event: any) {
    // look at how the date is emitted from save
    console.log(event.target.value.begin);
    //this.session.dateDebut = event.target.value.begin;
    console.log(event.target.value.end);

    // change in view
     this.dateRangeDisp = event.target.value;


    // save date range as string value for sending to db

    // ... save to db
  }

Upvotes: 2

Related Questions