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-datafor objects, arrays, and nested data - Always call
.to_jsonon Ruby objects forup-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 (useup-datainstead)
Posted by Felix Eschey to makandra dev (2025-10-17 10:01)