Unpoly: Passing Data to Compilers

Updated . Posted . Visible to the public.

Quick reference for passing data from Rails to JavaScript via Unpoly compilers.

Haml Attribute Syntax

# Ising hash rockets and string symbols (method calls)
= form.text_field :name, 'date-picker': true

# Curly braces or brackets (elements) 
%div.container{ id: 'main', 'data-value': '123' }

Simple Values: data-* Attributes

Use for: Scalar values (IDs, strings, booleans)

%span.user{ 'data-age': '18', 'data-first-name': 'Bob' }
up.compiler('.user', (element, data) => {
  console.log(data.age)        // "18" (always string!)
  console.log(data.firstName)  // "Bob" (camelCase from dash-case)
})

⚠️ Important: Data attributes are always strings. Use type conversion if needed.


Complex Data: up-data

Use for: Objects, arrays, nested data

Object Syntax

%span.user{ 'up-data': { name: 'Bob', age: 18 }.to_json }
up.compiler('.user', (element, data) => {
  console.log(data.name)  // "Bob"
  console.log(data.age)   // 18 (actual number)
})

Info

Unpoly accepts any format fulfilling relaxed JSON Show archive.org snapshot .

Array Syntax

.google-map{ 'up-data': [
  { lat: 48.36, lng: 10.99, title: 'Friedberg' },
  { lat: 48.75, lng: 11.45, title: 'Ingolstadt' }
].to_json }
up.compiler('.google-map', (element, pins) => {
  pins.forEach(pin => {
    console.log(pin.lat, pin.lng, pin.title)
  })
})

ES6 Destructuring

%table.calendar{ planner: true, 'up-data': { type: @angle, view: @view }.to_json }
up.compiler('[planner]', (element, { type, view }) => {
  // Destructure properties directly from second parameter
})

Real-world example:

%table.calendar{ 
  planner: true, 
  'up-data': { 
    type: @angle, 
    address: @address.to_h, 
    icons: icons 
  }.to_json 
}
up.compiler('[planner]', (container, { type, address, icons }) => {
  // You can now access any properties on address, e.g. address.street as Unpoly parses the JSON to an object
  window.Planner = new window.planners[type](address), icons)
})

Merging data-* and up-data

When both exist, they merge into a single object:

%span.user{ 'data-name': 'Bob', 'up-data': { age: 18 }.to_json }
up.compiler('.user', (element, data) => {
  console.log(data.name)  // "Bob" (from data-name)
  console.log(data.age)   // 18 (from up-data)
})

Event Handlers

Data is also passed to up.on() handlers as the third parameter:

%span.user{ 'up-data': { age: 18, name: 'Bob' }.to_json } Bob
up.on('click', '.user', (event, element, data) => {
  console.log(`${data.name} is ${data.age} years old`)
})

Arbitrary Attributes

Access any HTML attribute with standard methods or Unpoly helpers:

%span.user{ name: 'Bob', age: '18', active: 'true' }
up.compiler('.user', (element) => {
  element.getAttribute('name')               // "Bob"
  up.element.numberAttr(element, 'age')      // 18 (number)
  up.element.booleanAttr(element, 'active')  // true (boolean)
})

Available helpers:

  • up.element.booleanAttr(element, attr)
  • up.element.numberAttr(element, attr)
  • up.element.jsonAttr(element, attr)

Quick Reference

Use Case Haml JavaScript Access
Simple string { 'data-id': @id } data.id (string)
Multiple strings { 'data-id': @id, 'data-name': @n } data.id, data.name
Dash-case attr { 'data-first-name': 'Bob' } data.firstName (camelCase)
Complex object { 'up-data': {...}.to_json } (el, data) =>
Complex + destructure { 'up-data': {...}.to_json } (el, { key1, key2 }) =>
Type conversion { 'count': '5' } up.element.numberAttr(el, 'count')

Best Practices

✅ Do:

  • Use data-* for simple scalar values
  • Use up-data for objects, arrays, and nested data
  • Always call .to_json on Ruby objects for up-data
  • Use ES6 destructuring: (el, { prop1, prop2 })
  • Remember data-* values are always strings
  • Use up.element.*Attr() helpers for type conversion

❌ Don't:

  • Pass sensitive data (visible in browser source)
  • Forget that data-* values are strings (not numbers/booleans)
  • Use complex JSON in data-* attributes (use up-data instead)
Profile picture of Felix Eschey
Felix Eschey
Last edit
Felix Eschey
License
Source code in this card is licensed under the MIT License.
Posted by Felix Eschey to makandra dev (2025-10-17 10:01)