Table of Contents

Filter Expressions

A rule's FilterText is the condition that decides, per item, whether the match values or the no-match values are written. It is parsed by the shared TTMSFNCFilterBuilder engine — the same engine the TMS FNC Dataset uses for row filtering — so the syntax is consistent across the product. Write expressions by hand when you need operators, grouping, or runtime values that the one-line ShowWhere/AddRuleEquals helpers do not express. This guide covers the expression syntax, how field names map to item properties, forcing a field's data type, and substituting live values through {Placeholder} text sources.

Expression syntax

FilterText reads like a SQL WHERE clause:

  • Field references are wrapped in square brackets — [Status], [Order Date]. The brackets are the column delimiter, so they are required even for simple names (a bare or double-quoted name is not recognised as a field and silently matches nothing).
  • String literals use single quotes: 'Open'. LIKE accepts the % wildcard: [Name] LIKE 'A%'.
  • Operators mirror the TTMSFNCFilterBuilderExpressionOperator set: =, <>, >, <, >=, <=, and LIKE, plus empty/not-empty tests.
  • Grouping combines conditions with AND and OR and parentheses: ([Price] >= 100 AND [Stock] > 0) OR [Featured] = 1.

An empty FilterText matches every item unconditionally — useful when the rule exists only to push inactive values or to be filled in later.

Expression format

The syntax above is the default Delphi DataSet format, which mirrors a dataset filter: bracketed field names, single-quoted strings, LIKE with %/_ wildcards, and AND/OR keywords. The shared TTMSFNCFilterBuilder also understands an OData format — useful when you need to exchange filter expressions with a web API or backend that speaks OData v4. Field names are bare (no brackets), string functions replace wildcards, and the logical operators are lowercase keywords:

Condition Delphi DataSet OData
Equals (text) [Name] LIKE 'Open' Name eq 'Open'
Starts with [Name] LIKE 'A%' startswith(Name, 'A')
Ends with [Name] LIKE '%z' endswith(Name, 'z')
Contains [Name] LIKE '%A%' contains(Name, 'A')
Greater than [Price] > 100 Price gt 100
Is empty [Status] = '' Status eq null
And / Or AND / OR and / or

Write rule expressions in the Delphi DataSet format for local filtering. Use the OData format when generating or consuming filter strings for a remote API. To switch the engine to OData mode, set FormatType to fftOData on the TTMSFNCFilterBuilder instance the manager uses.

Field names and item paths

Field names in the expression are resolved as RTTI properties on each item that ItemsPath yields. ItemsPath itself is an RTTI path navigated from Source: Items[*] evaluates every element of an Items collection, Items[1..3|5] selects specific indices, and an empty path targets Source directly. The field name in FilterText must match a readable property on the resolved item.

Overriding a field's data type

The builder normally infers a field's type from its value. When a value would be read as the wrong type — a numeric code stored as text, a date that should sort chronologically — add a TTMSFNCFilterRuleFieldTypes entry to pin it. FieldTypes.Add(name, type) takes a TTMSFNCFilterBuilderDataType such as fdtNumber, fdtDate, fdtBoolean, or fdtText (the default is fdtAutomatic).

uses
  TMS.TMSFNCFilterRulesManager, TMS.TMSFNCFilterBuilder;

{ Inside a method of your form: }
var
  Rule: TTMSFNCFilterRule;
begin
  Rule := FilterRulesManager1.AddRule('Expensive', Grid1, 'Items[*]', '');

  // Field references use [brackets]; string literals use single quotes and
  // LIKE accepts the % wildcard.
  Rule.FilterText := '[Price] >= 1000 AND [Name] LIKE ''A%''';

  // The builder auto-detects types from the value, but you can force one when
  // the field text would otherwise be read as the wrong type.
  Rule.FieldTypes.Add('Price', fdtNumber);

  Rule.AddVisibilityAction(True);
  FilterRulesManager1.Apply;
end;

Placeholder text sources

To filter against a value that changes at runtime — text typed into a search box, a selected combo item — bind a placeholder through the manager's Placeholders collection (a TTMSFNCFilterRulePlaceholders). Text sources are defined once on the manager and shared across every rule. Write {PlaceholderName} anywhere in a rule's FilterText (or in ItemsPath); at each Apply, every occurrence is replaced with the current value of the bound Source.PropertyName before the expression is evaluated. This keeps the rule fixed while the value it tests against follows the UI.

Combining a placeholder with a typed field

A live search usually combines both features: a placeholder feeds the text part of the filter while a FieldTypes override keeps a numeric condition correct. Re-call Apply whenever the bound control changes:

uses
  TMS.TMSFNCFilterRulesManager, TMS.TMSFNCFilterBuilder;

{ Inside a method of your form, with an edit named SearchEdit: }
var
  Rule: TTMSFNCFilterRule;
begin
  Rule := FilterRulesManager1.AddRule('LiveSearch', Grid1, 'Items[*]', '');

  // Bind {SearchText} to SearchEdit.Text. Text sources live on the manager and
  // are shared across every rule; the token is substituted at Apply.
  FilterRulesManager1.AddPlaceholder('SearchText', SearchEdit, 'Text');

  // The placeholder appears inside the expression; a typed field keeps the
  // numeric comparison correct.
  Rule.FilterText := '[Name] LIKE ''{SearchText}%'' AND [Stock] > 0';
  Rule.FieldTypes.Add('Stock', fdtNumber);

  Rule.AddVisibilityAction(True);
  FilterRulesManager1.Apply;   // re-call Apply whenever SearchEdit changes
end;

Common pitfalls

  • Brackets for fields, single quotes for strings. [Name] = 'Open' is correct. A field reference must be bracketed: "Name" or a bare Name is not recognised as a column and the expression silently matches nothing.
  • The field must be readable on the item. A bracketed name in FilterText with no matching item property resolves to nothing and never matches.
  • Placeholders are substituted as text. {SearchText} is spliced into the expression literally, so wrap it in quotes and wildcards as needed (LIKE '{SearchText}%'). Re-call Apply for the new value to take effect.
  • Pin the type when auto-detection guesses wrong. A code like 007 read as a number loses its leading zeros — force fdtText via FieldTypes.

See also