Estoy usando una aplicación de selector de fecha de material angular en mi proyecto, que es un formulario de reserva. Lo que quiero es que el usuario seleccione la fecha a través del selector de fecha. El Selector de fechas tendrá un filtro que mostrará qué fechas están abiertas y cuáles no. Para que el Selector de fechas sepa qué fechas están disponibles, debe realizar una llamada a uno de mis Servicios que devuelve un Observable . Aquí está mi código HTML para el selector de fecha:
<mat-form-field> <label> <input matInput [matDatepickerFilter]="dateFilter" required [matDatepicker]="picker" placeholder="Choose a date" formControlName="date"> </label> <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle> <mat-datepicker #picker></mat-datepicker> </mat-form-field>y filtro de fecha():
dateFilter = (d: Date): boolean => { if(monthIsSame(d) { ..// have a return statement that process a global variable: 'month: Day[]' } else { // the user hit the arrow at the top that switches months // // have a process that sets the 'month' variable to a new array of Days that was gotten // through a RESTful api in one of my services. // Somehow there must be a loading wheel here until the result from my server comes back and // month is set to the new current month, and then a return statement that process a month } };El problema es que cada vez que el usuario cambia de mes, el calendario debe cargar la disponibilidad del mes desde el servidor trasero, pero el código del servidor devuelve un Observable. De alguna manera, el calendario debe mostrar una rueda de carga hasta que un valor regrese del método de suscripción del observable, y el calendario se completará con las fechas disponibles.
Cualquier ayuda es muy apreciada, y si estoy llegando a esto de una manera completamente equivocada, por favor dígame. ¡Gracias!
Ediciones:
Nota: cuando cambio el tipo de devolución de mi filtro a Observable<boolean> , establece que todas las fechas estén disponibles.
Encontré la siguiente publicación . ¿Sería de alguna manera posible tener una función de devolución de llamada que se llame cuando el usuario cambie de mes y ejecute mi solicitud http y cargue los resultados en una variable local? El único problema de esto es que de alguna manera el calendario tendría que mostrar una rueda de carga hasta que finalice la función de devolución de llamada.
Cómo hacer que mat-calendar actualice el filtro de fecha de forma asíncrona:
La solución es establecer dateFilter en un nuevo valor cada vez que se recuperan datos del servidor. Angular está comprobando este valor en su detección de cambios y actualizando la vista.
// initial filter function always returns true dateFilter = (date: Date): boolean => {return true;} constructor( ... ) { this.myDataObs.subscribe( () => { // set filter function when new data is available this.dateFilter = (date: Date): boolean => { return filterBasedOnDataFromServer(date); }; }); }Probaré esto con un plan de alto nivel, veamos si ayuda.
Necesitará un servicio para pasar datos entre su encabezado personalizado (detalles en un minuto) y el componente de su página, donde tiene el método dateFilter . Llamemos a este servicio OpenDatesService . Creo que no debería proporcionarse en un módulo, porque no siempre lo necesita, pero debe tener al menos un ciclo de vida tan largo como su página, por lo que idealmente se proporciona en su página. En ese caso, debería poder inyectar tanto en el componente de la página como en su encabezado personalizado, y serán la misma instancia.
Así que crea un encabezado personalizado. Puede tomar completamente el de Angular Material, o el ejemplo de docs . Lo explicaré usando este ejemplo. En las devoluciones de llamada anterior/siguiente del encabezado, no llama inmediatamente al calendario ( _dateFormats en el ejemplo), sino que primero inicia una solicitud de red, reemplaza las flechas con una flecha giratoria (use una bandera y algunos ngIf s), y solo re -habilitar los botones una vez que la solicitud de red haya regresado. Sí, esto es asincrónico (observables/promise/async-await), pero en este punto, no está obligado a hacer nada sincrónicamente. Entonces, después de que la solicitud de red haya terminado, pero aún antes de llamar al método anterior/método del calendario, pase los datos de las fechas abiertas al OpenDatesService , es decir. debe tener un método sg. como updateOpenDates . Luego puede llamar al método anterior/siguiente del calendario.
Después de haber llamado al método anterior/siguiente del calendario, pronto se dateFilter al filtro de fecha de su componente de página. Pero en ese momento, OpenDatesService tendrá los datos con las fechas abiertas, y el servicio se inyecta en la página, por lo que en el dateFilter de fecha puede obtenerlo de forma sincrónica y devolver de forma sincrónica el resultado en función del calendario.
Entonces, para resumir, hay 2 trucos:
dateFilter utilizado por el calendario, al controlador de clics del encabezadodateFilter de la página y el encabezado. Ahora que lo pienso, ni siquiera necesita ese servicio para pasar datos entre el encabezado personalizado y el componente de la página. Esto se debe a que el encabezado personalizado se crea en la plantilla del componente de la página, donde el encabezado puede simplemente pasar datos a la página con un @Output y la página puede almacenarlos en algunos campos locales, a los que también se puede acceder mediante el método dateFilter .
No estoy seguro de la parte de la rueda de carga (¿tal vez poner una bandera en el componente y colocarlo dentro de una tubería antes y después de su llamada de servicio?)
Pero en términos del problema de devolver un Observable, creo que su problema se puede resolver con la tubería async :
[matDatepickerFilter]="dateFilter | async" Eso manejará automáticamente la suscripción y la cancelación de la suscripción. Solo asegúrese de poner un shareReplay(1) después de su llamada a la API o, de lo contrario, se activará una llamada a la API en cada ciclo de detección de cambios (que puede ser bastante frecuente).