Skip to main content

Filters

foundation-filters provides a collection of client-side filters.

These client-side filters enable the client app to filter and process data locally on behalf of the user.

In general, client-side filters take primitive input parameters, and return true or false based on these values. You can combine them using the ClientFilterRunner utility provided in the foundation-filters package to create layered filtering conditions for complex data scenarios.

If a filter is used without all the input parameters needed, Genesis logs a warning and returns true by convention so that the chain can continue.

For detailed information on the API and configuration, see the API documentation.

Example

Here is an example of the timeWindow filter. In this case, the start and date are created dynamically; they show the day before the current day and the day after by default. You can click on the Check button to see the message returned. And you can change the dates to see a different message when today's date is not in the range.

Start PeriodEnd PeriodCheck

Each filter is provided in two flavours:

  1. Functions
  2. Dependency injection (DI)

Both flavours are shown in the example below. This also uses timeWindow, but in this case, the start and end for the period are both set to a specific date and time.

import { TimeWindow } from '@genesislcap/foundation-filters';

@customElement({
name: 'my-element',
template: html`
<div class="container">
<rapid-text-field type="datetime-local" value=${sync((x) => x.startDate)}>
Start Period
</rapid-text-field>
<rapid-text-field type="datetime-local" value=${sync((x) => x.endDate)}>
End Period
</rapid-text-field>
<rapid-button @click=${(x) => x.checkWindow()}>Check</rapid-button>
<span>${(x) => x.resultString}</span>
</div>
`,
})
export class MyElement extends GenesisElement {
@observable startDate: string = "2024-11-21T09:00";
@observable endDate: string = "2024-11-21T18:00";
@observable resultString: string;
@TimeWindow timeWindow: TimeWindow;
checkWindow() {
this.resultString = this.timeWindow.filter({start: this.startDate, end: this.endDate})
? 'Exists within the start and end period'
: 'Doesnot fall between start and end period';
}
}

Function or dependency injection?

In the example above, the function version is pretty self-explanatory, so why you would use the DI injected version?

Functions work well if you know exactly what data or states are needed to achieve the outcome. However, at the point of calling a filter, you might not necessarily know what data or states are required.

The DI versions enable you to handle this complexity, which aids later refactoring, and leaves callers free to pass only primitive input parameters where possible.

Available filters

Here is a list of available filters:

  • nodeEnv: This filter is used to match the current environment (production, development etc.) with the environment specified ( which is passed as function param). It is useful for supporting environment-specific conditions so that certain features are only available for targeted environment.
  • percentage: The percentage filter checks if a randomly generated number between 0 and 100 is within a specified percentage threshold. This enables you to set probabilistic conditions, so that features or elements can be displayed to a proportion of users based on the given percentage. This filter is useful for scenarios such as A/B testing or gradually rolling out new features.
  • timeWindow: This filter checks whether the current date and time come fall within the start and end period specified in the parameters. The filter returns true if the current date and time falls between the range, thus making it useful for time-sensitive conditions.
  • urlTargeting: This filters content by URL parts, such as scheme, path, host, or pattern matching. It enables you to target specific URLs, which can be useful for enabling or restricting features.
  • userAgent: This filters content by the user's browser, OS, devices or specified user agent string. For example, you can customize the experience according to specific environments, such as targeting mobile devices or audience, or you can restrict access from a specific browser.
  • userTargeting: This filters content on basis of attributes such as usernames, permissions and profiles. It enables you to show personalized content or to restrict access to specific content depending on user conditions.

Using dependency injection

Dependency injection (DI) is essential if you want to set up filters with dynamic configurations. This method also simplifies reusability across components. The example below uses the UserTargeting filter without the need to provide the current user or other data points directly to the underlying function. If you later change the source of the the current user, you can refactor centrally.

import { UserTargeting } from '@genesislcap/foundation-filters';
...
@UserTargeting userTargeting: UserTargeting;
...
const outcome = this.userTargeting.filter({
profiles: ['ADMIN']
});

All DI-based filters implement the ClientFilter interface. However, you can provide other APIs for convenience to abstract underlying implementation details. For example, UserTargeting provides a hasAdminProfile API, so you could rewrite the previous example as:

import { UserTargeting } from '@genesislcap/foundation-filters';
...
@UserTargeting userTargeting: UserTargeting;
...
const outcome = this.userTargeting.hasAdminProfile();

This is quite a simplistic example, but it highlights that common parameters can be predefined in the filters themselves where needed.

Also note that DI-based filters are registered as transient, so you will get a new instance for every dependency request or container.get(). The ClientFilterRunner used to run chained filters is also transient.

Available filters

This section looks at each filter in detail and provides examples.

nodeEnv

The nodeEnv filter checks the NODE_ENV environment variable to obtain the current application environment. If this is unset, then it is classed as development.

The filter returns true if the filter’s specified environment matches the current one. Use this, for example, to ensure that specific features or configurations are available only to the intended environments.

Here is an example:

import { NodeEnv } from '@genesislcap/foundation-filters';
...
@NodeEnv nodeEnv: NodeEnv;
...
const outcome = this.nodeEnv.filter({envs:[ 'development']});

The nodeEnv also provides a isDevelopment variable that can be used directly so the above example can be rewritten as:

import { NodeEnv } from '@genesislcap/foundation-filters';
...
@NodeEnv nodeEnv: NodeEnv;
...
const outcome = this.nodeEnv.isDevelopment;

You can find the other available API for nodeEnv in the API Docs.

percentage

The percentage filter checks if a randomly generated number between 0 and 100 is within a specified percentage threshold. This enables you to set probabilistic conditions, so that specified features or elements can be displayed to a proportion of users based on the given percentage.

For example, if you set the filter to 20%, then each time it runs, it has a 20% chance of passing. Consequently only about 20% of users will see the feature or content. This is useful for controlled feature rollouts or A/B testing without requiring complex set-ups.

import { Percentage } from '@genesislcap/foundation-filters';
...
@Percentage percentageFilter: Percentage;
...
const probability = 0.5; // define percentage between 0 & 1
const randomNumber = Math.random(); // provide a random number optionally
const outcome = this.percentageFilter.filter({percent: probability, random: randomNumber});
console.log(outcome); // true or false based on probability

timeWindow

This filter checks whether the current date and time fall within the specified period. The filter returns either true or false.

There are two parameters to enable you to set the period: start and end. You can provide these in any format that is recognized by the Date.parse() method.

Here is an example:

import { TimeWindow } from '@genesislcap/foundation-filters';
...
@TimeWindow timeWindow: TimeWindow;
...
const outcome = this.timeWindow.filter({
start: 'Sun Sep 25 2022 16:51:55 GMT+0000',
end: 'Fri Nov 25 2022 17:51:55 GMT+0000',
});
console.log(outcome); // returns true if current date/time falls within the start and end period

urlTargeting

urlTargeting filters content on the basis of URL structure. You can use specific parameters to target the required URLs or patterns. The parameters for this filter are:

ParameterDescription
urlA full URL.
patternA regex for matching URLs.
schemesAn array of schemes or protocols to target (e.g. "http").
hostsAn array of host names or domain names.
pathsA list of paths to target.

This filter returns true when the current URL matches ANY of the parameters explained. If you wish to match on multiple parameters, consider chaining an array of urlTargeting filters using ClientFilterRunner.

You can target specific URLs using these parameters. This can be extremely useful for restricting or enabling features based on URL parameters or domains. For example, you could use the filter to only allow access to certain content when the URL matches a secure HTTPS connection on specific hosts, such as https://test.genesislab.global.

Here is an example:

import { URLTargeting } from '@genesislcap/foundation-filters';
...
@URLTargeting urlTargeting: URLTargeting;
...
const outcome = this.urlTargeting.filter({
url: 'https://www.test.com',
schemes: ['http', 'https'],
hosts: ['test.com']
});
console.log(outcome); // returns true if any one of the above parameters matches with the passed url

userAgent

userAgent filters content on the basis of user’s browser, device, operating system (OS), or user agent.

You can use this to specify to control access to certain features by device or operating system (for example). So you can providing tailored experience based on these criteria.

The parameters for this filter are:

ParameterTypeDescription
browsersString arrayOne or more browser names, for example: 'Chrome', 'Firefox'.
devicesString arrayOne or more device names, for example: 'Mobile', 'Tablet'.
ossString arrayOne or more names of operating systems, for example: 'IOS', 'Android'
uaStringA user-agent string, for example: navigator.userAgent.

Here is an example:

import { UserAgent } from '@genesislcap/foundation-filters';
...
@UserAgent userAgent: UserAgent;
...
const outcome = this.userAgent.filter({
browsers: ['Firefox'],
devices: ['mobile', 'tablet'],
oss: ['Mac OS', 'Android', 'Window'],
ua: navigator.userAgent
});
console.log(outcome); // returns true if any one of the above parameters matches with the passed userAgent string

userTargeting

This filter is used to filter content on the basis of user-specific attributes. This filter is extremely helpful for:

  • restricting features to specific user profiles or permissions
  • accessing personalized content.
  • managing access control on basis of profile, role, or permissions.

The following parameters can be used as the criteria for filtering:

ParameterDescription
ProfilesFilters on the basis of assigned user profiles.
PermissionsApplies filtering on the basis of assigned permissions on user profile.

The filter returns true when the current user matches EITHER of the parameters. If you wish to match on multiple parameters, consider chaining an array of userTargeting filters using ClientFilterRunner.

Here is an example:

import { UserTargeting } from '@genesislcap/foundation-filters';
...
@UserTargeting userTargeting: UserTargeting;
...
const outcome = this.userTargeting.filter({
profiles: ['ADMIN']
});

Targeting admin users

The userTargeting filter provides the hasAdminProfile utility so that you can check if the user is an admin. Here is an example of this in use:

import { UserTargeting } from '@genesislcap/foundation-filters';
...
@UserTargeting userTargeting: UserTargeting;
...
const outcome = this.userTargeting.hasAdminProfile();

Combining filters with the runner method

The clientFilterRunner method enables you to run a combination of filters sequentially. If any filter fails (returns false), then the entire result is false. This enables you to manage complex filtering logic in one function.

Example

import { ClientFilterRunner } from '@genesislcap/foundation-filters';
...
@ClientFilterRunner runner: ClientFilterRunner;

const outcome = this.runner.run([
{
name: 'userAgent',
parameters: {
browsers: ['Chrome'],
},
},
{
name: 'timeWindow',
parameters: {
start: '2024-11-21',
end: '2024-12-31',
},
},
]);
console.log(outcome); // true if all the filters pass

Installation

To enable this module in your application, follow the steps below.

  1. Add @genesislcap/foundation-filters as a dependency in your package.json file.
{
"dependencies": {
"@genesislcap/foundation-filters": "latest"
},
}
  1. Run the $ npm run bootstrap command. (Whenever you change the dependencies of your project, you should always run this command to rebuild.) You can find more information in the package.json basics page.