Zmiana maksymalnej daty w selektorze daty

Wątek przeniesiony 2023-06-09 14:51 z JavaScript przez Riddle.

0

Witajcie,
Mam mały problem z Angularem.

Mam taki kod:

   import { Component, Input, Output, EventEmitter  } from '@angular/core';
import {NgbDate, NgbCalendar, NgbDateParserFormatter, NgbDatepickerModule, NgbDateStruct} from '@ng-bootstrap/ng-bootstrap';
import {FormsModule} from '@angular/forms';
import {JsonPipe} from '@angular/common';
import {ClaimService} from "../../service/claim.service";
// @ts-ignore
import Swal from 'sweetalert2';

@Component({
  selector: 'ngbd-datepicker-range-popup',
  standalone: true,
  imports: [NgbDatepickerModule, FormsModule, JsonPipe],
  templateUrl: './datepicker-range-popup.html',
  styles: [
    `
      .dp-hidden {
        width: 0;
        margin: 0;
        border: none;
        padding: 0;
      }

      .custom-day {
        text-align: center;
        padding: 0.185rem 0.25rem;
        display: inline-block;
        height: 2rem;
        width: 2rem;
      }

      .custom-day.focused {
        background-color: #e6e6e6;
      }

      .custom-day.range,
      .custom-day:hover {
        background-color: rgb(2, 117, 216);
        color: white;
      }

      .custom-day.faded {
        background-color: rgba(2, 117, 216, 0.5);
      }
    `,
  ],
})

export class NgbdDatepickerRangePopup {
  @Output() refreshClaimsEvent = new EventEmitter<string>();

  hoveredDate: NgbDate | null = null;
  today: NgbDate;

  fromDate: NgbDate | null;
  toDate: NgbDate | null;

  constructor(private calendar: NgbCalendar, public formatter: NgbDateParserFormatter, private claimService: ClaimService,) {
    this.fromDate = null;
    this.toDate = null;
    this.today = calendar.getToday();
  }

  onDateSelection(date: NgbDate, datepicker: any) {
    // if (!this.fromDate && !this.toDate) {
    //   this.fromDate = date;
    // }
    // else if (this.fromDate && !this.toDate && date) {
    //   this.toDate = date;
    //   datepicker.close();
    // } else {
    //   this.toDate = null;
    //   this.fromDate = date;
    // }



    let restart: boolean = false;
    if(!this.fromDate && !this.toDate){
      console.log('Datepicker: limitOD do dzisiaj');
      datepicker.maxDate = this.today;
      datepicker.minDate = null;
    }
    else{
      //console.log('xxxxxx');
      // datepicker.maxDate = this.today;
      // this.fromDate = null;
      // this.toDate = null;
    }

    console.log(this.fromDate && this.toDate);
    if(this.fromDate && this.toDate){
      console.log('reset');
      restart = true;
      this.toDate = null;
      this.fromDate = null;

      // console.log('Datepicker: limitOD do dzisiaj');
      // datepicker.maxDate = this.today;
      // datepicker.minDate = null;
      // datepicker.reset;

      // datepicker.minDate = this.today;
      // datepicker.maxDate = null;
      // datepicker.refresh;
    }


    const selectedDate = new Date(date.year, date.month - 1, date.day);
    if (selectedDate.getTime() <= Date.now() && !this.toDate && !restart) {
      console.log('level 0');


      if (!this.fromDate && !this.toDate) {
        this.fromDate = date;
        console.log('level 1');

        datepicker.minDate = this.today;
        datepicker.maxDate = null;
        datepicker.refresh

      }
      else {
        if(date.after(this.fromDate))
        {
          this.toDate = date;
          console.log('level 3', this.fromDate, this.toDate);

          // datepicker.minDate = null;
          // datepicker.maxDate = this.today
          datepicker.maxDate = this.today;
          datepicker.minDate = null;
          datepicker.refresh

          datepicker.close();
        }

      }
      // else if (this.fromDate && !this.toDate && date) {
      //   this.toDate = date;
      //   console.log('level 2');
      //   datepicker.close();
      // }
      // else {
      //   console.log('level 3');
      //   this.toDate = null;
      //   this.fromDate = date;
      // }
    }
    else{
      console.log('alert');
      console.log(this.fromDate , this.toDate);
      if (this.fromDate && !this.toDate && date) {
        this.toDate = date;
        console.log('level 2');
        // datepicker.minDate  = null;
        // datepicker.maxDate= this.today;


        datepicker.close();
      }
      else {
        console.log('alert 2');
        if(this.fromDate && !this.toDate)
        {
          datepicker.maxDate = null;
          datepicker.minDate = this.today;
          console.log('alert 2a');
        }
        // this.toDate = null;
        // this.fromDate = null;

      }
    }

  }

  clear(datepicker: any){
    datepicker.maxDate = this.today;
    datepicker.minDate = null;
    this.toDate = null;
    this.fromDate = null;
    datepicker.refresh;
  }

  isHovered(date: NgbDate) {
    return (
      this.fromDate && !this.toDate && this.hoveredDate && date.after(this.fromDate) && date.before(this.hoveredDate)
    );
  }

  isInside(date: NgbDate) {
    return this.toDate && date.after(this.fromDate) && date.before(this.toDate);
  }

  isRange(date: NgbDate) {
    return (
      date.equals(this.fromDate) ||
      (this.toDate && date.equals(this.toDate)) ||
      this.isInside(date) ||
      this.isHovered(date)
    );
  }

  validateInput(currentValue: NgbDate | null, input: string): NgbDate | null {
    const parsed = this.formatter.parse(input);
    return parsed && this.calendar.isValid(NgbDate.from(parsed)) ? NgbDate.from(parsed) : currentValue;
  }

  getFilteredData(query: string) {
    this.claimService.getAllClaims(query).subscribe(
      wynik => {
        const rezultaty = wynik['1'];
      }
    );
  }

  onRefreshButtonClick(): void {
    if (this.fromDate && this.toDate)
    {
      this.refreshClaimsEvent.next('');
    }
    else {
      Swal.fire({
        title: 'Błąd!',
        text: 'Proszę uzupełnić poprawny przedział dat',
        icon: 'error',
        confirmButtonText: 'Cool'
      })
    }
  }
}
<form class="row">
  <div class="row">
    <div class="col-9 col-md-8">
      <div class="dp-hidden position-absolute kalendarz">
        <div class="input-group">
          <input
            name="datepicker"
            class="form-control"
            ngbDatepicker
            #datepicker="ngbDatepicker"
            [autoClose]="'outside'"
            (dateSelect)="onDateSelection($event, datepicker)"
            [displayMonths]="2"
            [dayTemplate]="t"
            outsideDays="hidden"
            [startDate]="fromDate!"
            tabindex="-1"
          />
          <ng-template #t let-date let-focused="focused">
					<span
            class="custom-day"
            [class.focused]="focused"
            [class.range]="isRange(date)"
            [class.faded]="isHovered(date) || isInside(date)"
            (mouseenter)="hoveredDate = date"
            (mouseleave)="hoveredDate = null"
          >
						{{ date.day }}
					</span>
          </ng-template>
        </div>
      </div>
      <div class="input-group">
        <input
          #dpFromDate
          readonly
          class="form-control dpFromDate"
          placeholder="yyyy-mm-dd"
          name="dpFromDate"
          id="dpFromDate"
          [value]="formatter.format(fromDate) + ' - ' + formatter.format(toDate)"
          (input)="fromDate = validateInput(fromDate, dpFromDate.value)"
        />
        <button class="btn btn-outline-secondary bi bi-calendar3" (click)="datepicker.toggle()" type="button"></button>
      </div>
    </div>
    <div class="col-3 col-md-2">
      <button (click)="onRefreshButtonClick()" type="button" class="btn btn-success btn-warning">Filtruj</button>
    </div>
  </div>
</form>

Wszystko działa dobrze, ale mam mały problem.

Moje założenia:

  1. fromDate musi być = dzisiaj lub wcześniej
  2. toDate musi być późniejsza niż fromDate.

W tej chwili działa tak, że wybór za pierwszym razem działa dobrze. Jeśli wybiorę datę po raz drugi, to nie mogę poprawnie wybrać pierwszej daty (fromDate). Nie mogę kliknąć na dzisiejszą lub wcześniejszą datę. Muszę wybrać przyszłą datę.

Wie ktoś może jak to naprawić? :)

0

zerknij na poniższy kod:

ngbd-datepicker-range-popup.component.html

<form class="row row-cols-sm-auto">
    <div class="col-12">
        <div class="dp-hidden position-absolute">
            <div class="input-group">
                <input 
                    name="datepicker" 
                    class="form-control" 
                    ngbDatepicker 
                    #datepicker="ngbDatepicker"
                    [autoClose]="'outside'" 
                    (dateSelect)="onDateSelection($event)" 
                    [displayMonths]="2" 
                    [dayTemplate]="t"
                    outsideDays="hidden" 
                    [startDate]="fromDate!" 
                    tabindex="-1" />
                <ng-template #t let-date let-focused="focused">
                    <span class="custom-day" 
                        [class.focused]="focused" 
                        [class.range]="isRange(date)"
                        [class.faded]="isHovered(date) || isInside(date)" 
                        (mouseenter)="hoveredDate = date"
                        (mouseleave)="hoveredDate = null">
                        {{ date.day }}
                    </span>
                </ng-template>
            </div>
        </div>
    </div>

    <div class="input-group">
        <input #dpFromDate 
            readonly 
            class="form-control dpFromDate" 
            placeholder="yyyy-mm-dd" 
            name="dpFromDate"
            id="dpFromDate" 
            [value]="formatter.format(fromDate) + ' - ' + formatter.format(toDate)"
            (input)="fromDate = validateInput(fromDate, dpFromDate.value)" />
        <button class="btn btn-outline-secondary bi bi-calendar3" (click)="datepicker.toggle()" type="button"></button>
    </div>
</form>

<hr />
<pre>From date model: {{ fromDate | json }}</pre>
<pre>To date model: {{ toDate | json }}</pre>

ngbd-datepicker-range-popup.component.ts

import { Component } from '@angular/core';
import { NgbCalendar, NgbDate, NgbDateParserFormatter } from '@ng-bootstrap/ng-bootstrap';

@Component({
  selector: 'app-ngbd-datepicker-range-popup',
  templateUrl: './ngbd-datepicker-range-popup.component.html',
  styleUrls: ['./ngbd-datepicker-range-popup.component.scss']
})
export class NgbdDatepickerRangePopupComponent {
  hoveredDate: NgbDate | null = null;
  fromDate: NgbDate | null;
  toDate: NgbDate | null;

  constructor(private calendar: NgbCalendar, public formatter: NgbDateParserFormatter) {
    this.fromDate = calendar.getToday(); // aktualna data
    this.toDate = calendar.getNext(calendar.getToday(), 'd', 4); // aktualna data + 4 dni
  }

  onDateSelection(date: NgbDate) {
    if (!this.fromDate && !this.toDate) {
      this.fromDate = date;
    } else if (this.fromDate && !this.toDate && date && date.after(this.fromDate)) {
      this.toDate = date;
    } else {
      this.toDate = null;
      this.fromDate = date;
    }
  }

  isHovered(date: NgbDate) {
    return (
      this.fromDate && !this.toDate && this.hoveredDate && date.after(this.fromDate) && date.before(this.hoveredDate)
    );
  }

  isInside(date: NgbDate) {
    return this.toDate && date.after(this.fromDate) && date.before(this.toDate);
  }

  isRange(date: NgbDate) {
    return (
      date.equals(this.fromDate) ||
      (this.toDate && date.equals(this.toDate)) ||
      this.isInside(date) ||
      this.isHovered(date)
    );
  }

  validateInput(currentValue: NgbDate | null, input: string): NgbDate | null {
    const parsed = this.formatter.parse(input);
    return parsed && this.calendar.isValid(NgbDate.from(parsed)) ? NgbDate.from(parsed) : currentValue;
  }
}

Założenia działają oraz ponowne wybranie dat. Nie wiem , co ma robić przycisk onRefreshButtonClick jak są wybrane obie daty to ma emitować do rodzica pustą wartość...

0

albo prościej, zerknij dodając [maxDate]="fromDate && !toDate ? {year: 0, month: 0, day: 0} : {year: 2023, month: 6, day: 11}" oraz:

<input #dpFromDate 
            readonly 
            class="form-control dpFromDate" 
            placeholder="yyyy-mm-dd" 
            name="dpFromDate"
            id="dpFromDate" 
            [value]="formatter.format(fromDate ?? null) + ' - ' + formatter.format(toDate ?? null)"
            (input)="fromDate = validateInput(fromDate ?? null, dpFromDate.value ?? null)" />

w komponencie musisz też pozwolić, na niezdefiniowaną wartość:

  fromDate: NgbDate | null | undefined;
  toDate: NgbDate | null | undefined;

oraz w konstruktorze nie przypisywać wartości.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.