LiveVue 1.0
Examples Nested Objects

Nested Objects

Access nested form fields using dot notation paths. Perfect for forms with embedded objects like addresses, profiles, or settings.

What this example shows

1
Dot Notation
form.field("address.city")
2
Nested Validation
Errors per nested field
3
Flat Params
Nested data sent as nested map
Address
UnchangedInvalid

How it works

1 Define an embedded schema or relation

Use embeds_one or has_one/belongs_to to define nested objects. Each gets its own changeset and validation.

defmodule Address do
  use Ecto.Schema
  @derive LiveVue.Encoder
  embedded_schema do
    field :street, :string
    field :city, :string
    field :zip, :string
  end
end

embeds_one :address, Address, on_replace: :update

2 Use cast_embed for validation

Call cast_embed(:address) to automatically validate nested fields. Errors are mapped to the correct nested paths.

def changeset(profile, attrs) do
  profile
  |> cast(attrs, [:name, :email])
  |> validate_required([:name, :email])
  |> cast_embed(:address, with: &Address.changeset/2)
end

3 Access nested fields with dot notation or chaining

Use form.field("address.city") or chain with .field(). Both have full TypeScript support for type-safe paths. Use `field[index]` to access nested array fields, eg `form.field("tags[0].name")`.

// Dot notation
const cityField = form.field("address.city")

// Or chain .field() calls
const addressField = form.field("address")
const cityField = addressField.field("city")

4 Bind inputs exactly like flat fields

Once you have a field reference, use it exactly like any other field. The inputAttrs, errorMessage, and isTouched work identically.

<input v-bind="cityField.inputAttrs.value" type="text" />
<div v-if="cityField.errorMessage.value">
  {{ cityField.errorMessage.value }}
</div>
Next up: Dynamic Arrays with fieldArray()