# Data Properties

Data Properties hold the state of your component and are defined with a **data** structure. Each data property is assigned a default value and is automatically synchronized between server and client.

Data properties are reactive - when an action modifies a property, CBWIRE automatically re-renders only the affected parts of your template. They can hold strings, numbers, booleans, dates, arrays, and structures, and are directly accessible in templates without special syntax.

{% tabs %}
{% tab title="BoxLang" %}

```javascript
//./wires/SomeComponent.bx
class extends="cbwire.models.Component" {
    data = {
        "propertyName": "defaultValue"
    };
}
```

{% endtab %}

{% tab title="CFML" %}

```javascript
//./wires/SomeComponent.cfc
component extends="cbwire.models.Component" {
    data = {
        "propertyName": "defaultValue"
    };
}
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
JavaScript parses your data properties. Your data properties can only store JavaScript-friendly values: strings, numerics, arrays, structs, or booleans.
{% endhint %}

{% hint style="warning" %}
**Single or double quotes must surround property names.**

JavaScript is a case-sensitive language, and CFML isn't. To preserve the casing of your property names, you must surround them with quotes.

Don't do this.

```javascript
data = {
    propertyName: "defaultValue"
};
```

{% endhint %}

{% hint style="danger" %}
Data properties are visible to JavaScript. You SHOULD NOT store sensitive data in them. Use locked properties for additional protection against client-side modifications.
{% endhint %}

## Accessing From Actions

Using the **data** structure, you can access data properties from within your component [actions](/the-essentials/actions.md).

```javascript
// Data properties
data = {
    "time": now()
}; 
   
// Action
function updateTime(){
    data.time = now();
}
```

## Accessing From Templates

You can access data properties within your [templates](/the-essentials/templates.md) using **#propertyName#***.*

{% tabs %}
{% tab title="Boxlang" %}

```html
<bx:output>
    <div>
        <h1>Current time</h1>
        <div>#time#</div>
        <button wire:click="updateTime">Update</button>
    </div>
</bx:output>
```

{% endtab %}

{% tab title="CFML" %}

```html
<cfoutput>
    <div>
        <h1>Current time</h1>
        <div>#time#</div>
        <button wire:click="updateTime">Update</button>
    </div>
</cfoutput>
```

{% endtab %}
{% endtabs %}

## Nested Properties

Organize related data using nested structures and access them with dot notation. This keeps your data organized hierarchically instead of flattening everything into top-level properties.

{% tabs %}
{% tab title="BoxLang" %}

```javascript
// wires/UserProfile.bx
class extends="cbwire.models.Component" {
    data = {
        "user": {
            "name": {
                "first": "John",
                "last": "Doe"
            },
            "contact": {
                "email": "john@example.com",
                "phone": "555-1234"
            },
            "preferences": {
                "theme": "dark",
                "notifications": true
            }
        }
    };
}
```

{% endtab %}

{% tab title="CFML" %}

```javascript
// wires/UserProfile.cfc
component extends="cbwire.models.Component" {
    data = {
        "user" = {
            "name" = {
                "first" = "John",
                "last" = "Doe"
            },
            "contact" = {
                "email" = "john@example.com",
                "phone" = "555-1234"
            },
            "preferences" = {
                "theme" = "dark",
                "notifications" = true
            }
        }
    };
}
```

{% endtab %}
{% endtabs %}

### Accessing in Templates

Reference nested properties using dot notation in `wire:model` and other directives:

{% tabs %}
{% tab title="BoxLang" %}

```html
<!-- wires/userProfile.bxm -->
<bx:output>
<form wire:submit="saveProfile">
    <input type="text" wire:model="user.name.first" placeholder="First Name">
    <input type="text" wire:model="user.name.last" placeholder="Last Name">
    <input type="email" wire:model="user.contact.email" placeholder="Email">
    <input type="tel" wire:model="user.contact.phone" placeholder="Phone">

    <select wire:model="user.preferences.theme">
        <option value="light">Light</option>
        <option value="dark">Dark</option>
    </select>

    <label>
        <input type="checkbox" wire:model="user.preferences.notifications">
        Enable notifications
    </label>

    <button type="submit">Save Profile</button>
</form>
</bx:output>
```

{% endtab %}

{% tab title="CFML" %}

```html
<!-- wires/userProfile.cfm -->
<cfoutput>
<form wire:submit="saveProfile">
    <input type="text" wire:model="user.name.first" placeholder="First Name">
    <input type="text" wire:model="user.name.last" placeholder="Last Name">
    <input type="email" wire:model="user.contact.email" placeholder="Email">
    <input type="tel" wire:model="user.contact.phone" placeholder="Phone">

    <select wire:model="user.preferences.theme">
        <option value="light">Light</option>
        <option value="dark">Dark</option>
    </select>

    <label>
        <input type="checkbox" wire:model="user.preferences.notifications">
        Enable notifications
    </label>

    <button type="submit">Save Profile</button>
</form>
</cfoutput>
```

{% endtab %}
{% endtabs %}

### Accessing in Actions

Use dot notation to access and modify nested properties in your component methods:

{% tabs %}
{% tab title="BoxLang" %}

```javascript
// wires/UserProfile.bx
class extends="cbwire.models.Component" {
    data = {
        "user": {
            "name": {
                "first": "John",
                "last": "Doe"
            },
            "contact": {
                "email": "john@example.com"
            }
        }
    };

    function saveProfile() {
        // Access nested properties
        var fullName = data.user.name.first & " " & data.user.name.last;

        // Modify nested properties
        data.user.contact.email = data.user.contact.email.trim();

        // Save to database
        userService.update( data.user );
    }

    function updateTheme( theme ) {
        data.user.preferences.theme = theme;
    }
}
```

{% endtab %}

{% tab title="CFML" %}

```javascript
// wires/UserProfile.cfc
component extends="cbwire.models.Component" {
    data = {
        "user" = {
            "name" = {
                "first" = "John",
                "last" = "Doe"
            },
            "contact" = {
                "email" = "john@example.com"
            }
        }
    };

    function saveProfile() {
        // Access nested properties
        var fullName = data.user.name.first & " " & data.user.name.last;

        // Modify nested properties
        data.user.contact.email = trim( data.user.contact.email );

        // Save to database
        userService.update( data.user );
    }

    function updateTheme( theme ) {
        data.user.preferences.theme = theme;
    }
}
```

{% endtab %}
{% endtabs %}

### Lifecycle Hooks

Lifecycle hooks for nested properties use underscores instead of dots. A property path like `user.name.first` triggers `onUpdateuser_name_first()`:

{% tabs %}
{% tab title="BoxLang" %}

```javascript
// wires/UserProfile.bx
class extends="cbwire.models.Component" {
    data = {
        "user": {
            "contact": {
                "email": "john@example.com"
            }
        }
    };

    function onUpdateuser_contact_email( value, oldValue ) {
        // Triggered when user.contact.email changes
        writeLog( "Email changed from #oldValue# to #value#" );

        // Validate email format
        if ( !isValid( "email", value ) ) {
            data.user.contact.email = oldValue;
            addError( "user.contact.email", "Please enter a valid email address" );
        }
    }
}
```

{% endtab %}

{% tab title="CFML" %}

```javascript
// wires/UserProfile.cfc
component extends="cbwire.models.Component" {
    data = {
        "user" = {
            "contact" = {
                "email" = "john@example.com"
            }
        }
    };

    function onUpdateuser_contact_email( value, oldValue ) {
        // Triggered when user.contact.email changes
        writeLog( "Email changed from #oldValue# to #value#" );

        // Validate email format
        if ( !isValid( "email", value ) ) {
            data.user.contact.email = oldValue;
            addError( "user.contact.email", "Please enter a valid email address" );
        }
    }
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Missing nested keys are created automatically. If `data.user` doesn't have a `preferences` key, setting `data.user.preferences.theme = "dark"` creates the structure automatically.
{% endhint %}

## Resetting Properties

You can reset all data properties to their original default value inside your [actions](/the-essentials/actions.md) using **reset()**.

```javascript
data = {
    "time": now()
};

function resetTime(){
    reset();
}
```

You can reset individual, some, or all data properties.

```javascript
function resetForm() {
    reset( "message" ); // resets 'message' data property
    reset( [ "message", "anotherprop" ] ); // reset multiple properties at once
    reset(); // resets all properties
}
```

You can also reset all properties EXCEPT the properties you pass.

```javascript
function resetForm() {
    resetExcept( "email" );
    resetExcept( [ "email", "phoneNumber" ] );
}
```

## Locked Properties

Locked properties prevent client-side modifications to sensitive data like user IDs, roles, or permissions. Define a `locked` variable to protect specific properties from `wire:model` and other client interactions. CBWIRE throws an exception if locked properties are modified from the client.

### Single Property

{% tabs %}
{% tab title="BoxLang" %}

```javascript
// wires/UserProfile.bx
class extends="cbwire.models.Component" {
    // Lock single property
    locked = "userId";
    
    data = {
        "userId": 123,
        "name": "John Doe",
        "email": "john@example.com",
        "isActive": true
    };
    
    function updateProfile() {
        // Can modify name, email, isActive
        // Cannot modify userId - it's locked
    }
}
```

{% endtab %}

{% tab title="CFML" %}

```javascript
// wires/UserProfile.cfc
component extends="cbwire.models.Component" {
    // Lock single property
    locked = "userId";
    
    data = {
        "userId" = 123,
        "name" = "John Doe",
        "email" = "john@example.com",
        "isActive" = true
    };
    
    function updateProfile() {
        // Can modify name, email, isActive
        // Cannot modify userId - it's locked
    }
}
```

{% endtab %}
{% endtabs %}

### Multiple Properties

{% tabs %}
{% tab title="BoxLang" %}

```javascript
// wires/AdminPanel.bx
class extends="cbwire.models.Component" {
    // Lock multiple properties
    locked = ["userId", "role", "permissions", "accountType"];
    
    data = {
        "userId": 123,
        "name": "Jane Admin",
        "email": "jane@company.com",
        "role": "administrator",
        "permissions": ["read", "write", "delete", "admin"],
        "accountType": "premium",
        "lastLogin": now(),
        "theme": "dark"
    };
    
    function updateSettings() {
        // Can modify: name, email, lastLogin, theme
        // Cannot modify: userId, role, permissions, accountType
        data.lastLogin = now();
        data.theme = "light";
    }
}
```

{% endtab %}

{% tab title="CFML" %}

```javascript
// wires/AdminPanel.cfc
component extends="cbwire.models.Component" {
    // Lock multiple properties
    locked = ["userId", "role", "permissions", "accountType"];
    
    data = {
        "userId" = 123,
        "name" = "Jane Admin",
        "email" = "jane@company.com",
        "role" = "administrator",
        "permissions" = ["read", "write", "delete", "admin"],
        "accountType" = "premium",
        "lastLogin" = now(),
        "theme" = "dark"
    };
    
    function updateSettings() {
        // Can modify: name, email, lastLogin, theme
        // Cannot modify: userId, role, permissions, accountType
        data.lastLogin = now();
        data.theme = "light";
    }
}
```

{% endtab %}
{% endtabs %}

### Template Usage

Locked properties can be displayed but not modified through form inputs:

{% tabs %}
{% tab title="BoxLang" %}

```html
<!-- wires/adminPanel.bxm -->
<bx:output>
<div>
    <h1>User Profile</h1>
    
    <!-- Display locked properties (read-only) -->
    <div class="readonly-info">
        <p><strong>User ID:</strong> #userId#</p>
        <p><strong>Role:</strong> #role#</p>
        <p><strong>Account Type:</strong> #accountType#</p>
    </div>
    
    <!-- Form with editable properties -->
    <form wire:submit="updateSettings">
        <!-- These inputs work normally -->
        <input type="text" wire:model="name" placeholder="Name">
        <input type="email" wire:model="email" placeholder="Email">
        
        <select wire:model="theme">
            <option value="light">Light</option>
            <option value="dark">Dark</option>
        </select>
        
        <!-- These would throw an exception if attempted -->
        <!-- <input type="hidden" wire:model="userId"> ❌ -->
        <!-- <input type="text" wire:model="role"> ❌ -->
        
        <button type="submit">Update Profile</button>
    </form>
</div>
</bx:output>
```

{% endtab %}

{% tab title="CFML" %}

```html
<!-- wires/adminPanel.cfm -->
<cfoutput>
<div>
    <h1>User Profile</h1>
    
    <!-- Display locked properties (read-only) -->
    <div class="readonly-info">
        <p><strong>User ID:</strong> #userId#</p>
        <p><strong>Role:</strong> #role#</p>
        <p><strong>Account Type:</strong> #accountType#</p>
    </div>
    
    <!-- Form with editable properties -->
    <form wire:submit="updateSettings">
        <!-- These inputs work normally -->
        <input type="text" wire:model="name" placeholder="Name">
        <input type="email" wire:model="email" placeholder="Email">
        
        <select wire:model="theme">
            <option value="light">Light</option>
            <option value="dark">Dark</option>
        </select>
        
        <!-- These would throw an exception if attempted -->
        <!-- <input type="hidden" wire:model="userId"> ❌ -->
        <!-- <input type="text" wire:model="role"> ❌ -->
        
        <button type="submit">Update Profile</button>
    </form>
</div>
</cfoutput>
```

{% endtab %}
{% endtabs %}

### Common Use Cases

* **User Management**: Lock `userId`, `role`, `permissions`
* **E-commerce**: Lock product IDs, prices, inventory counts
* **Financial**: Lock account numbers, balances, transaction IDs
* **Content**: Lock author IDs, creation dates, publication status

{% hint style="info" %}
Locked properties only prevent client-side modifications. Server-side code in your action methods can still modify locked properties when necessary.
{% endhint %}

{% hint style="warning" %}
Remember that locked properties are still visible in the client-side JavaScript. For truly sensitive data that shouldn't be visible to users, avoid including it in data properties altogether and access it through server-side services instead.
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://cbwire.ortusbooks.com/the-essentials/properties.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
