valor-software / ngx-bootstrap

Fast and reliable Bootstrap widgets in Angular (supports Ivy engine)
https://valor-software.com/ngx-bootstrap
MIT License
5.52k stars 1.7k forks source link

feat(datepicker): add new event triggered after selecting first date #3348

Open ybeunard opened 6 years ago

ybeunard commented 6 years ago

Hi,

I would like to submit an improvement request.

In the datepicker-range component, I would like to be able to change min date and max date values after selecting the first date.

Context: I want to grey out certain dates bases on two rules.

1- Start date must be after today but not in more than 2 years. (This can be done by setting min & max date) 2- Range should be limited to 100 days. (I would like to grey out dates that are above this range, by dynamically changing max date based on first selected date).

Having a new event sent after selecting the first date would enable more flexible and dynamic rules to be applied.

Best Regards, YBeunard

valorkin commented 6 years ago

I am almost sure bsValueChange should be triggered on first click

ybeunard commented 6 years ago

Hi,

I have run a new test with bsValueChange following your tip. I have simply set (bsValueChange)="test()" in my datepicker-range. I can confirm that, on my side, the first click does not trigger the function. This function is called after the two dates have been selected and the value has been updated in my model. For your information, I’m using a form control so as to recover the value.

Best Regards YBeunard

valorkin commented 6 years ago

yep, indeed it's controlled here: https://github.com/valor-software/ngx-bootstrap/blob/development/src/datepicker/themes/bs/bs-daterangepicker-container.component.ts#L61-L87

sijeesh-02 commented 6 years ago

@valorkin date rangepicke should expose one more attribute to trigger callback when first date is selected. bsValueChange triggered after date picker is done with selection of both the dates.

devarg commented 6 years ago

@valorkin Same here, can't hook to initial date click, event triggers after the full range is selected, so I am not able to pick a max or min range for x amount of days, considering the first picked date.

mkamranhamid commented 6 years ago

any progress ? I need to enable 3 Months dates starting from first selection but this is not possible without getting an event on first selection ?

mkamranhamid commented 6 years ago

I don't know how long will it take you guys to do this i found a little hack ``handleClickDateRangePicker() { setTimeout(() => { let classname = document.getElementsByClassName("bs-datepicker-body");

  for (let i = 0; i < classname.length; i++) {
    let idName = "bs-date-range-picker-" + i;
    classname[i].setAttribute("id", idName);
    document.getElementById(idName).addEventListener('click', function (event) {
      console.log("CLICKED ", event);
      let dd = event['toElement']['innerText'];
      let junk_str = event['path'][6].innerText;
      let trimmed_str = junk_str.replace(/\s/g, '');
      trimmed_str = trimmed_str.replace('↵','');
      let firstDigit = trimmed_str.match(/\d/);
      let indexed = trimmed_str.indexOf(firstDigit);
      let indexed_of_endYear = trimmed_str.indexOf('›');
      let sliced_month = trimmed_str.slice(1, indexed);
      let sliced_year = trimmed_str.slice(indexed, indexed_of_endYear);
      console.log("FIRST DATE SELECTION ::", dd, sliced_month, sliced_year);
      let start_dt = new Date(`${dd}-${sliced_month}-${sliced_year}`);
    })
  }
}, 1000)}
yigitbacakoglu commented 5 years ago

Are you planning to add this feature soon?

dabbid commented 5 years ago

In the meantime, and in same vein than @mkamranhamid, I found a simpler way (but "hacky" as well) to achieve this:

// component.html
<input
  (ngModelChange)="onDateRangeChange(value)"
  (onShown)="onDatepickerShown()"
  [maxDate]="maxDate"
  [minDate]="maxDate"
  [ngModel]="dateRange"
  bsDaterangepicker
  class="form-control date-range-input"
  placeholder="Choose a date range"
  type="text"
>
// component.ts
onDatepickerShown() {
  const datepicker: HTMLElement = document.querySelector('bs-daterangepicker-container');
  datepicker.addEventListener('click', (e: Event) => {
    if ((e.target as HTMLElement).hasAttribute('bsdatepickerdaydecorator')) {
      const clickedDayNum: string = (e.target as HTMLElement).innerText;
      const monthYearButtons: NodeListOf<HTMLButtonElement> = datepicker
        .querySelector('.bs-datepicker-head')
        .querySelectorAll('.current');
      const clickedDate = new Date(`${monthYearButtons[0].innerText} ${clickedDayNum}, ${monthYearButtons[1].innerText}`);
      // define this.minDate & this.maxDate from clickedDate
    }
  });
}

However, there's 2 drawbacks (apart from the fact that's not a long-term solution 🙂):

aklaffenboeck commented 4 years ago

Is there still no offical way to handle this issue? Are you planning to add this feature soon?

ayehia commented 4 years ago

@dabbid solution works for me, here is the updated version after adding momentJS

const datepicker: HTMLElement = document.querySelector('bs-daterangepicker-container');

datepicker.addEventListener('click', (e: Event) => {

   if ((e.target as HTMLElement).hasAttribute('bsdatepickerdaydecorator')) {

      const clickedDayNum: string = (e.target as HTMLElement).innerText;

      const monthYearButtons: NodeListOf<HTMLButtonElement> = datepicker
         .querySelector('.bs-datepicker-head')
         .querySelectorAll('.current');

      const language = 'fr'; //assuming the current language is FR

      const clickedDate = moment(`${clickedDayNum} ${monthYearButtons[0].innerText} ${monthYearButtons[1].innerText}`, 'DD MMMM YYYY', language).locale('en').toDate();

      //this will disable 5 days after the clicked date
      for (let index = 1; index < 6; index++) {
         this.datesDisabled.push(moment(clickedDate).add(index, 'days').toDate());
      }
   }
});
pradeepbp1310 commented 4 years ago

any workaround for this yet?

rajguru827 commented 4 years ago

really looking forward for workaround.

mncorreia commented 3 years ago

Any person to advance with this issue?

juliedev commented 3 years ago

Someone found a workaround?

ush189 commented 2 years ago

This feature would make life a lot easier for other use cases as well.

greenjellygit commented 2 years ago

For a workaround I think better solution than access to native html elements is to read internal properties of date range picker directive.

You can inject it as @ViewChild: @ViewChild(BsDaterangepickerDirective, {static: false}) dateRangePicker: BsDaterangepickerDirective;

And then after datepicker is opened ((onShown)="onDatepickerShown()") you can call custom method to watch changes of first date:

public onDatepickerShown(): void {
        const datePickerInstance: BsDaterangepickerContainerComponent = this.dateRangePicker['_datepicker'].instance;
        datePickerInstance.valueChange
            .pipe(
                filter(value => value && value[0] && !value[1])
            )
            .subscribe((value: Date[]) => {
                const [startDate] = value;
                console.log(startDate);
            });
}