How to Set, Clear, and Reset Facets Using JavaScript in Oracle APEX Faceted Search

A tested guide to controlling Oracle APEX faceted search and smart filters from JavaScript, from facet value formats to counts, charts, and resets.

Faceted search and smart filters let users narrow a report by clicking values instead of writing queries. Sooner or later you want to drive them from code too: preset a filter from a link, clear everything from a button, show a chart of the counts, or read how many records match.

Both region types share one JavaScript interface, facetsRegion. This guide covers how to set facet values from JavaScript, including the value formats each facet type expects, and every method of the interface, from reading counts to clearing, hiding, locking, charting, and applying facets. Each has a tested example with the real output.

Quick Reference

TaskHow
Set a facet valueapex.item(facetItem).setValue(value)
React to any facet changeThe facetschange event
Count active facetsgetFacetCount
Read the matching total and per-value countsgetTotalResourceCount, getFacetValueCounts
Remove filtersclear, clearFacets, reset
Hide or show a facethideFacet, showFacet
Show or remove a chart of a facetaddChart, removeChart
Disable facets during worklock, unlock
Refetch counts, apply batched changes, redrawfetchCounts, apply, refreshView
Move focus to the search fieldfocus

How to Run These Examples

The examples ran in Oracle APEX 26.1 on a products page. A faceted search region with the DOM ID product_search filters a classic report with the DOM ID products. Its facets are the page items P4_CATEGORY_ID, P4_SUPPLIER_ID, P4_PRICE, P4_COLOR, and P4_IS_ACTIVE, plus the search field P4_SEARCH. The output under each example is exactly what the browser console printed.

To try one, open a page with faceted search, press F12, paste the code into the Console tab, and replace the region ID and item names with your own. Several examples pause with await at the top level while the report and counts reload, which the console allows; inside a dynamic action, wrap such code in an async function.

For building the regions themselves in Page Designer, see the guide to faceted search, smart filters, and search in Oracle APEX.

Faceted Search vs Smart Filters in JavaScript

Both regions report the type Facets and use the facets widget. The uiMode property tells them apart: "F" for faceted search, "S" for smart filters. Either one filters another region, its report, and that report names the filter region in its filterRegionId property, as covered in the guide to controlling regions with apex.region.

Reading the Region's Configuration

The region's settings are available as properties of its interface. Most come from Page Designer attributes and are effectively read-only; a few can be changed with region.call("option", ...) followed by refreshView.

const search = apex.region("product_search");
console.log("uiMode:", search.uiMode, "- batch:", search.batch, "- feedback:", search.feedback);
console.log("searchItem:", search.searchItem, "- currentFacets:", search.currentFacets);
for (const control of search.controls) {
    console.log(control.name.padEnd(15), control.type.padEnd(10), control.label.padEnd(10),
                (control.values || []).length, "values");
}

Output:

uiMode: F - batch: false - feedback: true
searchItem: P4_SEARCH - currentFacets: #active_facets
P4_CATEGORY_ID  list       Category   24 values
P4_SUPPLIER_ID  list       Supplier   12 values
P4_PRICE        rangeList  Price      4 values
P4_COLOR        list       Color      8 values
P4_IS_ACTIVE    list       Is Active  2 values
PropertyTypeDescription
uiModestring"F" for faceted search, "S" for smart filters. Cannot be changed.
controlsobject[]The facets. Each has name (the page item), type (list, range, rangeList, selectList, group, checkbox, or input), label, displayAs (INLINE or FILTER_DIALOG), multiple, values (the LOV as objects with r and d), and type-specific options. List controls also have allowExclude, new in 26.1, which lets users exclude values.
batchbooleanHold changes until the user clicks Apply in faceted search, or closes the filter in smart filters.
externalApplybooleanWith batch on, hide the Apply button so your code calls apply instead.
feedbackbooleanShow counts next to facet values.
searchFieldstring or booleanWhether to show a search field, or the ID of an item to use as one.
searchItemstringThe item that holds the search terms.
searchButtonstringThe button that starts a search, when the search field is an item.
multipleSearchesbooleanAllow several search terms, combined with AND.
currentFacetsstring or booleanWhere to list the applied filters: a selector, true for the top of the region, or false.
showTotalCountstring or booleanWhere to show the total count.
showChartsstring or booleanWhether facet charts are allowed: true for a dialog, a selector for a specific element, or false.
chartTopNValuesnumberHow many values a chart shows before grouping the rest as Others.
numberFormatstring or booleanHow counts are formatted, together with numberFormatOptions and numberFormatThreshold.
persistStatebooleanRemember collapsed facets and open charts for the browser session.
clearOnHidebooleanClear a facet's value when the user hides it, in faceted search.
collapsibleSearchBarboolean or nullSmart filters only: collapse the search bar, or null to collapse it on small screens only.
maxChips, moreFiltersChipnumber, booleanSmart filters only: how many suggestion chips to show, and whether to add a More Filters chip.
textobjectThe region's translatable text strings.

How to Set Facet Values from JavaScript

Every facet is a page item, so you set it with apex.item(name).setValue. The region notices the change, fires its change event, and refreshes the report. There is no separate facets method for this. What trips people up is the value format, which depends on the facet type:

FacetValue formatExample
List with multiple selectionI for include, then the values separated by colons. With Allow Exclude, E excludes them instead."I9:11"
List with a single value, or select listThe value as it is."TRUE"
Range or range listLower and upper bound separated by a bar. Leave one side empty for an open range."50|150", "|50", "300|"
Search itemThe search term."merino"
const search = apex.region("product_search");
const products = apex.region("products");
const done = () => new Promise((resolve) => setTimeout(resolve, 2000));   // report and counts
search.on("facetschange", () => console.log("facetschange"));

apex.item("P4_CATEGORY_ID").setValue("I9:11");            // Camp Kitchen and Backpacks
await done();
console.log("category:", apex.item("P4_CATEGORY_ID").getValue(), "->", search.getTotalResourceCount(), "products");
apex.item("P4_PRICE").setValue("50|150");                 // from 50 to 150
await done();
console.log("and price 50-150 ->", search.getTotalResourceCount(), "products");

Output:

facetschange
category: I9:11 -> 15 products
facetschange
and price 50-150 -> 8 products

The include prefix is not optional. In 26.1, a multiple-selection value without it, such as setValue("11"), is accepted in the browser but rejected by the server with ORA-20987: Filter prefix "1" is invalid, and the report does not refresh. The server reads the first character as the prefix, which is why the error mentions "1".

The example waits after each change because the new total only arrives after the report has refreshed. The same technique is behind a common request: opening a page with a filter already applied. Set the facet item's value in a dynamic action on page load, using these formats. The item methods themselves are covered in the guide to getting and setting page items with apex.item.

The change Event

Fires when one or more facet values change, whether the user or setValue changed them. With batch on, it fires when the changes are applied. Its full name is facetschange, and it carries no data. The dynamic action events Facets Change [Faceted Search] and Filters Change [Smart Filters] are this same event. The example above listens to it, which is why facetschange appears before each result.

Reading Counts

getFacetCount

Returns how many facets currently have a value. It is handy for a badge on a button that opens hidden facets.

region.getFacetCount(pIncludeSearchTerms) → number
ParameterTypeDescription
pIncludeSearchTermsbooleanCount search terms as well as facets.
const search = apex.region("product_search");
const done = () => new Promise((resolve) => setTimeout(resolve, 2000));   // report and counts
apex.item("P4_COLOR").setValue("IBlack");
await done();
apex.item("P4_SEARCH").setValue("merino");
await done();
console.log("facets:", search.getFacetCount(false), "- with search terms:", search.getFacetCount(true));

Output:

facets: 1 - with search terms: 2

getTotalResourceCount and getFacetValueCounts

getTotalResourceCount returns the number of records matching the current filters, the same total the region displays but unformatted, or null when feedback is off. getFacetValueCounts returns every count the facets show, grouped by facet and value.

region.getTotalResourceCount() → number
region.getFacetValueCounts() → object
const search = apex.region("product_search");
const counts = search.getFacetValueCounts();
console.log("total:", search.getTotalResourceCount());
console.log("P4_PRICE:", counts.P4_PRICE);
console.log("P4_IS_ACTIVE:", counts.P4_IS_ACTIVE);
console.log("P4_COLOR:", counts.P4_COLOR);

Output:

total: 96
P4_PRICE: {"|50":23, "300|":10, "50|150":36, "150|300":27}
P4_IS_ACTIVE: {"FALSE":3, "TRUE":93}
P4_COLOR: {
  "Red": 9,
  "Slate Grey": 11,
  "Forest Green": 12,
  "Burnt Orange": 10,
  "Black": 15,
  "Sand": 11,
  "Ocean Blue": 14,
  "Olive": 8
}

The keys use the same formats as setValue: range buckets like "50|150" and plain values like "TRUE". That makes it easy to feed a count straight into a filter, for example to preselect the most common color.

Clearing and Resetting Filters

clear, clearFacets, and reset

clear removes every facet value and search term. clearFacets removes the facet values but keeps the search terms. reset does what clear does and also restores the remembered settings, such as collapsed facets and open charts, to the region's configuration.

region.clear()
region.clearFacets()
region.reset()
const search = apex.region("product_search");
const done = () => new Promise((resolve) => setTimeout(resolve, 2000));   // report and counts
const show = (label) => console.log(label.padEnd(12), search.getFacetCount(true), "filters,",
                                    search.getTotalResourceCount(), "products");
apex.item("P4_CATEGORY_ID").setValue("I11");            // Backpacks
await done();
apex.item("P4_SEARCH").setValue("daypack");
await done();
show("filtered:");
search.clearFacets();
await done();
show("clearFacets:");
search.clear();
await done();
show("clear:");

Output:

filtered:    2 filters, 2 products
clearFacets: 1 filters, 2 products
clear:       0 filters, 96 products

After clearFacets, the filter count drops from 2 to 1 but the product count stays at 2, because the search term "daypack" alone still narrows the results to the same two products. clear removes that too, and all 96 products come back.

const search = apex.region("product_search");
const done = () => new Promise((resolve) => setTimeout(resolve, 2000));   // report and counts
apex.item("P4_IS_ACTIVE").setValue("TRUE");
await done();
console.log("filtered:", search.getTotalResourceCount());
search.reset();
await done();
console.log("reset:   ", search.getTotalResourceCount(), "- filters:", search.getFacetCount(true));

Output:

filtered: 93
reset:    96 - filters: 0

A Reset Filters button of your own needs nothing more than a dynamic action that calls the region's reset method.

Changing What the User Sees

hideFacet and showFacet

Hide and show a facet. Hiding does not clear the facet's value, so the report stays filtered by it even though the user can no longer see why. They work for inline facets; in smart filters, a hidden facet is simply not suggested.

region.hideFacet(pFacetName)
region.showFacet(pFacetName)
const search = apex.region("product_search");
const visible = () => search.widget().find(".a-FS-label:visible").map((i, e) => e.textContent).get().join(", ");
search.hideFacet("P4_SUPPLIER_ID");
search.hideFacet("P4_IS_ACTIVE");
console.log("visible:", visible());
search.showFacet("P4_IS_ACTIVE");
console.log("visible:", visible());

Output:

visible: Category, Price, Color
visible: Category, Price, Color, Is Active

Because a hidden facet keeps filtering, clear its item's value first when you hide it for good, unless the hidden filter is exactly what you want, such as a fixed status the user should not change.

addChart and removeChart

Open and close a chart of a facet's counts, the same chart the user opens with a facet's chart button. By default the chart goes where the showCharts setting says, which on this page is a dialog. Pass an element as the second argument to put it somewhere of your own.

region.addChart(pFacetName, [pAppendTo$], [pConfig])
region.removeChart(pFacetName)
ParameterTypeDescription
pFacetNamestringThe facet to chart.
pAppendTo$jQueryThe element to put the chart in.
pConfigobjecttype, either "bar" or "pie", and topN, the number of values to show.
const search = apex.region("product_search");
search.on("facetsbeforeaddchart facetsafterremovechart", (event, data) =>
    console.log(event.type, data.chartName, "- charts:", data.count));
search.addChart("P4_COLOR", null, { type: "bar", topN: 5 });
await new Promise((resolve) => setTimeout(resolve, 1500));

Output:

facetsbeforeaddchart P4_COLOR - charts: 1
A bar chart of product colors opened from an Oracle APEX faceted search
The Color facet as a bar chart: the top five colors, with the remaining values grouped as Others.

With topN set to 5, the chart shows the five most common colors and rolls the other three, 27 products in total, into Others.

const search = apex.region("product_search");
const pause = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
search.on("facetsbeforeaddchart facetsafterremovechart", (event, data) =>
    console.log(event.type, data.chartName, "- charts:", data.count));
search.addChart("P4_COLOR");
await pause(1500);
search.removeChart("P4_COLOR");
await pause(500);

Output:

facetsbeforeaddchart P4_COLOR - charts: 1
facetsafterremovechart P4_COLOR - charts: 0

The beforeAddChart and afterRemoveChart Events

Fire just before a chart is added and just after one is removed. Their full names are facetsbeforeaddchart and facetsafterremovechart, and in dynamic actions they appear as Before Add Chart and After Remove Chart [Faceted Search]. The two examples above listen to both.

Property of dataTypeDescription
countnumberThe number of charts after the change.
chartNamestringThe chart's name, which is the facet item.
chartElementbeforeAddChart only: the new chart's element.

Controlling Updates

lock and unlock

Disable the facets, and enable them again. The report calls these itself while it refreshes, so a second request cannot start before the first finishes. Locks are counted: every lock needs its own unlock before the facets become usable again.

region.lock()
region.unlock()
const search = apex.region("product_search");
const locked = () => search.widget().hasClass("is-disabled");
search.lock();
console.log("locked:  ", locked());
search.lock();                                            // locks are counted
search.unlock();
console.log("unlock 1:", locked());
search.unlock();
console.log("unlock 2:", locked());

Output:

locked:   true
unlock 1: true
unlock 2: false

Call lock around your own slow work that the facets should not interrupt, and always pair it with unlock, ideally in a finally block, or the user is left with facets that never come back.

fetchCounts and refresh

Fetch every facet's counts from the server again. You need this when the report is also filtered by page items outside the facets region and one of them changes, because the facets do not know about it on their own. refresh does the same thing. Neither returns anything.

region.fetchCounts()
region.refresh()
const search = apex.region("product_search");
let requests = 0;
apex.jQuery(document).on("ajaxSend", () => requests++);   // count the calls to the server
console.log("fetchCounts returned:", search.fetchCounts());
await new Promise((resolve) => setTimeout(resolve, 1500));
console.log("server requests:", requests, "- total:", search.getTotalResourceCount());

Output:

fetchCounts returned: undefined
server requests: 1 - total: 96

apply

Applies the changes the user made while batch and externalApply are on. That combination suits facets in a dialog or drawer with an Apply button of your own.

region.apply()
const search = apex.region("product_search");
const pause = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
search.call("option", { batch: true, externalApply: true });
search.refreshView();
const box = search.widget().find(".a-FS-control").first().find("input[value='11']");
box.prop("checked", true).trigger("change");              // as if the user checked Backpacks
await pause(3000);
console.log("before apply:", apex.item("P4_CATEGORY_ID").getValue(), "-", search.getTotalResourceCount(), "products");
search.apply();
await pause(3000);
console.log("after apply: ", apex.item("P4_CATEGORY_ID").getValue(), "-", search.getTotalResourceCount(), "products");

Output:

before apply: I11 - 96 products
after apply:  I11 - 7 products

Until apply runs, the item already holds the new value, I11, but the report and total are unchanged at 96. After apply, the report filters down to 7 products.

refreshView

Redraws the facets from the region's options. Call it after changing an option such as searchButton, or a control's label.

region.refreshView()
const search = apex.region("product_search");
const labels = () => search.widget().find(".a-FS-label").map((i, e) => e.textContent.trim()).get().join(" | ");
console.log(labels());
search.controls[0].label = "Product Category";
search.refreshView();                                     // render the facets again
console.log(labels());

Output:

Category | Supplier | Price | Color | Is Active
Product Category | Supplier | Price | Color | Is Active

focus

Moves focus to the search field, or to the first facet when there is no search field. In smart filters it focuses the search bar.

region.focus()
apex.region("product_search").focus();
console.log("focus is in:", document.activeElement.id, `(${document.activeElement.placeholder})`);

Output:

focus is in: product_search_fr_search (Search...)

Conclusion

Faceted search and smart filters share the facetsRegion interface, told apart by uiMode. Facet values are page items, so you set them with apex.item().setValue, using I-prefixed, colon-separated values for multi-select lists, bar-separated bounds for ranges, and plain values elsewhere; leaving out the I prefix fails on the server. facetschange tells you when filters change. getFacetCount, getTotalResourceCount, and getFacetValueCounts read the numbers. clear, clearFacets, and reset remove filters at different depths. hideFacet keeps a facet filtering while out of sight, addChart and removeChart manage count charts, lock and unlock are counted, fetchCounts picks up outside filters, apply commits batched changes, and refreshView redraws after you change an option. For selection and paging in the report that the facets filter, see the guide to selecting rows and paging through reports and cards.

Vinish Kapoor
Vinish Kapoor

An Oracle ACE and software veteran with 25+ years of experience, passionate about AI and IT innovation.

guest

0 Comments
Oldest
Newest Most Voted
00