Angular - Transform calendar dates to a lively green upon initial popup activation

I'm looking to customize my calendar so that all the dates in my list have a green background when the calendar is opened.

ngOnInit(): void {
   this.roomService.getReservableDatesFromRoom(room.roomName).subscribe(data => {
      for (let i = 0; i < data.length; i++) {
        this.dates.push(`${data[i].reservableDate[0]}-${data[i].reservableDate[1]}-${data[i].reservableDate[2]}`);
      }
    });
  }

This is how my HTML code looks like:

<mat-card class="demo-inline-calendar-card">
    <h2 class="text-center">Reserveerbare datums</h2>
    <mat-calendar #calendar (selectedChange)="select($event,calendar)" [dateClass]="isSelected"></mat-calendar>
</mat-card>

Visual representation can be found at this link: https://i.sstatic.net/svrUI.png

The dates list is already populated with values when entering this screen, but currently they are all displayed in red color.

I have a function in place which changes the background color to green when a date is selected using the [dateClass] property. However, I want all the dates to have a green background without requiring a selection:

isSelected = (date: any) => {
    if (date.isBefore(Date.now() - 1, 'day')) {
      return 'disabled-dates'
    }
    return this.dates.filter((x) => date.isSame(x)).length > 0 ? 'selected' : 'not-selected';
  };

If anyone has a solution for achieving this functionality, it would be greatly appreciated!

Thank you in advance!

Answer №1

I finally cracked it: Simply ensure to invoke the calendar.updateTodaysDate() method right after populating your dates list:

this.roomService.getReservableDatesFromRoom(room.roomName).subscribe(data => {
      for (let i = 0; i < data.length; i++) {
        this.dates.push(moment(`${data[i].reservableDate[0]}-${data[i].reservableDate[1]}-${data[i].reservableDate[2]}`).format('YYYY-MM-DD'));
      }
      this.calendar.updateTodaysDate();
    });

Voila! All dates in my calendar now boast a lush green backdrop if they exist in the dates-list

Answer №2

The Date object in JavaScript does not have the methods isBefore or isSame. These are methods of the momentjs library, specifically of the object type moment.

If you want to achieve something similar, you could do:

if (moment(date).isBefore(moment()))
   return 'disabled-dates'
   return this.dates.find((x) => moment(date).isSame(x,'day'))? 'selected' :
                    'not-selected';

(It's not necessary to filter, simply find the first date that fulfills the condition)

However, it's important to note that using momentjs may not be necessary.

When dealing with dates, one should take into consideration factors such as UTC, format, etc.

Furthermore, the "dateClass" function (your function isSelect) is executed for each day displayed on the calendar, so it should be optimized for performance. It might be helpful to use console.log(date) to see the value.

console.log(date) //'Fri Jan 20 2023 00:00:00 GMT+0100' 

This means our dates are at 00:00:00.

Let's imagine your service returns an array of strings:

['2023-01-05','2023-01-12','2023-01-26']

We are working with two variables:

dates:number[]
today:number

Yes, numbers. We are going to convert the array of strings into arrays of numbers like this:

ngOnInit() {
    this.dataService.getDates().subscribe((res: any[]) => {
      const now = new Date();
      now.setHours(0, 0, 0);
      this.today = now.getTime();
      this.dates = res.map((x) => {
        const date = new Date(x);
        date.setHours(0, 0, 0);
        return date.getTime();
      });
    });
}

Now our function looks like this:

dateClass: MatCalendarCellClassFunction<Date> = (date: Date, view) => {
    const time = date.getTime();
    if (time < this.today) return 'disabled-dates';
    return this.dates.find((x: number) => x == time)
      ? 'selected'
      : 'not-selected';
};

For more details, check out the StackBlitz. If you don't use encapsulation: ViewEncapsulation.None, make sure to declare the .css in styles.scss.

Update: Using an API that returns selected days in a month

If you are using an API that only retrieves dates selected for one month at a time, you cannot use dateClass. In this case, you need to handle it differently using a "JavaScript way."

First, define a panelClass for your matInput datePicker and add the "opened" event:

<mat-form-field class="example-full-width" appearance="fill">
<mat-label>Choose a date</mat-label>
<input matInput [matDatepicker]="picker2">
<mat-hint>MM/DD/YYYY</mat-hint>
<mat-datepicker-toggle matIconSuffix [for]="picker2"></mat-datepicker-toggle>
<mat-datepicker [dateClass]="dateClassAsync" #picker2 panelClass="picker2" (opened)="open(picker2.startAt)"></mat-datepicker>
</mat-form-field>

The dateClassAsync is used to capture the new month:

dateClassAsync: MatCalendarCellClassFunction<Date> = (date: Date, view) => {
    if (date.getDate() == 1) {
      setTimeout(()=>{
        this.showMonth(date);
      })
    }
    return '';
};

Then, implement the open function:

open(date: any) {
    date = date || new Date();
    setTimeout(()=>{
      this.showMonth(date);
    })
}

Finally, create the showMonth function to manipulate the calendar display based on the retrieved data:

showMonth(date: any) {
    this.dataService
      .getDateMonth(date.getMonth() + 1)
      .subscribe((dates: string[]) => {
        const cal = document.getElementsByClassName('picker2')[0];
        const days = cal.getElementsByClassName('mat-calendar-body-cell');

        const yearmonth =
          date.getFullYear() +
          '-' +
          ('00' + (date.getMonth() + 1)).slice(-2) +
          '-';
        const today = new Date();
        const todayTxt =
          today.getFullYear() +
          '-' +
          ('00' + (today.getMonth() + 1)).slice(-2) +
          '-' +
          ('00' + today.getDate()).slice(-2);
        for (var i = 0; i < days.length; i++) {
          const dateTxt =
            yearmonth + ('00' + i).slice(-2);
            console.log(dateTxt)
          if (dateTxt < todayTxt) days[i].classList.add('disabled-dates');
          else
            days[i].classList.add(
              dates.find((x: string) => x == dateTxt)
                ? 'selected'
                : 'not-selected'
            );
        }
      });
}

Answer №3

Simple Fix :

 **<mat-calendar #calendar (selectedChange)="select($event,calendar)" [dateClass]="!isSelected"></mat-calendar>**

Rather Than

**<mat-calendar #calendar (selectedChange)="select($event,calendar)" [dateClass]="isSelected"></mat-calendar>**

All you have to do is set the dateClass as default and ensure that the isSelected property has a false value.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

TypeScript and Express create a powerful array combination capability within the type system

After defining the EventType as either "TYPE A" or "TYPE B", I am looking to create a type for an array that can only contain one or both of these event types. Simply typing it as an EventType[] allows duplicates, which is not ideal. type Test = EventType ...

What is the process of accessing the changelog.md file within a component in Angular?

My challenge is to showcase the content from my changelog.md file, which is situated at the same level as the package.json file. I created a service for this purpose using the following code, function getData() { return http.get('../c ...

Protractor for Angular 2: Pausing execution until specified element obtains a specified class

Looking for a method to delay my e2e test (angular2 project) until the targeted element receives a specific css class. Is there an alternative approach without using browser.wait() or browser.sleep()? ...

How to use Angular 2 to communicate with JavaScript API whenever the router switches to

I am currently working on an Angular2 component that has a template which relies on JavaScript calls to load various components such as Facebook, Google Maps, and custom scripts. The necessary scripts are already loaded in the index.html file, so all I ne ...

Error code TS2749 is indicating that the identifier 'XXX' is being interpreted as a value instead of a type. Perhaps you intended to use 'typeof XXX' instead

I've encountered a strange issue while running my npm run dev command in my project built with Nuxt.js, which includes Vue.js components. While launching the application, I'm encountering errors related to TypeScript like TS2749: 'About&apos ...

What steps can be taken to prioritize files with specific extensions in webpack?

I have a dilemma with two files: - somefile.scss - somefile.scss.ts When importing the file in my typescript code, it is referencing the wrong one: import styles from "somefile.scss" The typescript part works fine with the correct import (.scss ...

Creating a button that displays the current day with Angular

I'm in the process of developing a timetable app that features buttons for the previous day, current day, and next day. How can I implement a button to specifically show the current day? HTML File <button type="button" (click)="previousDay()" ...

What steps can I take to resolve a CSS problem in an Angular Web Component within a React Application?

I recently integrated an Angular Web Component with some widgets from Angular Material UI into my simple React Application. While the functionality of the buttons, tables, and radio buttons is working perfectly fine, I am facing issues with the styling and ...

Angular (2/4) application utilizing custom-named routing within a single page architecture

I'm currently working on an Angular application that consists of a login component and a home component which serves as the main handler for the entire single page application. Additionally, I have three more components named users, each with function ...

Utilizing the useContext hook within a strictly Typescript-based class component

I have developed a pure Typescript class that serves as a utility class for performing a specific task. Within this class, I have created a context that is intended to be used universally. My goal is to utilize this context and its values within the pure T ...

Creating a function in Ionic 2: A step-by-step guide

I'm having trouble defining a simple function in Ionic 2. Here is the code I am struggling with: import { Component } from '@angular/core'; import { NavController, NavParams } from 'ionic-angular'; @Component({ selector: &a ...

Dockerized Angular CLI app experiencing issues with hot reload functionality

My existing angular cli application has been dockerized with the following setup: Dockerfile at root level: # Create a new image from the base nodejs 7 image. FROM node:7 # Create the target directory in the imahge RUN mkdir -p /usr/src/app # Set the cr ...

Struggling to grasp the concept of Observable Catch closure scope in Angular2?

Seeking guidance on using catch with Observables. I find myself confused and would appreciate some assistance. My goal is to handle a 403 error from the API by deleting the local token and marking the user as unauthenticated in my TokenStore. The approach ...

"Encountering a problem when trying to display Swagger-Editor for the second

While integrating the swagger-editor package into my React application, I encountered an issue. The first time I fetch the Swagger specifications from GitHub, everything works perfectly and validates correctly. However, upon rendering it a second time, an ...

What is the reason for the regeneration of the 2D array?

I have a method called generateWeights() that retrieves random values in an array; And a method named learn() that calls the changeWeights() method to alter the values in the array. Expected: Prior to invoking the learn() method, I anticipate receiving an ...

Despite having the same versions for Angular and Angular CLI, the build process using 'ng build'

After running ng v, the output shows: Angular CLI: 9.1.13 Node: 12.22.12 OS: win32 x64 Angular: 9.1.13 Strangely, attempting to execute ng build resulted in the following error: This version of CLI is only compatible with Angular versions ^13.0.0 || ^13. ...

How is it possible for this for loop to function properly without the need to pass the incrementing variable

I managed to compile my code and it's working fine, but there's something interesting - the variable that should reference the incrementing value is not included as an argument in the for loop. var _loop2 = function _loop2() { var p = docume ...

Unable to compile Angular 5 using the AOT systemjs configuration

I've hit a roadblock in finding a solution to my issue. Maybe someone out there can lend me a hand. I'm in the process of upgrading from ng 4.4.4 to 5.0.1 and everything seems to be functioning fine in JIT mode. However, when attempting to compi ...

Retrieve both the name and id as values in an angular select dropdown

<select (change)="select($event.target.value)" [ngModel]="gen" class="border border-gray-200 bg-white h-10 pl-6 pr-40 rounded-lg text-sm focus:outline-none appearance-none block cursor-pointer" id="gend ...

An argument of type 'Array<T>' necessitates the inclusion of one type argument

I've come across this issue multiple times on various online forums. Despite trying different solutions, I'm still unable to resolve the following error: (11,23): Generic type 'Array' requires 1 type argument(s). This pertains to t ...