如何手动触发“角度材料日期”选择器过滤器?

时间:2020-04-23 18:04:39

标签: angular angular-material

我在日期选择器上设置了一个过滤器,该过滤器基本上可以过滤掉所有不可用的日期。

这一切都按预期工作,但是,由于它是一个Observable,可获取所有不可用的日期,因此直到首先为日期选择器提供输入后,它才显示已过滤的日期。

这是我的过滤器:

从服务中获取最新的不可用日期,然后筛选出这些结果

    bookingsFilter = (d: Date | null): boolean => {
        this.contentService.bookingResults.subscribe(bookings => this.bookings = bookings);
        const day = (d || new Date()).setHours(14, 0, 0, 0);
        return !this.bookings.includes(day);
    }

模板的摘要:

   <input matInput [matDatepickerFilter]="bookingsFilter" ... required />

bookingResults是一个行为主体,它持有一系列日期,每当对后端进行检查时,它都会更新该主体,并且此过滤器会检查该主体。

当用户选择一个日期并在验证过程中对其进行检查时,所有这些功能都能正常工作,我似乎无法正常工作的唯一事情就是让它显示有负载的更新过滤器。

3 个答案:

答案 0 :(得分:1)

在当前的API下,您要的内容无法实现。实际上,我假设this.contentService.bookingResults从后端获取数据,因此它是异步的。

MatDatepicker有一个重要的行为:每次打开过滤器时都会将其设置为组件。使用异步API提取数据,如果没有任何复杂的设置,就不可能在过滤器上拥有最新的数据。

您可以尝试两种方法,但是它们都有缺点。

  1. 显示禁止日期的过时列表:这就是您现在正在做的事情:显示上次打开日期选择器时捕获的禁止日期。这是第一种方法:在实际打开日期选择器之前的某个时间获取最新数据。当前设置的方式(您抱怨过滤器未如预期的那样第一次打开),您正在日期选择器打开期间 抓取数据。因此,当您第一次打开它时,您将获取数据列表,但是它对已打开的日期选择器面板没有任何影响。日期将在那里,没有任何过滤。此处其他答案中的建议告诉您进行后端访问,并使用OnInit方法获取禁止的日期。我认为,如果您的禁忌日没有疯狂地改变,这将是一个可以接受的解决方案。(在预订应用程序中,我认为这些频繁的约会发生了,因此至关重要,因此这种方法是接受)。

  2. 按住日期选择器打开,直到从服务器获取数据为止:另一种方法(尚未在任何答案中建议)在请求服务器提供数据时保留日期选择器。好吧……至少这是您将给用户的体验。实际上,您应该在打开日期选择器后立即将其关闭,然后在服务器端获取数据,并在数据最终在客户端上时重新打开日期选择器。 IMO会给用户带来糟糕的用户体验,但您的过滤器中将拥有最新数据。

    由于其他答案已经显示了方法(1)的执行方式,因此我将展示这种方法也不是很好的方法(2),因为它可以确保您在过滤器上拥有最新数据:

// grab a reference to your datepicker
@ViewChild(MatDatepicker) datepicker: MatDatepicker<Date>;

ngAfterViewInit() {
  this.subscribeToOpeningStream();
}

subscribeToOpeningStream() {
  this.datepicker.openedStream
    .pipe(
      tap(() => {
        // close the datepicker
        this.datepicker.close();
        // disable the datepicker so the user didn't try to open it again
        // while going to the server
        this.datepicker.disabled = true;
      }),
      // unsubscribe to the openedStream: it avoids an infinite loop
      // when you repoen the datepicker inside the subscriber function
      take(1),

      // this is when you go to the server to grab the most recent
      // list of forbidden dates. Notice that if you don't go to the
      // server, this would be a potentially synchronous observable
      // and the overall logic on this setup will probably not work
      // without adding a delay(300) to the observables pipe here
      switchMap(() => this.contentService.bookingResults),
    )
    .subscribe((bookings: any) => {
      this.bookings = bookings;
      
      // enable the datepicker again
      this.datepicker.disabled = false;

      // reopens the date picker
      this.datepicker.open();

      // resubscribe to openedStream
      this.subscribeToOpeningStream();

    });
}

这里有一个Stackblitz demo

上述解决方案仍未完成:尽可能改善UX。打开日期选择器时,也许是输入中的微调器。

此外,请考虑(如果有)使用网络套接字的可能性,以便您可以在禁止的日期订阅更改。正确使用它可以节省应用的用户体验。

答案 1 :(得分:0)

您可以只订阅onInit(),因为您已经将其分配给了this.bookings

答案 2 :(得分:0)

您有两个问题,第一个是Nabel的答案,您需要订阅并为ngOnInit中的this.bookings赋值,请记住您的服务进行了异步调用,因此,在函数“ this.bookings”中有没有价值

ngOnInit()
{
    this.contentService.bookingResults
        .subscribe(bookings => this.bookings = bookings);
}

第二个问题是,它总是给您错误,正在比较对象,并且两个对象(两个日期)是不同的-是,具有相同的值,但它们是不同的。例如,

const date1=new Date()
const date2=new Date()
const equal=date==date1 //equal is false

因此,您可以将getTime或以YYYYMMdd的方式存储在预订中,并与d.getTime()或d格式化的

进行比较。
ngOnInit()
{
    this.contentService.bookingResults
        .subscribe(bookings => this.bookings = bookings.map(x=>{
                 return x.getFullYear()
                        +('00'+(x.getMonth()+1)).slice(-2)
                        +('00'+x.getDate()).slice(-2)
                 })
        )    
}

现在,这个预订是一个字符串数组,所以您可以这样做

  bookingsFilter = (d: Date): boolean => {
    const date=d.getFullYear()+
               ('00'+(d.getMonth()+1)).slice(-2)+
               ('00'+d.getDate()).slice(-2)
        return this.bookings.includes(date);
    }
相关问题