SmartFiltering Multiple Regions

A fellow Oracle employee wielding the power of APEX to solve an internal business problem for her team asked an interesting question on our internal discussion forum this week. Her app shows resources partitioned into three categories in side-by-side cards regions. She wanted to use a single SmartFilter region to filter the results showing up in these three regions, each based on the same underlying table with a slightly different WHERE clause.

Wherever possible, my brain tends to map questions onto the familiar EMP and DEPT tables to simmer a question down to its essence. So I imagined side-by-side cards regions with employees in three different departments as shown below.

Three EMP-based cards regions partitioned by department

Setting Up the Page

I started by setting up the three cards regions based on the EMP table, and including an appropriate DEPTNO=10 where clause in the “Accounting” region, DEPTNO=20 predicate in the “Research” region, and DEPTNO=30 in the “Sales” region. Then I added a SmartFilter region and positioned it in the Before Navigation Bar slot with the w100p class to make to stretch to 100% width and fill up the space left in the center of the navigation bar.

A Twist on The Gift That Keeps on Giving

By default, a SmartFilter region filters the results of only one other region on the page. However, my colleague Carsten’s article Add a Chart to your Faceted Search Page is the gift that keeps on giving, because it inspired me with the idea I used to achieve the result our fellow APEX user desired. Carsten explains therein how to use the open_query_context() function in the apex_region package in a pipelined table function that returns the results from another region’s data source, automatically taking into account the search filters currently applied by the end user in that region. The implementation uses functions in the apex_exec package to retrieve the data.

My twist on his idea was to create a pipelined table function get_region_results_pk_values()to return only the primary key values of a given region’s filtered results. Assuming a numeric primary key column, the get_region_results_pk_values() function can accept three parameters:

  1. Page id
  2. Static ID of a region on that page
  3. Name of the primary key column whose value we should return

The function’s pipelined result rows will each contain a NUMBER column named ID containing the primary key values identifying the rows that the filtered region is returning. For our employee-based example here, it will return ID values representing the EMPNO values of the filtered region’s query results. With this function in place, we can update the WHERE clause of the three employees cards regions to reference get_region_results_pk_values() in an additional predicate like this:

  • “Accounting”
    • deptno=10 and empno in (select id from get_region_results_pk_values(:APP_PAGE_ID,'RESULTS','EMPNO'))
  • “Research”
    • deptno=20 and empno in (select id from get_region_results_pk_values(:APP_PAGE_ID,'RESULTS','EMPNO'))
  • “Sales”
    • deptno=30 and empno in (select id from get_region_results_pk_values(:APP_PAGE_ID,'RESULTS','EMPNO'))

Configuring the Filtered Region

To play the role of the SmartFilter region’s Filtered Region, we need an additional region based on the EMP table with no WHERE clause of its own. This is because using any of the Accounting, Research, or Sales cards regions would only filter the employee rows of one particular department and consequently its list of filtered EMPNO primary key values would not correspond to any employees in the other cards regions who, by design, belong to a different department.

So, I added an EmployeesResults classic report region based on the EMP table and configured this classic report region to be the SmartFilter region’s Filtered Region. I set the static ID of this region to RESULTS as referenced in the three WHERE clause function calls above.

A Chain Reaction of Filtering

When the end-user changes the filters on a SmartFilter, it automatically refreshes its filtered region. So in our example, the EmployeesResults classic report region will be refreshed. When this happens, we need the other three cards regions on the page to also get refreshed. Luckily, this is simple to achieve with a dynamic action event handler on the EmployeesResults region’s AfterRefresh event. I included three dynamic action steps in the TRUE branch of the event handler. Each one uses the built-in Refresh action to refresh one of the cards regions.

AfterRefresh dynamic action on filtered region refreshes employees cards regions

At this point, I had a working page with a SmartFilter at the top, whose search filters would restrict the rows in the filtered EmployeesResults region at the bottom, and whose filtered list of primary keys were correctly filtering the three separate Accounting, Research, and Sales cards regions as desired. However, seeing the classic report at the bottom was ruining the magic effect, so I sought out a way to hide it.

Classic report filtered region displays, ruining the magic

Hiding the Filtered Region

My first experiment was to set the Server-side Condition on the filtered EmployeesResults region to Never. This succeeded in hiding the classic report, but it also broke the chained filtering functionality being implemented by the dynamic action. I guess a region that’s not rendered on the server can’t be used to trigger events on the client, which I could understand.

For my second attempt, I went looking for properties of the classic report region that I could adjust to limit the data that it was retrieving. After all, I primarily cared about the filters applied to its data source, not seeing the results themselves in the classic report. These applied filters are important to correctly produce the filtered list of primary keys to filter the Accounting, Research, and Sales cards regions appropriately.

With the classic report EmployeesResults region selected, I studied the Attributes tab of the property palette and noticed a Number of Rows property which I tried setting to zero (0). I figured the classic report functionality related to pagination might be producing some content on the page, so I also set the Pagination > Type to the value No Pagination (Show All Rows). Since I knew that the SmartFilter region uses partial page refresh to update the filtered region, I kept the Partial Page Refresh switch in the ON position.

Setting the classic report to retrieve no rows & skip pagination, keeping partial page refresh

These changes got me very close to the desired result, but as shown below the column headings were still showing.

Almost There! The Filter chaining works, but the filtered region’s column headers still show

I managed to resolve this final issue by hiding the classic report’s column headings using the CSS property display:none. I accomplished this by adding a CSS class called hidden to the page-level CSS > Inline settings:

hidden {
   display: none;
}

After doing this, I configured the EmployeesResults classic report region’s Column CSS Classes to be the name of my hidden CSS class. After doing that, I had a working SmartFilter that was filtering the three separate cards regions, without showing the classic report region involved in triggering the chain reaction of filtering.

Filtering Code In the Demo

In practice, the APEX 21.2 demo app that you can download using the link below is based on an EMP-like table called EBA_DEMO_WORKLIST_EMP and the filtering code for the function is in a package named EBA_DEMO_WORKLIST whose specification looks like this. The package body looks very similar to Carsten’s blog article, but it’s simplified to only return the single primary key column of results in each row.

create or replace package eba_demo_worklist as 
    type eba_demo_worklist_pk_t_row is record (
        id number
    );

    type eba_demo_worklist_pk_t_tab is table 
        of eba_demo_worklist_pk_t_row;

    function get_region_results_pk_values( 
        p_page_id          in number,
        p_region_static_id in varchar2,
        p_pk_column_name in varchar2 )  
    return eba_demo_worklist_pk_t_tab pipelined;
end;
 

You can download the demo app from here, and install it into an APEX 21.2 (or later).

Adding Tags Using Multi-Value PopupLOVs and Smart Filters

Overview

Google’s Gmail was the first app I remember that popularized users’ inventing their own descriptive labels and applying them as “tags” to data. Instead of moving an email from the “Inbox” folder into one named “Charitable Donations 2019 USA”, for example, it encouraged me to tag the message with multiple, distinct labels like “2019”, “taxes”, “charitable donations”, and “USA”. While not initially as intuitive as folders, this approach quickly proved its value. It let me quickly locate mails related to a particular year, to charitable donations, to taxes in general, or to the US, or any combination of those criteria.

For the same reasons, a flexible tagging facility comes in handy for many kinds of data we work with everyday. Whether it’s pictures in a photo library, products in an online store, or books in your collection, tags that help users quickly find what they are looking for are a boon. When combined with Oracle APEX’s powerful faceted search and Smart Filters capabilities, it packs a powerful productivity punch.

In this article, I explain the technique I used over the holidays to add a flexible tagging facility to my art tracker application using a no-code approach that takes advantage of APEX’s multiple value support in popup LOV page items and Smart Filters. Here, we’ll apply the approach to a simple application that tracks books, publishers and authors. You can find the link to download the example application at the end of the article.

Book Finder page featuring Cards region showing title, authors, publisher, and tags

Multi-Value PopupLOV for Tags

The BOOK_TAGS table defines an ID and NAME for the descriptive tags you can apply to books. The sample data includes pre-defined tags like “Trains” (2), “Travel Narrative” (1), “Children” (5), “Fiction” (6), and others.

The BOOK_TITLES table contains a book’s TITLE, the PUBLISHER_ID, and a TAGS column, whose value is a colon-separated list of tag ids. For example, a book that is a fictional travel narrative about riding on trains might have the value “6:1:2” representing the id values of the three tags “Fiction”, “Travel Narrative”, and “Trains”. Since the order of the the tags is not significant to our use case, it also might have the value “1:2:6” depending on the order in which the end-user added the tags to the list.

The figure below shows how I configured the P3_TAGS page item in page 3’s form region to support visualizing and editing the possibly-multiple tags applied to a book. Notice that the page item type is Popup LOV, the Multiple Values property is enabled, the literal colon character (:) is indicated as the value Separator, and the Search as You Type property is on. I’ve configured a shared component TAGS_LOV List of Values to provide the alphabetized list of available tags.

Configuring multi-value popup LOV page item in an edit form page

Pay special attention to the Manual Entry setting above. We’ve consciously disabled that property since by design at the moment APEX shows the end user the underlying values (e.g. 6 , 1 , 2 ) for a manual-entry popup LOV instead of showing their corresponding display text values (e.g. Fiction , Travel Narrative, Trains ). That may be appropriate for some multi-value use cases where the LOV entry’s display and return values are the same, but here I preferred that my end-users would see the tag display values.

The result is the easy-to-use book editing page shown below. Notice that the P3_AUTHOR_IDS page item is configured in the same way as P3_TAGS to allow entering the colon-separated list of one or more author ids for the book.

Multi-value popup LOV page items editing book details at runtime

Multi-Value Smart Filter for Tags

APEX makes it simple to easily filter on one or more tags applied to records in both faceted search as well as the new Smart Filters region. The figure below shows how the P5_TAGS smart filters search facet in the Book Finder cards region page (5) is configured to enable this behavior. Notice the search facet Type is set to Checkbox Group, the Multiple ValuesType is set to Delimited List with the Separator configured to be a literal colon character (:), and we’ve enabled the Trim Whitespace option.

Configuring multi-value P5_TAGS smart filter facet to handle colon-delimited values

This is the only configuration necessary to get the tags field working for searching. The result produces a smart filter search field (above our cards region) on page 5 in the example app that looks like the figure below. Ticking one or more tags in the list narrows the search results to show only books having that/those tags applied (in any order).

Checkbox Group smart filters facet to search for books by one or more tags applied.

As above, the P5_AUTHOR_IDS search facet has been configured identically to the P5_TAGS one to allow narrowing down the search results by any combination of authors and/or tags as shown below where we’ve found books authored by Brian Spendolini about APEX.

Applying two multi-value smart filters: one for authors, one for tags

Displaying Multi-Value Fields in Report Regions

When working with multi-value columns like TAGS and AUTHOR_IDS in our BOOK_TITLES example table, it’s useful in report pages or card regions to show the list of display values corresponding to the one-or-more ids stored in the colon-separated column value. For this task, I employed the handy LISTAGG() function to aggregate the set of related tag display values and author names into an ordered, comma-separated list. I combined it with the useful split_numbers() function in the apex_string utility package. When wrapped by a table() operator, this helpful routine lets us select the numbers in the colon-separated list as a table row source right in the query. To make it easier to use this information from any report or card regions where I needed it in my application, I created the BOOK_TITLES_V database view with the following SELECT statement.

select 
    b.id,
    b.title,
    b.tags,
    b.rating,
    b.publisher_id,
    b.author_ids,
    (select listagg(name,', ')
            within group (order by name collate binary_ai)
       from book_tags 
      where id in (
        select column_value 
          from table(apex_string.split_numbers(b.tags,':'))
        )
    ) display_tags,
    (select listagg(name,', ')
            within group (order by name collate binary_ai)
      from book_authors 
     where id in (
       select column_value 
         from table(apex_string.split_numbers(b.author_ids,':')))
    ) display_authors,
    p.name as publisher
from book_titles b
left outer join book_publisher p 
             on p.id = b.publisher_id

Notice that I’m passing the literal colon character as the separator to the apex_string.split_numbers() function in two places, and passing the separator string consisting of a comma followed by a space to the listagg() function. The collate binary_ai keywords in the order by part of the listagg function’s within group clause ensures that display values sort in an accent-insensitive (and case-insensitive) way.

Enabling Custom PopupLOV Behavior via CSS Class

I had achieved my goal without writing any code and was happy with how simple it was to implement… when I noticed an interesting extra-credit opportunity that piqued my interest.

While interacting with the PopupLOV component, I observed that its Search as You Type filter remained “sticky” across multiple interactions with the dropdown list of choices. For example, consider the screenshot below where I was editing the tags for a Bill Bryson book. If I typed tr into the PopupLOV search field, as expected the list narrowed down to only show relevant tags “Trains” and “Travel Narrative”. However after choosing “Trains”, if I clicked again into the multi-value field to drop-down the list to choose another tag to apply, the list remained filtered as before to only those tags containing tr in their names. However, often the next tag I wanted to apply required me to clear the previous search field text to start fresh with the complete list of tags to choose from or search through.

Search as You Type filter in use in a PopupLOV for tags applied to a book.

After not finding any declarative PopupLOV setting to control the “stickiness” of the search field value, I began by experimenting with various ideas using dynamic actions to force the PopupLOV search field to clear. Not satisfied with the results, I reached out to my colleague John for some expert advice.

He suggested I implement generic JavaScript code in an app.js static application file that would automatically enable a custom behavior whenever a PopupLOV page item had a particular custom CSS class applied to it. This way, the code was reusable and enabled declaratively by simply adding a CSS class name like popup-lov-reset when the non-default search-field-resetting behavior was desired. The figure below shows the modal Edit Book page (12) called by the full card action on the card region of the Book Finder page (5). Notice the custom CSS class name popup-lov-reset in the AdvancedCSS Classes section. This is the signal to our generic code in app.js that this particular PopupLOV prefers the reset-search-field behavior each time the user engages the page item’s dropdown list.

Opting-in to custom application behavior by adding a custom CSS class to a page item

If you’re curious, you can study the full details of the custom JavaScript code John helped me with in the downloaded example app. However, most important were the higher-level principles he taught me along the way about how he recommends structuring application-level JavaScript code. Since I’m not a JavaScript expert, these were the even more interesting bits of precious knowledge that I felt fortunate to learn from him.

The high-level structure of the code in the example application’s app.js file appears below. It exposes a single app namespace inside of which can appear private functions specific to its implementation. This app namespace exports only the members it wants to be the public API. In this application, only the single function handlePopupLOVsWithSearchResetClass() is exported.

// Export just a single global symbol "app" to keep code clean
const app = (function($) {

    // Function private to the impl of the
    // exported 'app' namespace

    function makeResetPopupLov(itemName) {
      // Code removed for clarity here registers an event
      // handler on body of the page to react to the popupopen
      // event of PopupLOV page item in question.
      // See example app for full details.
    }

    // app namespace members
    return {
        //----------------------------------------------------
        // Turn any PopupLOV page items on page into ones that
        // reset their search field when dropdown pops open. 
        //----------------------------------------------------
        handlePopupLOVsWithSearchResetClass: function() {
            $(".popup-lov-reset.apex-item-popup-lov").each(
                function() {
                  makeResetPopupLov(this.id);
                }
             );
        }
    };
    // Ensure $ in app namespace resolves to correct jQuery
})(apex.jQuery);

If we include the “document ready” event handler code below inside the app.js file, then all pages in the application magically inherit the ability to have any PopupLOV page item on the page opt-in to reset-search-field behavior just by adding the CSS class name popup-lov-reset in the page item’s AdvancedCSS Classes section. In contrast, if you only want the functionality to be available on selected pages, then include this event handler code just on the specific pages where you want the behavior to be available.

// Inside app.js, runs for every page after document is ready
// Alternatively, you can just add to the pages where you want
// the behavior to be available.
//
// "Document Ready" event handler code
$(function() {
    app.handlePopupLOVsWithSearchResetClass();
});

If you want to try out the example for yourself, then download the APEX 21.2 example application here.

Enhancing Existing APEX App with PWA, Nav Bar Items & Smart Filters

I was excited to try three powerful new no-code-required APEX 21.2 features in my art tracker application, and I finally had an hour to spare after work this week to give it a go. This article documents my successful attempt to evolve the showcase page from my existing app to leverage the space-saving Smart Filters search region, leaving more room for the cards that visualize the main application content.

In the process I regained even more screen real estate by positioning items in the navigation bar using the new layout positions available in 21.2’s Universal Theme. And last but not least, I enabled users of my app to install it like a native desktop or mobile application for quick access from the home screen or dock and faster startup time. Read on if any (or all!) of these features sound like they might be a useful upgrade in your own APEX application.

The screenshot below shows the main page of my application before the enhancements. It employs a faceted search region and two select-list page items in a Left Column layout position to let users find the artworks they are looking for. They can also switch dynamically among several useful orderings and interactively change the display size of the images. These complement a Cards region showing the works of art they are tracking.

Main page of an APEX 21.1 art collection app using faceted search and cards region

Setting the Stage

While none of these three new features required writing any code, the simplest of all to enable was the installable Progressive Web App capability. I first needed to ensure my app was using Friendly URLs, as shown below in the application definition’s Properties tab. While I was here, I also changed the Compatibility Mode to 21.2, which is a prerequisite to refreshing its Universal Theme version to 21.2. As we’ll see shortly, this theme refresh allowed me to access the new, flexible layout positions in Page Designer.

Ensuring friendly URLs are used and setting compatibility mode to 21.2

Installable Native App in Two Clicks

With these two settings configured appropriately, I proceeded to enhance my application to allow installation on desktop or mobile devices. As shown below, on the Progressive Web App tab of the application definition, I just needed to switch on the Enable Progressive Web App toggle, as well as the Installable toggle. The former enables the device to cache key application resources for faster startup, while the latter lets users add the app to their home screen or dock to launch it like every other app on their computer, phone, or tablet.

Turning your app with friendly URLs into an installable native app in two clicks

Adding Existing Items to the Nav Bar

The new page item positions are made available by the 21.2 version of the Universal Theme. Since my existing application is using the theme’s 21.1 version, the new positions won’t show in Page Designer until I’ve refreshed the theme to version 21.2. Above, we set the application’s Compatibility Mode to 21.2. This is a required step to be able to refresh our theme to the latest 21.2 version that supports the new page item positions.

Refreshing the Theme

From my application’s Shared Components page, I clicked on Themes. My application only uses a single theme “Universal Theme” (number 42), so I clicked on its name to edit it. On the Edit Theme page, as shown below, clicking the (Refresh Theme) button gets the job done. If by chance I had forgotten to update the application compatibility mode to 21.2, I would have seen a helpful error message telling me to go do that first before attempting again to refresh the theme.

Refreshing Universal Theme to latest 21.2 version to see new component positions

Seeing the New Page Positions in Page Designer

After updating the Universal Theme, editing the page in Page Designer shows the new positions. The one I was specifically interested in is Before Navigation Bar. This will allow me to position my sort control, size control, and new smart filter search field into the previously unused space in that bar. If you don’t see the Before Navigation Bar position (or any of the other new ones) you might have hidden the empty positions. If so, then as shown below, right-click on the layout panel and ensure that Hide Empty Positions is unchecked.

Existing art collection page in Page Designer before starting the changes

Moving Existing Items/Regions into New Positions

I had previously grouped the two select lists that control the dynamic sorting and card image size into a static content region called Sort. I could have simply set the Layout Position property of this Sort region to Before Navigation Bar, but I wanted to see how individual items could be placed into the new positions without belonging to a region, so instead I did the following for both the P18_ORDER_BY and P18_ZOOM page items:

  • Set the Layout Parent Region property to No Parent.
  • Set the Layout Position property to Before Navigation Bar

Changing Faceted Search to Smart Filters

My existing page was using the Left Side Column template, and the existing faceted search region (named Filter) resided in the template’s Left Column position. So I started by selecting the Filter faceted search region and changing its Layout Position property to Before Navigation Bar.

After doing this, I no longer needed the Left Side Column page template, so I set the page template to Theme Default to free up the space currently taken up by the left column, and deleted the now-empty Sort static content region. After, right-clicking on the layout pane to select Hide Empty Positions, the in-progress page looked like what you see below.

In-progress work after moving items and Filter region to Before Navigation Bar position

Next, I needed to change the region type of the Filter region from Faceted Search to Smart Filters. However, before doing this, for each facet I made a note of the following three elements of its definition:

  • Facet name (e.g. P18_ARTWORK_TYPE)
  • List of Values name (e.g. TYPE_LOV)
  • Icon name (e.g. fa-shapes)

This step simplified my recreating the same filters under the Smart Filter region. At the moment APEX does not automatically convert the existing facets into filters when the region type changes from Faceted Search to Smart Filters. However, with the information noted down above regarding the facet names, LOV names, and icon names, setting up the new filters was just a few additional clicks. So, in practice this missing automatic conversion was not a big deal.

After changing the Filter region’s type from Faceted Search to Smart Filters, an initial filter named P18_SEARCH got created for me. This is the mandatory search filter that will search on row text by default. Next I proceeded to recreate the five additional filters using the same names, corresponding List of Values, and icon names that the facets were using previously. I kept the default Checkbox Group type for each filter I created, and finally (based on a tip from my colleague Vincent) I set the CSS class on the Filter smart filter region to w100p so that it would stretch to fill up the space in the navigation bar. Then, I ran the page to test out the upgraded functionality.

As shown below, by default the Smart Filters region shows a suggestion “chip” for each filter category below the search bar with the most popular value and a record count for each filter type. [Note: the record counts have been replaced in the screenshot by (..)]

By default, the Smart Filter search region shows suggestion chips for each filter with counts

These suggestion chips simultaneously educate the end-user about the available filters as well as provide useful information about the most popular value for each category. Clicking on the label of a particular filter chip, the end-user can select specific value(s) they want to search for in that category. In contrast, clicking on the suggestion chip value, applies that most-popular value to the user’s search.

For my particular application, I preferred to further streamline the navigation bar by disabling these suggestions. To suppress the suggestion chips, I set the Maximum Suggestion Chips to the value zero (0) on the Attributes tab of the Filter region as shown below.

Disabling the Smart Filter suggestion chips for a region if desired.

Installing the App on Windows and iPad

After saving this Page Designer change and re-running the page, I saw the result below without the suggestion chips. Notice the new (Install App) button in the navigation bar. When I enabled the Progressive Web App feature above, the APEX builder added a new item into the Navigation Bar list which advertises the ability for the user to install the application on their device.

Enabling PWA with the Installable option adds a new (Install App) navigation bar button

Clicking on the (Install App) button in Chrome or Edge desktop browser let me install the application in one click on my desktop. I tried the same thing on my iPad and there the app can also be installed using the native iOS gesture of clicking on the share icon and choosing “Add to Home Screen” as shown below.

Adding your installable APEX application to the iPad’s home screen

This allowed me to long-press on the home screen icon for the application to enter “wiggle mode” so I could drag it into to my iPad’s dock like this:

Native app icon for an APEX application in the iPad dock

Clicking this new iPad dock icon launched the app into a full screen experience indistinguishable from other native apps on my iPad as shown below. Also notice that the (Install App) navigation bar button no longer displays, so APEX has configured the new navigation bar list entry to be conditionally displayed only when it makes sense. A nice touch!

Full screen iPad app user experience looks like every native app

Trying the Smart Filters User Experience

Back on my Windows desktop machine, I repeated this same experience of launching the application from the dock and noticed it now displayed in its own window with no browser address bar as shown below. Clicking into the Smart Filters search field, the user sees which filter categories are available and can click one of them to choose specific values to search for in that category…

Filter categories suggest the smart ways the user can narrow their search

Alternatively, the user can just start typing some text to search for. As shown below, after typing the two characters gr into the search field, the Smart Filters region shows matches in any appropriate categories. It’s showing some artists (e.g. Gregory Mason, Rich Pellegrino), an artwork type (Photography), and some sources (i.e. stores) like Ross Art Group and others, all which contain a “gr” in their name. If desired, clicking on one of these filter category matches will apply that filter to the search for the respective category.

Filter search matches show in the drop down as the user types

The user can ignore the filter category suggestions and just keep typing, for example, to enter the search term grand and upon pressing [Enter] they’ll see the artworks that contain grand in their row text (title plus other card text) as shown below.

Resulting show all types of artwork with “grand” in their title

Clicking again into the search field, suppose the user clicked on the Type filter category and selected Painting. That would result in the situation shown below where the results have been narrowed to only show paintings with “grand” in their title.

Results filtered to show only paintings with “grand” in the title

Clicking back on the “Type: Painting” filter chip in the search bar, the user can select other types of artwork to also include as shown below. The set of choices available is automatically filtered to reflect the narrowed search results so other types of artwork like “Animation Cel” are not in the list since no animation cel in the collection contains the word “grand” in the title or descriptive text.

Adding additional types of artwork to the search

After further narrowing the results by Decade and Source, the result looks like what you see below. Clicking back into the search field only shows those filter categories that are not already involved in the filter.

After applying multiple filters, only unused filter types show in the dropdown

I was very satisfied with the functionality and additional screen space I gained in my application by uptaking these new features. In short order, without having to write any code, I evolved my application to give users more room to see the primary subject of the application (the artwork cards!) while gaining a compact searching interface every bit as powerful and smart as the faceted search I was using before.

What’s more, enabling the installable Progressive Web App (PWA) feature let my users have a more native desktop and mobile experience that looks like all the other apps on their machine when they’re using my app.

Of course, in many use cases users may appreciate a more “AirBnB”- or “Amazon”-style search experience with the faceted search items always visible, but the new Smart Filters region gives developers a new option to consider when users may appreciate the powerful search capability to be more compact so other page content can be the star of the “show”.

I encourage you all to give these three new features a try!

P.S. In the process of doing the enhancements above, I also simplified the P18_ORDER_BY and P18_ZOOM select list items to have no visible label and fiddled with CSS styling to get these two items to be positioned and sized exactly how I wanted them in the navigation bar. But as I’m not a CSS expert, I didn’t want to offer any advice on what I did to achieve the final result since I’m not certain yet that it’s the best way. Once I have a chance to check with colleagues with more CSS expertise, I’ll either write a new blog article about that or update this article with more specific advice.