Skip to content

Calendar

The calendar view provides a visual overview of a week, a month, or both. It supports both single or range date selection. It also allow Use calendar view to filter the information based on days. For example, events, tasks, and deadlines.

The calendar consists of three parts: header, weekday label, and dates.

Calendar Structure

Calendar Header

The header displays the current month or selected date information if a date is selected. For Android, there are components on the right side (not applicable for DateRange type): a back-to-today button (small calendar icon) allows you to return to the current month or week by tapping the button. Left and right arrow buttons enable navigation between months or weeks.

Weekday Label

A one-letter label shows the weekday as the column header of the calendar. StartDayOfWeek customizes the start day of the week to either Sunday, Saturday, or Monday. The default is Sunday.

Date Label Container

The date label container shows all the days of the month/week in a grid view.

Legend

The legend section will display the icon and title of the event indicators to give user meaningful information of the events on the calendar. It will only be visible if you have defined event indicators and the visible month or week has events.

Dragging Handle

The dragging handle provides a visual cue on the dragging behavior to expand the week view and collapse the month view when calendar type is set to Expendable.

Other Features

Calendar Type

Calendar support various types:

  • Month
    • Displays the calendar in month view. Only single date selection is applicable.
  • Week
    • Displays the calendar in week view. Only single date selection is applicable.
  • Expandable
    • Displays the calendar in either week or month view. A dragging handle allows you to expand from week view to month view by swiping down. You can collapse from month view to week view by swiping up. Only single date selection is applicable.
  • DateRange
    • Displays the calendar similar to month view but allows more than one month to be displayed. You may scroll vertically through the calendar months. Use this type to allow range selection. The first date selected is the start date of the range, and when a second date is selected, it becomes the end date of the range, with the entire range of dates between the start and end dates (inclusive) being selected. To modify the set of selected dates on iOS, click on a selected date within the range, and it becomes the new end date, with all later dates deselected. To unselect the entire range, click outside the range, anywhere in the calendar view. For Android, any click on a date within or outside the selected range results in deactivating the existing range. The end date of the range must be later than the start date. If you click on a date earlier than the start date, the newly selected date becomes the start date, and the original start date is deselected. Specifically for DateRange type, in multi-section scenarios, the calendar displays with default height (600 for Android and 450 for iOS), while in single-section scenarios, the calendar displays in full screen. You may customize the height of the calendar by specifying the Height property in the metadata definition for both single and multi-section scenarios.

To navigate between months or weeks in sequence, use left and right arrow buttons for Android, and scroll for iOS.

Selection Behavior

The selected date is displayed based on today's date by default. You can customize it through metadata definition or ClientAPI. You may change the selected date by tapping the desired date. For DateRange type, you need to select two dates to form a valid range. IsPersistentSelection is a flag that determines the behavior of the selection. When set to true (the default), the selected date is retained even when you navigate to other months. If the flag is set to false, when you navigate to another month, the first day of the displayed month is shown as selected. IsPersistentSelection is not applicable for DateRange type and defaults to true.

Calendar Range Limit

There is a limit to the months you can navigate. The calendar is limited with a start date and end date. The start date defaults to January 1st of two years prior to the initially selected date. The end date defaults to December 31st of two years following the initially selected date. You may customize the limit by defining StartDate and EndDate on metadata definition.

Selected Date

When user selects a date in a single selection calendar type, an event OnSelectedDateChange will be triggered.

In DateRange calendar type, when you complete selection of a date range, an event OnSelectedDateRangeChange will be triggered.

If you have defined events and the selected date(s) has events tied to it:

  • In single date selection type (Month, Week, or Extendable), you will also receive a binding that's an object with properties IndicatorName and EntitySets containing the related entities.
  • In date range selection type (DateRange), you will also receive a binding that's an array of object with properties Date, IndicatorName and EntitySets containing the related entities.

See Event Indicators topic below for more details.

You can get or set selected date programmatically via CalendarProxy's getSelectedDate and setSelectedDate API for single date selection calendar type, or getSelectedDateRange or setSelectedDateRange API for date range calendar type.

Event Indicators

You can use Calendar Event Indicators to identify special events or statuses directly on the calendar. This feature helps you quickly spot important dates and plan effectively. The indicators have customizable appearances and can be dynamically assigned based on metadata or runtime data. This provides an efficient way to manage and view calendar events.

Indicators

You can define a list of indicators in the Indicators property that accept an array of indicator definition, each indicator is represented by a name, icon and title.

  • The name will be used to reference the indicator to display in an event
  • The icon will be displayed in each of the date in the calendar that has an event
  • Both icon and title will be displayed as part of the legend section in the calendar

You can then display these indicators in your calendar by specifying their name in events via the calendar's Events or Event property.

Events

There are 2 ways you can populate events in the Calendar. Using static array via Events property or using dynamic binding list via Event property:

  • Events can be used if you wish to construct your own list of events and return them as an array via rule. This is useful if you wish you have full control on how you retrieve data and construct the list of events for your Calendar. However, you must retrieve all events in one go as it does not support on-demand loading. Each item in the array is an object that must have IndicatorName and Date:

    • Date indicates which date (in ISO 8601 format) in the calendar must be display the indicator.
    • IndicatorName that specify the indicator that'll be displayed for that that event. The value should match one of the indicators' name you have predefined in the Indicators property.
  • Event can be used if you wish to make use of the Targets binding to populate events in your calendar. The Event object accepts only a single property IndicatorName that specifies the indicator that'll be displayed for that event. The value should match one of the indicators' name you have predefined in the Indicators property. Typically, you will use rule to decide which indicator's name to return for each populated date.

    To populate the events using Event, you need to define Targets (note on the plural naming) binding, It accepts an array of Target definition. Each Target accepts the standard target binding specifiers such as Service, EntitySet, and QueryOptions, but it also accept a calendar specific property called DateProperties which accept an array.

    DateProperties allows you to specify one or more properties of your Entity Set that should be used as filter criteria for on-demand querying of events data. These properties must be of type Date or DateTime. The filters for multiple date properties will be combined using OR operator.

Targets Binding Example

The following example will guide you on how to use Event and Targets for dynamic binding of events in your calendar. Let's assume you have defined your calendar as the following:

{
  "_Type": "Section.Type.Calendar",
  "_Name": "CalendarSection",
  "Indicators":[{
    "_Name": "UpcomingIndicator",
    "Icon": "sap-icon://to-be-reviewed",
    "Title": "Upcoming Deadline",
    "Style": {
      "Icon": "GreenColorStyle"
    }
  },{
    "_Name": "OverdueIndicator",
    "Icon": "sap-icon://alert",
    "Title": "Overdue",
    "Style": {
      "Icon": "WarningColorStyle"
    }
  },{
    "_Name": "HighPrioOverdueIndicator",
    "Icon": "sap-icon://error",
    "Title": "Overdue (Critical)",
    "Style": {
      "Icon": "CriticalColorStyle"
    }
  }],
  "Event": {
    "IndicatorName": "/MyApp/Rules/GetIndicators.js"
  },
  "Targets": [
    {
      "Service":"/MyApp/Services/EquipmentMaintenance.service",
      "EntitySet": "ServiceOrders",
      "DateProperties":["DueDate"]
    }
  ]
}

Whenever user scrolls through the calendar, the app will send queries to retrieve ServiceOrders entities with DueDate that fall within the months that user is scrolling to. Finally, the app will render the indicators on the dates that matches the entities' DueDate.

By default, at least 3 months of data will be retrieved, which is the month user scrolled to and the month before and after, it can be configured with EventLoadRange.

If user scrolls to the month of September 2025, a query that looks like this /ServiceOrders?$filter=(DueDate ge datetime'2025-08-01T00:00:00' AND DueDate le datetime'2025-10-31T00:00:00') will be sent and as an example, the OData service might return something like the following:

[
  { "@odata.readLink": "ServicerOrders(1001)", "DueDate":"2025-08-22T00:00:00", "Priority":"Low"},
  { "@odata.readLink": "ServicerOrders(1801)", "DueDate":"2025-09-02T00:00:00", "Priority":"Medium"},
  { "@odata.readLink": "ServicerOrders(3005)", "DueDate":"2025-09-06T00:00:00", "Priority":"High"},
  { "@odata.readLink": "ServicerOrders(3011)", "DueDate":"2025-10-07T00:00:00", "Priority":"Low"},
  { "@odata.readLink": "ServicerOrders(4411)", "DueDate":"2025-10-07T00:00:00", "Priority":"High"}
]
// Note that the `DueDate` values all falls within the range.

In this case, 4 dates, 22-Aug, 02-Sep, 06-Sep, and 07-Oct, will be displayed with indicators in the Calendar.

If you have set your Event's IndicatorName with a rule, it will be triggered for each date that is found to contain entities.

When triggered, the rule will receive a CalendarProxy instance with a binding that contains Date and EntitySets property. You can access them via clientAPI.binding.Date or clientAPI.binding.EntitySets, assuming your rule's first argument is named clientAPI.

  • Date will contain the ISO 8601 formatted date that tells you which date is being resolved
  • EntitySets is an object that contains one or more names of the OData entity set you have defined in the Targets binding. Each entity set is an array that contains the entities that falls on that date based the property you set in DateProperties (in this example, the DueDate).

With these information, you can decide indicator's name to be returned to be displayed on a specific date.

Using the above ServiceOrders data set as example, your rule will be triggered 4 times, each time the binding will contain data as such:

{
  "Date":"Fri Aug 29 2025 00:00:00 GMT+0800 (+08) {}", //This property will be a JavaScript Date object, not a string
  "EntitySets": {
    "ServiceOrders":[
      {
        "@odata.readLink": "/ServiceOrders(1001)",
        "DueDate": "2025-08-22T00:00:00", //OData date will be in ISO8601 format
        "Priority": "Low",
        "AndOtherProperties": "..."
      }
    ]
  }
}
{
  "Date":"Tue Sep 02 2025 00:00:00 GMT+0800 (+08) {}", //This property will be a JavaScript Date object, not a string
  "EntitySets": {
    "ServiceOrders":[
      {
        "@odata.readLink": "/ServiceOrders(1801)",
        "DueDate": "2025-09-02T00:00:00", //OData date will be in ISO8601 format
        "Priority": "Medium",
        "AndOtherProperties": "..."
      }
    ]
  }
}
{
  "Date":"Sat Sep 06 2025 00:00:00 GMT+0800 (+08) {}", //This property will be a JavaScript Date object, not a string
  "EntitySets": {
    "ServiceOrders":[
      {
        "@odata.readLink": "/ServiceOrders(3005)",
        "DueDate": "2025-09-06T00:00:00", //OData date will be in ISO8601 format
        "Priority": "High",
        "AndOtherProperties": "..."
      }
    ]
  }
}
{
  "Date":"Tue Oct 07 2025 00:00:00 GMT+0800 (+08) {}", //NOTE: this property will be a JavaScript Date object, not a string
  "EntitySets": {
    "ServiceOrders":[
      {
        "@odata.readLink": "/ServiceOrders(3011)",
        "DueDate": "2025-10-07T00:00:00", // NOTE: OData date will be in ISO8601 format
        "Priority": "Low",
        "AndOtherProperties": "..."
      },
      {
        "@odata.readLink": "/ServiceOrders(4411)",
        "DueDate": "2025-10-07T00:00:00", // NOTE: OData date will be in ISO8601 format
        "Priority": "High",
        "AndOtherProperties": "..."
      }
    ]
  }
}

And you can then use these information to make decision and return a specific indicator's name, such as the following:

export default function GetIndicators(clientAPI) {
  let today = new Date();
  let targetDate = clientAPI.binding.Date;
  let entitySets = clientAPI.binding.EntitySets;
  // display OverdueIndicator because the entities found has DueDate older than today
  if (targetDate < today) {
    // return HighPrioOverdueIndicator to be displayed if there's even one entity with priority=High falls on this date
    if (entitySets && entitySets.ServiceOrders) {
      const found = entitySets.ServiceOrders.find((element) => element.Priority === "High");
    }
    return "OverdueIndicator";
  }
  else { // else return UpcomingIndicator
    return "UpcomingIndicator";
  }
}

API Reference


Last update: September 20, 2025