---
title: "doo.model"
url: "https://help.tabidoo.cloud/en/article/doomodel"
language: "en"
last_updated: "2026-08-24T10:46:32.810Z"
---

# doo.model

Accessing and Working with Current Record in Scripts

The `doo.model` object gives you access to the current record in Scripting within Tabidoo. Whether you're using scripts in form’s lifecycle (**on model load**, **on model change**, **before model save**), or in **Workflow Automation**, this object is the core way to read and modify field values.

## What Is `doo.model`?

`doo.model` is a JavaScript object representing the data model of the current record. Each property corresponds to a field in your table.

## General Properties and Methods

`doo.model` contains all of the table fields and some other predefined properties:

### `doo.model.id`

Returns internal record identifier (e.g. *"865f8bac-ed8a-4979-972f-3f1706fb1bb1"*)

**Example**

```typescript
const recordId = doo.model.id;
```

Here’s what `doo.model.id` is useful for:

**Example: Accessing the current record in another context**

```typescript
const record = await doo.table.getRecord('Orders', doo.model.id);
```

**Example: Creating relationships between records**

```typescript
const record = await doo.table.createRecord('Order Row', { orderId: doo.model.id });
```

**Example: Generating links (e.g., in emails or buttons)**

```typescript
const urlLink = `https://app.tabidoo.cloud/app/myApp/schema/orders?recordId=${doo.model.id}`
```

### `doo.model.ver`

Holds the internal version number of the record. It’s incremented automatically every time the record is updated.

**Version Values and Their Meaning**

-   `**-1**` — The record does not exist yet. You are in the form *before* the record is created (e.g., during the *On Model Load* of a new record).
-   `**0**` — The record has just been created (No updates yet).
-   `**1+**` — The record has been updated at least once.

You can use `doo.model.ver` to apply different logic for new vs. existing records (e.g. Enable/Disable Field on form).

**Example** 

```typescript
if (doo.model.ver === -1) {
  console.log('Record not created yet.');
} else {
  console.log('Existing record with version:', doo.model.ver);
}
```

### `doo.model.isReadOnly`

`doo.model.isReadOnly` is a special property that, when set to `true`, makes the entire form **non-editable**. It’s typically used to enforce business rules — for example, preventing edits to records with certain statuses or by unauthorized users.

**Example**

```typescript
doo.model.isReadOnly = true; // Locks the entire form
```

**Example: Make the form read-only for archived records**

```typescript
doo.model.isReadOnly =  doo.model.status.value === 'Archived';

// be careful, this part set the reaOnly to true - never back
if (doo.model.status.value === 'Archived') {
 	doo.model.isReadOnly = true;
}
```

**Example: Allow only admins to edit**

```typescript
doo.model.isReadOnly = !doo.currentUser.roles.includes('Admin'));
```

Note: The form may appear editable because users can open and view available options in some fields, such as dropdown, multichoice, or link-to-table fields. However, field values cannot actually be changed because the form cannot be saved. If your goal is only to prevent saving a record, consider using validations (`isValid`) instead.

### `doo.model.isValid`

`doo.model.isValid` controls whether the form can be saved. By default, it is `true`. If your script sets it to `false`, Tabidoo will block form submission and notify the user with an error message.

This is useful for applying **custom validations** that go beyond field-level rules.

**Example**

```typescript
doo.model.isValid = false; // Prevents the form from being saved
```

You typically use this in combination with some logic in the *Before Model Save* event.

**Example: Custom validation across multiple fields**

```typescript
// Start date must be before end date.
doo.model.isValid = doo.model.startDate.value > doo.model.endDate.value);
```

### `doo.model.infotext`

Assign a message to this property in case you want to show something in the form header.

**Example**

```typescript
doo.model.infotext = 'Please double-check all fields before saving.';
```

### `doo.model.hideCategory()`

Allows you to dynamically hide a specific **tab (category)** within a form. It is useful when you want to simplify the user interface or restrict access to sections of the form based on certain conditions.

-   The argument is the **label (header)** of the tab as defined in the form.
-   The category becomes hidden immediately when the script runs.

**Example**

```typescript
doo.model.hideCategory('Advanced Settings');
```

**Example: Hide a tab for new records**

```typescript
if(doo.model.ver === -1) {
	doo.model.hideCategory('Internal Notes');
}
```

### `doo.model.showCategory()`

`doo.model.showCategory()` is used to **reveal** a tab (category) within a form that was previously hidden using `doo.model.hideCategory()`. This method is useful when you want to show specific parts of the form only under certain conditions.

**Example**

```typescript
doo.model.showCategory('Advanced Settings');
```

**Example: Show category after field selection**

```typescript
if (doo.model.type.value === 'Custom') {
	doo.model.showCategory('Customization Options');
}
```

### `doo.model.selectCategory()`

Allows you to programmatically focus a specific tab (category) in a form. This is useful for improving user experience — guiding them to relevant parts of the form based on their input or actions.

-   Switches the active tab in the form to the specified category.
-   Has no effect if the category does not exist or is hidden.

**Example**

```typescript
doo.model.selectCategory('Details');
```

**Example: Focus on the first required tab**

```plaintext
if (!doo.model.customer.value) {
 	doo.model.selectCategory('Customer Info');
}
```

### `doo.model.modelMetadata`

The `modelMetadata` object provides access to metadata about the currently open form as well as any parent forms in a nested form hierarchy. This is useful for context-aware scripting, especially in linked or widget-based forms.

#### `doo.model.modelMetadata.currentForm`

Metadata related to the current form.

`currentForm` **Properties**

`doo.model.modelMetadata.currentForm.level` - Indicates the level of nesting in the hierarchy. A top-level form has `level = 1`.

```typescript
const isTopLevel = doo.model.modelMetadata.currentForm.level === 1;
```

`doo.model.modelMetadata.currentForm.tableId` \- The unique identifier of the table to which the form belongs.

```typescript
const tableId = doo.model.modelMetadata.currentForm.tableId;
```

`doo.model.modelMetadata.currentForm.tableInternalName` - Internal name of the table (used in scripts and APIs).

```typescript
const tableName = doo.model.modelMetadata.currentForm.tableInternalName;
```

`doo.model.modelMetadata.currentForm.isWidget` - Indicates whether the form is being displayed as a widget (read-only detail preview).

```typescript
if (doo.model.modelMetadata.currentForm.isWidget) {
	console.log('This form is shown in widget view.');
}
```

#### `doo.model.modelMetadata.parentModel`

Access and control the parent form when working within nested forms.

`parentModel` **Properties**

`doo.model.modelMetadata.parentModel.id` \- The internal record identifier of the parent form.

```typescript
const parentId = doo.model.modelMetadata.parentModel.id;
```

`doo.model.modelMetadata.parentModel.ver` \- The internal version of the parent record.

```typescript
const parentVersion = doo.model.modelMetadata.parentModel.ver;
```

`doo.model.modelMetadata.parentModel.isReadOnly`\- Set to `true` to make the parent form read-only.

```typescript
doo.model.modelMetadata.parentModel.isReadOnly = true;
```

`doo.model.modelMetadata.parentModel.isValid`\- Set to `false` to prevent saving the parent form.

```typescript
doo.model.modelMetadata.parentModel.isValid = false;
```

`doo.model.modelMetadata.parentModel.infotext` - Show a custom message in the parent form header.

```typescript
doo.model.modelMetadata.parentModel.infotext = 'Parent form validation failed.';
```

`parentModel` **Methods**

`doo.model.modelMetadata.parentModel.hideCategory(categoryLabel: string)`\- Hides a specific category (tab) in the parent form.

```typescript
doo.model.modelMetadata.parentModel.hideCategory('Internal Notes');
```

`doo.model.modelMetadata.parentModel.showCategory(categoryLabel: string)`\- Shows a previously hidden category (tab) in the parent form.

```typescript
doo.model.modelMetadata.parentModel.showCategory('Internal Notes');
```

`doo.model.modelMetadata.parentModel.selectCategory(categoryLabel: string)`\- Focuses a specific category (tab) in the parent form.

```typescript
doo.model.modelMetadata.parentModel.selectCategory('Customer Info');
```

## Field-Level Properties and Methods

`doo.model` is a JavaScript object that represents the data model of the current record. Each property corresponds to a field in your table. Each field (e.g., `doo.model.status`) includes the following properties and methods:

### `doo.model.<field>.value`

Allows you to **get or set the current value** of a specific field in the form/record. It’s the most commonly used property when working with data in scripts.

**Example**

```typescript
const status = doo.model.status.value; // Reading a value
doo.model.priority.value = 'High';     // Setting a new value
```

You access fields using their **internal name**, not the visible label. For example, if your field label is *Order Status* and its internal name is `status`, you use `doo.model.status.value`.

-   `value` reflects what's currently in the form — including unsaved changes.
-   Use `.setValue(...)` if you want more control or when working with linked fields.

**Behavior by Data Type**

**Text / Long Text / Markdown**

```typescript
doo.model.firstname.value = 'John'; // Text field
doo.model.description.value = '<p>Hello!</p><p><br></p><p>How are you?</p>'; // Long text field
doo.model.pageContent.value = '# Headline\nText\n'; // Markdown field
```

**Number / Decimal / Percentage / Money**

```typescript
doo.model.age.value = 10; // Number field
doo.model.length.value = 65.2543; // Decimal field
doo.model.vat.value = 0.1 // Percentage field
doo.model.cost.value = 999.99; // Money field
```

**Money With Currency**

```typescript
doo.model.price.value = { amount: 10, currency: "USD", date: "2025-07-16T00:00:00" }; // Money with currency field
```

**Checkbox (Yes/No)**

```typescript
doo.model.isPublic.value = true; // Checkbox field
```

**Date / Datetime**

```typescript
doo.model.validFrom.value = new Date(); // Date field
```

**Dropdown / Radio**

```typescript
doo.model.state.value = 'Done'; // Dropdown field
doo.model.choice.value = 'Single'; // Radio field
```

**Multi choice / Tags**

```typescript
doo.model.colors.value = ['Red', 'Blue']; // Multi choice field
doo.model.tags.value = ['Garden', 'Outdoor']; // Tags field
```

**URL/Mailto link**

```typescript
doo.model.email.value = { href: doo.currentUser.login, isMailto: true, search: doo.currentUser.login }; // Mailto link  
doo.model.urlLink.value = { href: 'https://tabidoo.cloud', description: 'Link to Tabidoo' }; // URL link
```

**Note: Email and URL fields use the same object structure for reading and writing**

Email and URL fields are stored as an **object**, not a plain string. This applies both when **setting** the value (as shown above) and when **reading** it — via `doo.model.<field>.value` in a form script, or via `row.fields.<field>` in`doo.table.getData()` results.

```typescript
// Structure of an Email field
{
  href: "john.doe@example.com",
  isMailto: true
} 
// Structure of a URL field
{
  href: "https://tabidoo.cloud",
  description: "Link to Tabidoo"
}
```

When reading the value from a record (e.g. a linked record's field), always access `.href` — do not use the field object directly as a string:

```typescript
// WRONG – passes the whole object instead of the email string
addEmail(row.fields?.approver?.fields?.email); 
// CORRECT – reads the actual email address from .href
addEmail(row.fields?.approver?.fields?.email?.href);
```

**List of checkboxes (Checklist)**

```typescript
const subtasks = doo.model.subtasks.value; // READ-ONLY
```

**Chat**

```typescript
const chat = doo.model.chat.value; // READ-ONLY
```

**Link to Table**

```typescript
doo.model.customer.value = { id: 'c56a4180-65aa-42ec-a945-5fd21dec0538' }; // Link to table (Lookup) field
```

**Example: Setting a field based on a condition**

```typescript
if (doo.model.total.value > 1000) {
 	doo.model.discount.value = '10%';
}
```

### `doo.model.<field>.setValue(value)`

The `.setValue()` method is a convenient and reliable way to assign a value to a specific field in your form. We recommend using `.setValue()` especially for date fields and linked records, where direct assignment to `.value` might not work as expected.

**Setting linked records**

When setting linked records in a form script, we recommend using `.setValue()`.

For relation fields that link to a single record (**1:1 or N:1**), pass the target record ID directly.

```typescript
doo.model.<field>.setValue(recordId);
```

**Example:**

```typescript
doo.model.customer.setValue('865f8bac-ed8a-4979-972f-3f1706fb1bb1');
```

**Note:** `.value` and `.setValue()` use different formats for linked fields.

-   `.value` expects an **object**: `{ id: 'recordId' }`
-   `.setValue()` expects the **ID as a plain string**: `'recordId'`

Do not mix the two — `doo.model.customer.setValue({ id: 'recordId' })` is incorrect.  
  
For relation fields that can link to multiple records (**N:M / many-to-many**), pass an array of target record IDs.

```typescript
doo.model.<field>.setValue([recordId]);
```

**Example:**

```typescript
doo.model.members.setValue(['865f8bac-ed8a-4979-972f-3f1706fb1bb1']);
```

**To set multiple linked records:**

```typescript
doo.model.members.setValue([
  '865f8bac-ed8a-4979-972f-3f1706fb1bb1',
  '7c3f09de-9df6-4c35-a3f4-0f9df4a11c21'
]);
```

If you do not know the exact record ID, you can pass a text value and let Tabidoo search for the matching linked record.

```typescript
doo.model.customer.setValue('John Smith');
```

**Set a Links to many records field using a filter**

For fields linking to many records, you can also use a filter.

```typescript
doo.model.members.setValue({ filter: "surname(in)(Black)" });
```

*Note: When using* `*.setValue()*` *in a* ***Before Model Save*** *action, it must be called with* `*await*` *to ensure the value is applied before the model is saved.*

### `doo.model.<field>.originalValue`

`originalValue` gives you access to the **initial value** of a field before the user made any changes. It’s useful when you need to compare the **current value** (`.value`) with what was originally loaded into the form.

**Example**

```typescript
const original = doo.model.status.originalValue;
const current = doo.model.status.value;
if (original !== current) {
 	console.log('Status has been changed by the user.');
}
```

**Example: Prevent specific field change**

```typescript
if (doo.model.status.originalValue === 'Approved' &&
    doo.model.status.value !== 'Approved') {
 	doo.model.isValid = false;
 	doo.toast.warning('Approved status cannot be changed.');
}
```

**Use Cases**

-   Detecting whether a user has updated a field.
-   Logging or auditing changes before saving.
-   Validating transitions (e.g., allowed status changes).

**Notes**

-   `originalValue` is available as soon as the form loads.
-   It does not reflect unsaved intermediate changes — it’s always the last saved value.
-   Best used in *Before Save* scripts or *On Change* when validating edits.

### `doo.model.<field>.currentlyChanged` 

`currentlyChanged` is a **boolean property** available only in *On Model Change* scripts. It tells you whether the specific field that triggered the script was actually modified — avoiding unnecessary execution when the change event is triggered by other updates in the form.

**Example**

```typescript
if (doo.model.status.currentlyChanged) {
	console.log('The status field was changed by the user.');
}
```

This ensures that your logic runs **only** when the value truly changes — not when other fields update or the form is re-evaluated.

**Example: Trigger logic only when a field changes**

```typescript
if (doo.model.discount.currentlyChanged) {
 	doo.model.total.value = recalculateTotal();
}
```

**Notes**

-   Only available in `On Model Change` event scripts.
-   Helps reduce redundant logic executions.
-   Prevents side effects from re-triggering scripts unnecessarily.

### `doo.model.<field>.isVisible` 

`isVisible` is a boolean property that allows you to **control the visibility** of a field in the form. It’s commonly used to simplify the interface or show fields conditionally based on user input, roles, or record status.

**Example**

```typescript
doo.model.comments.isVisible = false; // Hide the field
doo.model.notes.isVisible = true;     // Show the field
```

Use this in `On Model Load` or `On Model Change` scripts to dynamically manage what users see.

**Example: Show/Hide a field based on status**

```typescript
doo.model.resolution.isVisible = doo.model.status.value === 'Closed'; 
```

**Example: Show field for admins only**

```typescript
if (doo.currentUser.roles.includes('Admin')) {
	doo.model.internalNote.isVisible = true;
} else {
	doo.model.internalNote.isVisible = false;
}
```

**Notes**

-   Hidden fields are still part of the record — their values are retained unless you clear them manually.
-   Use with caution when hiding **required fields**; validation may still fail unless you also adjust `isRequired`.

### `doo.model.<field>.isRequired` 

`isRequired` is a boolean property that allows you to dynamically set whether a field must be filled before saving the form. It’s useful when required fields depend on other field values, user roles, or the current record state.

**Example**

```typescript
doo.model.deadline.isRequired = true;  // Make the field required
doo.model.notes.isRequired = false;    // Make the field optional
```

This setting overrides the default configuration in the table definition for the current form session.

**Example: Make a field required only for new records**

```typescript
if (doo.model.ver === -1) {
 	doo.model.responsible.isRequired = true;
}
```

**Example: Conditionally require a field**

```typescript
doo.model.resolution.isRequired = doo.model.status.value === 'Closed';
```

**Notes**

-   The user must fill in all required fields before saving; otherwise, the form is blocked.
-   Combine with `isVisible` to ensure required fields aren’t hidden unintentionally.
-   Applies only during the current editing session — the original table setup remains unchanged.

### `doo.model.<field>.isEnabled`

`isEnabled` is a boolean property that allows you to **enable or disable a field** in the form. When disabled, the field becomes **read-only** and cannot be modified by the user, although its value can still be changed programmatically.

**Example**

```typescript
doo.model.discount.isEnabled = false; // Disable input
doo.model.comment.isEnabled = true;   // Enable input
```

This affects only the form UI — the field remains visible but grayed out when disabled.

**Example: Lock a field based on record status**

```typescript
doo.model.total.isEnabled = doo.model.status.value === 'Approved';
```

**Example: Disable field after first save**

```typescript
if (doo.model.ver >= 0) {
	doo.model.orderNumber.isEnabled = false;
}
```

**Notes**

-   Disabling a field does not clear its value or prevent the form from being saved.
-   Use `doo.model.isReadOnly` to disable the **entire form**.
-   `isEnabled` is ideal for partial control — e.g. lock specific inputs while leaving others editable.

### `doo.model.<field>.filterForDropdown` 

`filterForDropdown` allows you to **filter available options** in a **Dropdown** or **Lookup** field based on a condition. This is helpful when the list of choices should change depending on user input, roles, or record context.

**Example**

```typescript
doo.model.status.filterForDropdown = ['New', 'Approved'];
```

**Excluding options**

Instead of listing the options you want to show, you can **exclude** specific options by prefixing them with an exclamation mark `!`. All other options remain available.

```typescript
doo.model.status.filterForDropdown = ['!New']; // Shows all options except 'New'
```

You can exclude multiple options at once:

```typescript
doo.model.status.filterForDropdown = ['!New', '!Canceled']; // Hides 'New' and 'Canceled'
```

**Changing the filter later (e.g. in On Model Change)**

If you set a filter in *On Model Load* and later want to replace it with a different one (for example in an *On Model Change* script), first reset the filter by assigning `null` and then set the new value:

```typescript
// On Model Load
doo.model.dropdown_3.filterForDropdown = ['!New']; // Shows all options except 'New'

// On Model Change — reset and apply a new filter
doo.model.dropdown_3.filterForDropdown = null; // Reset filter
doo.model.dropdown_3.filterForDropdown = ['!Canceled']; // Shows all options except 'Canceled'
```

For **linked fields (Lookups)**, you can use filter expressions in a string format:

```typescript
doo.model.customer.filterForDropdown = "region === 'North'";
```

**Note:**

  
The example above shows the correct syntax format for defining a filter expression as a string.  
However, when you want to filter customers where the `region` field equals `North`, the actual expression itself (without being wrapped in quotes) would look like this:

```typescript
region === North 
```

In the example

```typescript
doo.model.customer.filterForDropdown = "region === 'North'";
```

the quotation marks are used only because the filter expression is stored **as a string**. The inner quotes around `'North'`represent the string value of the `region` field, not part of the syntax itself.

  
**Example: Filter dropdown by user role**

```typescript
if (doo.currentUser.roles.includes('Admin')) {
	doo.model.state.filterForDropdown = ['Active', 'Closed'];
} else {
	doo.model.state.filterForDropdown = ['New'];
}
```

**Example: Filter Lookup based on another field**

```typescript
const branch = doo.model.branch.value;
doo.model.employees.filterForDropdown = `Branch === ${branch}`;
```

**Example: Filter for multiple conditions**

```typescript
doo.model.members.filterForDropdown = "Branch === 'A01' or Branch === 'B01'";
doo.model.cities.filterForDropdown = "city contains Pra";
doo.model.tasks.filterForDropdown = "state in [New, Canceled]"; // for arrays - e.g. multichoice
```

**Notes**

-   Filtering is **text-based** and limited to **simple expressions** (e.g., `===`, `contains`, `in`). `filterForDropdown` has its own simple syntax. Do not use `doo.table` API operators such as `(eq)`, `(neq)`, `(lt)`, or `(gt)` here.
-   Applied only during the form session — doesn't affect the data in the table.

### `doo.model.<field>.label`

The `label` property allows you to **dynamically change the visible label (header)** of a field in the form. This is useful for context-sensitive forms where field meaning or language may vary based on user role, status, or input.

Example

```typescript
doo.model.priority.label = 'Urgency Level';
```

This updates the label shown above the input field, without affecting the internal name or data.

**Example: Change label based on status**

```typescript
if (doo.model.status.value === 'Escalated') {
	doo.model.comment.label = 'Manager Comment';
} else {
	doo.model.comment.label = 'User Comment';
}
```

**Notes**

-   The change is visual only — it does not affect the field’s internal name or data structure.
-   Can be used together with `isVisible`, `isEnabled`, and `isRequired` for full dynamic UI control.

### `doo.model.<field>.setStyle()`

The `setStyle()` method allows you to dynamically style individual fields in a form. It’s useful when you want to draw attention to specific fields — for example, as part of a custom validation or warning.

**Using Predefined Tabidoo Styles**

Tabidoo provides several predefined styles such as `Warning`, `Danger`, `Primary`, etc., which you can use to highlight fields easily.

**Example**

```typescript
doo.model.firstname.setStyle(DooFieldStyle.Warning);
```

To reset the style back to default, use:

```typescript
doo.model.firstname.setStyle({});
```

**Using Custom CSS Styles**

You can define custom styles using standard CSS (in camelCase notation). You can style the field label (`labelStyle`) and the field wrapper (`wrapperStyle`).

**Example**

```typescript
doo.model.firstname.setStyle({
	labelStyle: { color: 'red' },
	wrapperStyle: { background: '#ffefef' }
});
```

`**IDooFieldStyle**` **Interface**

-   `labelStyle?`: Styles the field label (e.g., `{ color: 'red' }`)
-   `wrapperStyle?`: Styles the field wrapper (e.g., `{ background: 'lightyellow' }`)

**Notes**

-   Use camelCase syntax for CSS properties (e.g., `borderLeft` instead of `border-left`).
-   This styling is purely visual — it does not affect field values or validation behavior.
