Search⌘ K

Using the Data Store

Explore how to manage application state in Stimulus code by implementing a central data store with reducer patterns. Understand how to connect Stimulus controllers to this store to synchronize UI components like calendar filters. Learn methods for subscribing to state changes, toggling filter states, and handling edge cases such as showing all filters. This lesson helps you build modular, reactive UI components within Rails using Stimulus while managing state beyond the DOM.

We'll cover the following...

Let’s start with the CalendarFilterController. We need to define it in the ERb for the schedule show page. Right at the top of the file, the loop over the calendar days now looks like this:

HTML
<% @schedule.schedule_days.each do |schedule_day| %>
<% date_string = schedule_day.day.by_example("2006-01-02") %>
<section class="column"
data-controller="calendar-filter"
data-calendar-filter-selected-class="has-text-danger"
data-calendar-filter-date-value="<%= date_string %>">
<div class="has-text-centered js--schedule-day"
data-action="click->calendar-filter#toggle"
data-calendar-filter-target="display">
<%= schedule_day.day.by_example("Jan 2") %>
</div>
</section>
<% end %>

There’s a lot going on here, in part because some existing Stimulus controllers are also being tagged. Inside the section tag, we declare the data-controller=calendar-filter and also use the Stimulus class and values API to define a hidden class and a date value, which is a string representation of the date formatted like 2020-01-21. The div tag inside the section declares a click action, calling a toggle method on the filter.

The controller code itself looks like this:

TypeScript 3.3.4
import { Controller } from "stimulus"
import { CalendarFilterStore } from "../src/calendar_filter_store"
export default class extends Controller {
static classes = ["selected"]
static targets = ["display"]
static values = { date: String }
selectedClass: string
dateValue: string
displayTarget: HTMLElement
connect(): void {
CalendarFilterStore.store().addFilter(this.dateValue)
CalendarFilterStore.store().addSubscriber(this)
}
calendarFilterChanged(store): void {
this.displayTarget.classList.toggle(
this.selectedClass,
store.filterStates[this.dateValue]
)
}
toggle(): void {
CalendarFilterStore.store().toggleDateStatus(this.dateValue)
}
}

After all ...