Applying User Filters
To apply a user filter to the specified view at runtime. The method validates the value against the filter's data type, applies it, refreshes the view, and returns a Promise that resolves on success or rejects with a descriptive error.
Returns: a Promise.
- Resolves once the filter is applied and the view has refreshed.
- Rejects with an error object describing the validation failure. The message is available as
err.errorDesc(falling back toerr.message). See the User Filter Change Event & Error Handling page for the full list of error messages.
For more detailed information, check the following help documentation regarding the User filter configuration and User filter error handling.
Function Call
ObjName.applyUserFilter(columnLabel, values, dataType, exclude)Parameters
| Parameter Name | Description |
|---|---|
| columnLabel* | String The column label / name of the user filter as configured in the view. |
| values* | String / Number / Array / Object The filter value(s) to apply. Pass the keyword
|
| dataType | String Explicitly sets the filter data type, bypassing auto-detection. Use this when the value could be ambiguous or when you want to force a specific interpretation. One of the 33 supported data types (e.g. |
| exclude | Boolean / String When value is Not supported for single-select filters. |
Accepted Value Formats
The values argument adapts to the filter's data type. All formats below can be combined with the exclude flag (except single-select).
| Filter Type | Accepted values | Example |
|---|---|---|
| Numeric – single | Number or numeric string | applyUserFilter('Sales', '100') |
| Numeric – multi | CSV string or Array |
|
| Numeric – slider range | 'from:to' string or { from, to } object |
|
| Categorical (text) – single | String | applyUserFilter('Region', 'East') |
| Categorical (text) – multi | CSV string or Array (spaces around commas are trimmed) |
|
| Date Range |
Either bound may be omitted for an open-ended range | applyUserFilter('OrderDate', { from: '01 Jan 2025', to: '31 Mar 2025' }) |
| Date Relative | Relative phrase |
|
| Date Actual – single | String Year / Quarter-year / Month-year / Week-year / full date / datetime Also accepts relative phrases |
|
| Date Actual – multi | Array or CSV May mix absolute + relative values |
|
| Date Seasonal | Single, CSV, or Array Quarter ( |
|
| Duration | Formatted string, raw seconds, or a range as { from, to } / 'from to to' |
|
| Timeline | Range in year / quarter / month / week / day / datetime form, a relative phrase, or { from, to } |
|
| Any type – reset | 'RESET' | applyUserFilter('Region', 'RESET') |
Notes
- Exclude mode:
applyUserFilter('Region', 'Delhi,Goa', 'CATEGORICAL_MULTI_SELECT', true)shows everything except Delhi and Goa. - No-op values:
null,undefined, and''leave the filter unchanged and do not raise an error. - Whitespace around values (e.g.
' 100 ') is trimmed before applying. - Sequential / concurrent calls are supported; each applied filter stacks on the view.
Sample Response
The live preview embeds the report with two user filters, each wired to a custom control on the page:
- Region : single‑select. Applies one value, e.g.
applyUserFilter('Region', 'East'). - Date : multi‑select quarters. Applies an array, e.g.
applyUserFilter('Date', ['Q1', 'Q3'], 'DATE_SEASONAL_QUARTERS').
This sample is built as a complete, end‑to‑end example and brings every customization together in one place:
It combines every customization in one example:
- Config - default values are pre-applied at load and the filter tab is hidden (
userfilterHolderState: 'hidden'), so the page controls drive all filtering. - Event - the
USER_FILTER_CHANGElistener updates the "Last filter event" line on each apply. - Errors - invalid input rejects the Promise and is shown via
.catch(); the Apply invalid value button demonstrates this.
For more details, please refer to User Filter Configurations and Validations
How this sample behaves – the custom controls call applyUserFilter to filter the embedded report:
| Control | It calls | Result |
|---|---|---|
| Region | applyUserFilter('Region', value) | Works – Region is a user filter on the report. |
| Date (quarters) | applyUserFilter('Date', [...]) | Fails – report has no user filter named "Date", so it returns No User Filter found with the name Date. |
| Apply invalid value | applyUserFilter('Date', 'Q5') | Same "not found" error – the label is checked before the value, so it never reaches the Q5 quarter check. |
The live preview below demonstrates this in action.
Sample Code:
Copied<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ZAnalytics JS API - applyUserFilter (full example)</title>
<link rel="icon" type="image/x-icon" href="https://analytics.zoho.com/favicon.ico">
<script src="https://downloads.zohocdn.com/zanalyticslib/jsapi/v1.2.0/zanalytics.min.js"></script>
<style>
:root {
--filter-bg: #ffffff;
--filter-border: #d1d5db;
--filter-border-focus: #2563eb;
--filter-shadow: 0 1px 2px rgba(0,0,0,0.05);
--filter-shadow-focus: 0 0 0 3px rgba(37, 99, 235, 0.15);
--filter-radius: 8px;
--filter-height: 40px;
--filter-text: #1f2937;
--filter-muted: #6b7280;
--filter-hover: #f3f4f6;
--filter-arrow: #6b7280;
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
* { box-sizing: border-box; }
body { margin: 0; font-family: var(--font-sans); font-size: 14px; color: var(--filter-text); }
.filter-bar {
background: var(--filter-bg); border: 1px solid var(--filter-border); border-radius: 12px;
padding: 20px 24px; margin-bottom: 16px; box-shadow: 0 1px 3px rgba(0,0,0,0.06);
}
.filter-bar-inner { display: flex; flex-wrap: wrap; align-items: center; gap: 20px 28px; }
.filter-group { display: flex; align-items: center; gap: 8px; }
.filter-label { font-weight: 600; color: var(--filter-muted); white-space: nowrap; font-size: 13px; }
/* Custom select (single + multi share the shell) */
.custom-select-wrap { position: relative; width: 180px; min-height: var(--filter-height); }
.custom-select-trigger {
width: 100%; height: var(--filter-height); padding: 0 36px 0 14px; background: var(--filter-bg);
border: 1px solid var(--filter-border); border-radius: var(--filter-radius); cursor: pointer;
display: flex; align-items: center; font-size: 14px; color: var(--filter-text);
box-shadow: var(--filter-shadow); transition: border-color 0.15s, box-shadow 0.15s;
overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
.custom-select-trigger .text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.custom-select-trigger:hover { border-color: #9ca3af; background: var(--filter-hover); }
.custom-select-trigger:focus { outline: none; border-color: var(--filter-border-focus); box-shadow: var(--filter-shadow-focus); }
.custom-select-trigger .arrow {
position: absolute; right: 12px; top: 50%; transform: translateY(-50%); width: 0; height: 0;
border-left: 5px solid transparent; border-right: 5px solid transparent;
border-top: 6px solid var(--filter-arrow); pointer-events: none; transition: transform 0.2s;
}
.custom-select-wrap.open .custom-select-trigger .arrow { transform: translateY(-50%) rotate(180deg); }
.custom-select-dropdown {
position: absolute; left: 0; right: 0; top: calc(100% + 4px); background: var(--filter-bg);
border: 1px solid var(--filter-border); border-radius: var(--filter-radius);
box-shadow: 0 4px 12px rgba(0,0,0,0.1); z-index: 100; max-height: 240px; overflow-y: auto; display: none;
}
.custom-select-wrap.open .custom-select-dropdown { display: block; }
.custom-select-option { padding: 10px 14px; cursor: pointer; transition: background 0.1s; display: flex; align-items: center; gap: 8px; }
.custom-select-option:hover { background: var(--filter-hover); }
.custom-select-option.selected { background: #eff6ff; color: var(--filter-border-focus); font-weight: 500; }
.custom-select-option input { pointer-events: none; }
/* Demo button + status */
.demo-btn {
height: var(--filter-height); padding: 0 16px; border: 1px solid var(--filter-border);
border-radius: var(--filter-radius); background: #fff7ed; color: #9a3412; font-weight: 600;
cursor: pointer; font-size: 13px;
}
.demo-btn:hover { background: #ffedd5; }
.status-bar {
display: flex; flex-wrap: wrap; gap: 8px 24px; padding: 12px 16px; margin-bottom: 16px;
background: #f8fafc; border: 1px dashed var(--filter-border); border-radius: 10px; font-size: 13px;
}
.status-bar .k { color: var(--filter-muted); font-weight: 600; margin-right: 6px; }
#errorLog { color: #b91c1c; }
.report-container { border-radius: 12px; overflow: hidden; background: var(--filter-bg); }
#reportView { border: none !important; border-radius: 12px; overflow: hidden; }
#reportView iframe { border: none !important; border-radius: 12px; display: block; }
.page-wrap {
max-width: 1400px; margin: 0 auto; padding: 24px 20px; min-height: 100%;
background: linear-gradient(160deg, #f8fafc 0%, #f1f5f9 100%);
}
</style>
</head>
<body>
<div class="page-wrap">
<div class="filter-bar">
<div class="filter-bar-inner">
<!-- Region: single select -->
<div class="filter-group">
<span class="filter-label">Region</span>
<div class="custom-select-wrap" id="regionWrap">
<div class="custom-select-trigger" id="regionTrigger" tabindex="0">
<span class="text">Select</span><span class="arrow"></span>
</div>
<div class="custom-select-dropdown" id="regionDropdown">
<div class="custom-select-option" data-value="East">East</div>
<div class="custom-select-option" data-value="West">West</div>
<div class="custom-select-option" data-value="Central">Central</div>
</div>
</div>
</div>
<!-- Date: multi select quarters -->
<div class="filter-group">
<span class="filter-label">Date (quarters)</span>
<div class="custom-select-wrap" id="dateWrap">
<div class="custom-select-trigger" id="dateTrigger" tabindex="0">
<span class="text">Select</span><span class="arrow"></span>
</div>
<div class="custom-select-dropdown" id="dateDropdown">
<div class="custom-select-option" data-value="Q1"><input type="checkbox">Q1</div>
<div class="custom-select-option" data-value="Q2"><input type="checkbox">Q2</div>
<div class="custom-select-option" data-value="Q3"><input type="checkbox">Q3</div>
<div class="custom-select-option" data-value="Q4"><input type="checkbox">Q4</div>
</div>
</div>
</div>
<!-- Error demo -->
<button class="demo-btn" id="invalidBtn" type="button">Apply invalid value</button>
</div>
</div>
<div class="status-bar">
<div><span class="k">Last filter event:</span><span id="eventLog">none yet</span></div>
<div id="errorLog"></div>
</div>
<div class="report-container"><div id="reportView"></div></div>
</div>
<script>
(function () {
var reportDiv = document.getElementById("reportView");
var reportUrl = "https://analytics.zoho.in/open-view/476765000002267067";
var vizObj = null;
// ================= Error handling =================
function getFilterError(err) {
if (!err) return 'Unable to apply User Filter';
return err.errorDesc || err.errorMsg || err.message
|| (err.response && (err.response.errorDesc || err.response.errorMsg))
|| 'Unable to apply User Filter';
}
function showError(msg) {
var el = document.getElementById('errorLog');
if (el) el.textContent = msg ? ('Error: ' + msg) : '';
}
// Central apply helper - every filter goes through here.
function applyFilter(columnLabel, value, dataType, exclude) {
if (!vizObj) return;
showError('');
var p = vizObj.applyUserFilter(columnLabel, value, dataType, exclude);
if (p && typeof p.then === 'function') {
p.then(function () {
console.log('Applied "' + columnLabel + '"');
}).catch(function (err) {
showError(getFilterError(err));
});
}
}
// Surface readable errors for direct console calls too.
window.addEventListener('unhandledrejection', function (event) {
showError(getFilterError(event.reason));
event.preventDefault();
});
// ================= USER_FILTER_CHANGE event =================
function onUserFilterChange(eventData) {
var filters = (eventData && eventData.appliedUserFilters) || [];
var parts = filters.map(function (f) {
var name = f.columnName || f.name || '';
var val = f.displayCriteria != null && f.displayCriteria !== '' ? f.displayCriteria : '(reset)';
return name + ': ' + val + (f.exclude ? ' [exclude]' : '');
});
var el = document.getElementById('eventLog');
if (el) el.textContent = parts.length ? parts.join(' | ') : 'none';
}
// ================= Custom single-select =================
function setupSingleSelect(wrapId, triggerId, dropdownId, onSelect) {
var wrap = document.getElementById(wrapId);
var trigger = document.getElementById(triggerId);
var dropdown = document.getElementById(dropdownId);
trigger.addEventListener('click', function () { wrap.classList.toggle('open'); });
trigger.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); trigger.click(); }
});
dropdown.querySelectorAll('.custom-select-option').forEach(function (opt) {
opt.addEventListener('click', function () {
var val = this.getAttribute('data-value');
trigger.querySelector('.text').textContent = val;
dropdown.querySelectorAll('.custom-select-option').forEach(function (o) { o.classList.remove('selected'); });
this.classList.add('selected');
wrap.classList.remove('open');
onSelect(val);
});
});
document.addEventListener('click', function (e) { if (!wrap.contains(e.target)) wrap.classList.remove('open'); });
}
// ================= Custom multi-select (quarters) =================
function setupMultiSelect(wrapId, triggerId, dropdownId, onChange) {
var wrap = document.getElementById(wrapId);
var trigger = document.getElementById(triggerId);
var dropdown = document.getElementById(dropdownId);
function selected() {
return Array.prototype.slice.call(dropdown.querySelectorAll('.custom-select-option.selected'))
.map(function (o) { return o.getAttribute('data-value'); });
}
function refresh() {
var vals = selected();
trigger.querySelector('.text').textContent = vals.length ? vals.join(', ') : 'Select';
}
trigger.addEventListener('click', function () { wrap.classList.toggle('open'); });
trigger.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); trigger.click(); }
});
dropdown.querySelectorAll('.custom-select-option').forEach(function (opt) {
opt.addEventListener('click', function (e) {
e.stopPropagation();
this.classList.toggle('selected');
var cb = this.querySelector('input');
if (cb) cb.checked = this.classList.contains('selected');
refresh();
onChange(selected());
});
});
document.addEventListener('click', function (e) { if (!wrap.contains(e.target)) wrap.classList.remove('open'); });
return { refresh: refresh };
}
// ================= Init report with full userFilterConfig =================
var reportOptions = {
width: '100%',
height: 600,
// Load-time configuration (see the "Configuring User Filters at Load Time" page)
defaultConfig: {
userFilterConfig: {
applyDefaultUserFilterValues: 'true',
defaultUserFilterValues: {
Region: 'East',
Date: ['Q1', 'Q2']
},
// Hide the built-in filter tab; the custom controls drive filtering.
userfilterHolderState: 'hidden'
}
},
// Change event (see the "User Filter Events & Errors" page)
onViewLoad: function () {
vizObj.addEventListener(ZACustomEvent.USER_FILTER_CHANGE, onUserFilterChange);
}
};
vizObj = new ZAnalyticsLib(reportDiv, reportUrl, reportOptions);
vizObj.createViz();
// ================= Wire the controls =================
// Region - single select -> single value or RESET
setupSingleSelect('regionWrap', 'regionTrigger', 'regionDropdown', function (val) {
applyFilter('Region', val || 'RESET');
});
// Date - multi select quarters -> array, with an explicit dataType
setupMultiSelect('dateWrap', 'dateTrigger', 'dateDropdown', function (vals) {
applyFilter('Date', vals.length ? vals : 'RESET', 'DATE_SEASONAL_QUARTERS');
});
// Error demo - an unsupported quarter value rejects the Promise
document.getElementById('invalidBtn').addEventListener('click', function () {
applyFilter('Date', 'Q5', 'DATE_SEASONAL_QUARTERS');
});
window.vizObj = vizObj;
})();
</script>
</body>
</html>