Skip to content

Commit 76b0286

Browse files
authored
Merge pull request mendix#10881 from LEGIO-SEXTA-FERRATA/wtf/formatter-details
[WTF-2493] Document custom formatters
2 parents cb3a8e0 + ae02303 commit 76b0286

3 files changed

Lines changed: 429 additions & 0 deletions

File tree

  • content/en/docs/apidocs-mxsdk/apidocs
    • studio-pro-10/pluggable-widgets/pluggable-widgets-client-apis
    • studio-pro-11/pluggable-widgets/pluggable-widgets-client-apis
    • studio-pro-9/pluggable-widgets/pluggable-widgets-client-apis-9

content/en/docs/apidocs-mxsdk/apidocs/studio-pro-10/pluggable-widgets/pluggable-widgets-client-apis/_index.md

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,149 @@ There is a way to use more the convenient `displayValue` and `setTextValue` whi
163163

164164
The optional field `universe` is used to indicate the set of all possible values that can be passed to a `setValue` if a set is limited. Currently, `universe` is provided only when the edited value is of the Boolean or enumeration [types](/refguide/attributes/#type).
165165

166+
#### Formatter Details {#formatter-details}
167+
168+
The `formatter` field on `EditableValue` is defined as follows:
169+
170+
```ts
171+
type ParseResult<T> = { valid: true; value: T } | { valid: false };
172+
173+
interface SimpleFormatter<T> {
174+
format(value: T | undefined): string;
175+
parse(value: string): ParseResult<T>;
176+
}
177+
```
178+
179+
##### Built-in Formatter Types {#built-in-formatter-types}
180+
181+
The Mendix platform provides two typed, configurable built-in formatters that extend `SimpleFormatter<T>`: `NumberFormatter` and `DateTimeFormatter`. The actual type of `EditableValue.formatter` is `ValueFormatter<T>` — a union that covers both built-in and plain formatters:
182+
183+
```ts
184+
type ValueFormatter<T> =
185+
| (TypedFormatter<T> & (NumberFormatter | DateTimeFormatter))
186+
| (SimpleFormatter<T> & { readonly type?: never });
187+
```
188+
189+
Use the `type` property as a type guard to narrow to a specific built-in formatter before accessing its extra API:
190+
191+
```ts
192+
if (myAttribute.formatter.type === "datetime") {
193+
// DateTimeFormatter — has withConfig, getFormatPlaceholder
194+
} else if (myAttribute.formatter.type === "number") {
195+
// NumberFormatter — has withConfig
196+
} else {
197+
// Plain SimpleFormatter — string, enum, or boolean
198+
}
199+
```
200+
201+
##### DateTimeFormatter
202+
203+
**Date/DateTime** attributes receive a `DateTimeFormatter`, which extends `SimpleFormatter<Date>`:
204+
205+
```ts
206+
interface DateTimeFormatter extends SimpleFormatter<Date> {
207+
readonly type: "datetime";
208+
readonly config: DateTimeFormatterConfig;
209+
withConfig(config: DateTimeFormatterConfig): DateTimeFormatter;
210+
getFormatPlaceholder(): string | undefined;
211+
}
212+
```
213+
214+
The `withConfig` method returns a **new formatter** with a different date pattern while preserving the user's locale. It accepts a `DateTimeFormatterConfig` with the following options:
215+
216+
* `{ type: "date" }`: platform default date format
217+
* `{ type: "time" }`: platform default time format
218+
* `{ type: "datetime" }`: platform default datetime format
219+
* `{ type: "custom", pattern: "..." }`: custom Unicode date pattern (for example `"EEEE"`, `"dd MMMM"`, `"MMMM YYYY"`)
220+
221+
The following example formats a date attribute using a custom month-year pattern:
222+
223+
```ts
224+
if (myDateAttribute.formatter.type === "datetime") {
225+
const customFormatter = myDateAttribute.formatter.withConfig({
226+
type: "custom",
227+
pattern: "MMMM YYYY"
228+
});
229+
const formatted = customFormatter.format(myDateAttribute.value); // e.g. "March 2026"
230+
}
231+
```
232+
233+
`getFormatPlaceholder` returns a locale-appropriate placeholder string for the active date pattern, useful for input field `placeholder` attributes:
234+
235+
```ts
236+
const placeholder = myDateAttribute.formatter.type === "datetime"
237+
? myDateAttribute.formatter.getFormatPlaceholder()
238+
: undefined;
239+
```
240+
241+
##### NumberFormatter
242+
243+
**Decimal**, **Integer**, and **Long** attributes receive a `NumberFormatter`, which extends `SimpleFormatter<Big>`:
244+
245+
```ts
246+
interface NumberFormatter extends SimpleFormatter<Big> {
247+
readonly type: "number";
248+
readonly config: NumberFormatterConfig;
249+
withConfig(config: NumberFormatterConfig): NumberFormatter;
250+
}
251+
```
252+
253+
`NumberFormatterConfig` has the following options:
254+
255+
```ts
256+
interface NumberFormatterConfig {
257+
readonly groupDigits: boolean; // e.g. 1,000,000
258+
readonly decimalPrecision?: number;
259+
}
260+
```
261+
262+
The following example disables the thousands separator and fixes the output to four decimal places:
263+
264+
```ts
265+
if (myNumberAttribute.formatter.type === "number") {
266+
const customFormatter = myNumberAttribute.formatter.withConfig({
267+
groupDigits: false,
268+
decimalPrecision: 4
269+
});
270+
const formatted = customFormatter.format(myNumberAttribute.value); // e.g. "1234.5600"
271+
}
272+
```
273+
274+
##### Plain SimpleFormatter
275+
276+
For **string**, **enumeration**, and **Boolean** attributes the platform provides a plain `SimpleFormatter<T>` without a `type` property. These formatters convert raw values to human-readable captions (for example, enum captions configured in Studio Pro) and parse text input back to the typed value. They do **not** have `withConfig` or `getFormatPlaceholder`:
277+
278+
```ts
279+
// myEnumAttribute is an EditableValue<string>
280+
const caption = myEnumAttribute.formatter.format(myEnumAttribute.value); // e.g. "In Progress"
281+
```
282+
283+
##### Custom Formatters via setFormatter
284+
285+
You can supply a fully custom formatter for any attribute type using `setFormatter`. The object must implement `format` and `parse`:
286+
287+
```ts
288+
myDecimalAttribute.setFormatter({
289+
format(value: Big | undefined): string {
290+
return value !== undefined ? `$${Number(value).toFixed(2)}` : "";
291+
},
292+
parse(text: string): { valid: true; value: Big } | { valid: false } {
293+
const num = Number(text.replace(/[$,]/g, ""));
294+
return isNaN(num) ? { valid: false } : { valid: true, value: new Big(num) };
295+
}
296+
});
297+
```
298+
299+
Call `setFormatter(undefined)` to reset the formatter to the platform default.
300+
301+
##### Quick Reference
302+
303+
| Formatter type | `type` value | `withConfig` | `getFormatPlaceholder` | Applies to |
304+
|---|---|---|---|---|
305+
| `DateTimeFormatter` | `"datetime"` |`DateTimeFormatterConfig` || `Date` |
306+
| `NumberFormatter` | `"number"` |`NumberFormatterConfig` || `Big` (Decimal, Integer, Long) |
307+
| `SimpleFormatter` | `undefined` ||| `string`, `boolean`, Enum |
308+
166309
### ModifiableValue {#modifiable-value}
167310

168311
`ModifiableValue` is used to represent values that can be changed by a pluggable widget client component. It is passed only to [association properties](/apidocs-mxsdk/apidocs/pluggable-widgets-property-types-10/#association), and is defined as follows:

content/en/docs/apidocs-mxsdk/apidocs/studio-pro-11/pluggable-widgets/pluggable-widgets-client-apis/_index.md

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,149 @@ There is a way to use more the convenient `displayValue` and `setTextValue` whi
161161

162162
The optional field `universe` is used to indicate the set of all possible values that can be passed to a `setValue` if a set is limited. Currently, `universe` is provided only when the edited value is of the Boolean or enumeration [types](/refguide/attributes/#type).
163163

164+
#### Formatter Details {#formatter-details}
165+
166+
The `formatter` field on `EditableValue` is defined as follows:
167+
168+
```ts
169+
type ParseResult<T> = { valid: true; value: T } | { valid: false };
170+
171+
interface SimpleFormatter<T> {
172+
format(value: T | undefined): string;
173+
parse(value: string): ParseResult<T>;
174+
}
175+
```
176+
177+
##### Built-in Formatter Types {#built-in-formatter-types}
178+
179+
The Mendix platform provides two typed, configurable built-in formatters that extend `SimpleFormatter<T>`: `NumberFormatter` and `DateTimeFormatter`. The actual type of `EditableValue.formatter` is `ValueFormatter<T>` — a union that covers both built-in and plain formatters:
180+
181+
```ts
182+
type ValueFormatter<T> =
183+
| (TypedFormatter<T> & (NumberFormatter | DateTimeFormatter))
184+
| (SimpleFormatter<T> & { readonly type?: never });
185+
```
186+
187+
Use the `type` property as a type guard to narrow to a specific built-in formatter before accessing its extra API:
188+
189+
```ts
190+
if (myAttribute.formatter.type === "datetime") {
191+
// DateTimeFormatter — has withConfig, getFormatPlaceholder
192+
} else if (myAttribute.formatter.type === "number") {
193+
// NumberFormatter — has withConfig
194+
} else {
195+
// Plain SimpleFormatter — string, enum, or boolean
196+
}
197+
```
198+
199+
##### DateTimeFormatter
200+
201+
**Date/DateTime** attributes receive a `DateTimeFormatter`, which extends `SimpleFormatter<Date>`:
202+
203+
```ts
204+
interface DateTimeFormatter extends SimpleFormatter<Date> {
205+
readonly type: "datetime";
206+
readonly config: DateTimeFormatterConfig;
207+
withConfig(config: DateTimeFormatterConfig): DateTimeFormatter;
208+
getFormatPlaceholder(): string | undefined;
209+
}
210+
```
211+
212+
The `withConfig` method returns a **new formatter** with a different date pattern while preserving the user's locale. It accepts a `DateTimeFormatterConfig` with the following options:
213+
214+
* `{ type: "date" }`: platform default date format
215+
* `{ type: "time" }`: platform default time format
216+
* `{ type: "datetime" }`: platform default datetime format
217+
* `{ type: "custom", pattern: "..." }`: custom Unicode date pattern (for example `"EEEE"`, `"dd MMMM"`, `"MMMM YYYY"`)
218+
219+
The following example formats a date attribute using a custom month-year pattern:
220+
221+
```ts
222+
if (myDateAttribute.formatter.type === "datetime") {
223+
const customFormatter = myDateAttribute.formatter.withConfig({
224+
type: "custom",
225+
pattern: "MMMM YYYY"
226+
});
227+
const formatted = customFormatter.format(myDateAttribute.value); // e.g. "March 2026"
228+
}
229+
```
230+
231+
`getFormatPlaceholder` returns a locale-appropriate placeholder string for the active date pattern, useful for input field `placeholder` attributes:
232+
233+
```ts
234+
const placeholder = myDateAttribute.formatter.type === "datetime"
235+
? myDateAttribute.formatter.getFormatPlaceholder()
236+
: undefined;
237+
```
238+
239+
##### NumberFormatter
240+
241+
**Decimal**, **Integer**, and **Long** attributes receive a `NumberFormatter`, which extends `SimpleFormatter<Big>`:
242+
243+
```ts
244+
interface NumberFormatter extends SimpleFormatter<Big> {
245+
readonly type: "number";
246+
readonly config: NumberFormatterConfig;
247+
withConfig(config: NumberFormatterConfig): NumberFormatter;
248+
}
249+
```
250+
251+
`NumberFormatterConfig` has the following options:
252+
253+
```ts
254+
interface NumberFormatterConfig {
255+
readonly groupDigits: boolean; // e.g. 1,000,000
256+
readonly decimalPrecision?: number;
257+
}
258+
```
259+
260+
The following example disables the thousands separator and fixes the output to four decimal places:
261+
262+
```ts
263+
if (myNumberAttribute.formatter.type === "number") {
264+
const customFormatter = myNumberAttribute.formatter.withConfig({
265+
groupDigits: false,
266+
decimalPrecision: 4
267+
});
268+
const formatted = customFormatter.format(myNumberAttribute.value); // e.g. "1234.5600"
269+
}
270+
```
271+
272+
##### Plain SimpleFormatter
273+
274+
For **string**, **enumeration**, and **Boolean** attributes the platform provides a plain `SimpleFormatter<T>` without a `type` property. These formatters convert raw values to human-readable captions (for example, enum captions configured in Studio Pro) and parse text input back to the typed value. They do **not** have `withConfig` or `getFormatPlaceholder`:
275+
276+
```ts
277+
// myEnumAttribute is an EditableValue<string>
278+
const caption = myEnumAttribute.formatter.format(myEnumAttribute.value); // e.g. "In Progress"
279+
```
280+
281+
##### Custom Formatters via setFormatter
282+
283+
You can supply a fully custom formatter for any attribute type using `setFormatter`. The object must implement `format` and `parse`:
284+
285+
```ts
286+
myDecimalAttribute.setFormatter({
287+
format(value: Big | undefined): string {
288+
return value !== undefined ? `$${Number(value).toFixed(2)}` : "";
289+
},
290+
parse(text: string): { valid: true; value: Big } | { valid: false } {
291+
const num = Number(text.replace(/[$,]/g, ""));
292+
return isNaN(num) ? { valid: false } : { valid: true, value: new Big(num) };
293+
}
294+
});
295+
```
296+
297+
Call `setFormatter(undefined)` to reset the formatter to the platform default.
298+
299+
##### Quick Reference
300+
301+
| Formatter type | `type` value | `withConfig` | `getFormatPlaceholder` | Applies to |
302+
|---|---|---|---|---|
303+
| `DateTimeFormatter` | `"datetime"` |`DateTimeFormatterConfig` || `Date` |
304+
| `NumberFormatter` | `"number"` |`NumberFormatterConfig` || `Big` (Decimal, Integer, Long) |
305+
| `SimpleFormatter` | `undefined` ||| `string`, `boolean`, Enum |
306+
164307
### EditableFileValue {#editable-file-value}
165308

166309
`EditableFileValue` is used to represent file values, that can be changed by a pluggable widget client component and is passed only to [file](/apidocs-mxsdk/apidocs/pluggable-widgets-property-types/#file). It is defined as follows:

0 commit comments

Comments
 (0)