Using Events to Filter & Select Contacts in Grid
Now that you have the CRM data in the store, you can build the list view functionality.
This chapter covers:
-
listening to events on the search field,
-
creating a view-specific store,
-
using computed properties for filtering contacts,
-
handling grid selection.
Filtering Contacts in Grid
The grid now displays all the contacts in the system. You want it to display only contacts that match the search field input. You can achieve this using computed properties.
Create a new MobX store for the list view, frontend/views/list/list-view-store.ts
.
import Contact from 'Frontend/generated/com/example/application/data/entity/Contact';
import ContactModel from 'Frontend/generated/com/example/application/data/entity/ContactModel';
import { crmStore } from 'Frontend/stores/app-store';
import { makeAutoObservable, observable } from 'mobx';
class ListViewStore {
filterText = '';
constructor() {
makeAutoObservable(
this,
{},
{ autoBind: true }
);
}
updateFilter(filterText: string) {
this.filterText = filterText;
}
get filteredContacts() {
const filter = new RegExp(this.filterText, 'i');
const contacts = crmStore.contacts;
return contacts.filter((contact) =>
filter.test(`${contact.firstName} ${contact.lastName}`)
);
}
}
export const listViewStore = new ListViewStore();
The list view store follows the same pattern as the CRM store.
It has one observable property, filterText
, and one computed property, filteredContacts
, which depends on the filterText
and the crmStore.contacts
array.
Any time any of the dependencies of a computed property changes, the property is re-evaluated.
The computed property returns an array of contacts that match the filterText
.
Update the search field template in list view.
Make sure to import listViewStore
.
<vaadin-text-field
placeholder="Filter by name"
.value=${listViewStore.filterText}
@input=${this.updateFilter}
clear-button-visible
></vaadin-text-field>
Bind the value
property (input text) to filterText
.
Add an event listener for the input
event and bind it to the updateFilter()
method.
Implement updateFilter()
after the render()
method:
updateFilter(e: { target: HTMLInputElement }) {
listViewStore.updateFilter(e.target.value);
}
The method reads the input value though the event (e.target.value
) and calls the updateFilter()
action.
The action changes the filterText
observable, which in turn updates the filteredContacts
computed property.
The only thing needed to complete the filtering functionality is to change the grid template and bind to the computed filteredContacts
property, instead of the array of all contacts.
<vaadin-grid class="grid h-full" .items=${listViewStore.filteredContacts}>
In your browser, try out the filtering functionality by typing something into the filter input box.

Selecting Contacts in Grid
The list view store should also track the selected contact. Start by adding a new observable property in the list view store:
selectedContact: Contact | null = null;
The selectedContact
observable is either a Contact
or null
.
By default, MobX creates proxy objects for observable values.
The downside of using proxy objects is that it breaks equality checks that Hilla Grid uses for tracking items.
In this case, you aren’t interested in tracking changes to any of the properties of the selected contact, only in being notified when the contact changes.
Update the constructor to override the observable behavior to observable.ref
, which tracks the object reference without creating a proxy.
constructor() {
makeAutoObservable(
this,
{ selectedContact: observable.ref },
{ autoBind: true }
);
}
Lastly, add a new action to the store to update the selected contact.
setSelectedContact(contact: Contact) {
this.selectedContact = contact;
}
Update the grid template.
<vaadin-grid
class="grid h-full"
.items=${listViewStore.filteredContacts}
.selectedItems=${[listViewStore.selectedContact]}
@active-item-changed=${this.handleGridSelection}>
Grid supports multiple selection, so the selectedItems
property needs to be expressed as a single-item array.
Bind the active-item-changed
event to a new method, handleGridSelection()
.
Implement the new method at the end of the class.
// vaadin-grid fires a null-event when initialized. Ignore it.
firstSelectionEvent = true;
handleGridSelection(e: CustomEvent) {
if (this.firstSelectionEvent) {
this.firstSelectionEvent = false;
return;
}
listViewStore.setSelectedContact(e.detail.value);
}
The method calls the setSelectedContact()
action with the value from the event, either a Contact
or null
.
Hilla Grid fires an event with a null
selection when it initializes, which you can ignore by adding a guard expression.
In your browser, you should now be able to click on a row and see that it gets highlighted. In the next chapter, you use the selected contact to populate the edit form.
