跳到主要内容

属性绑定

Darra HMI 的"属性绑定" = HTML 属性如何关联到 PLC 变量。有四种绑定方式, 从最声明式到最命令式:

  1. var="..." 属性 (darra-controls 自动订阅)
  2. Alpine x-text / x-bind / x-model 表达式
  3. data-darra-* 自定义属性 + JS 桥接
  4. 手动 darra.bind + DOM 操作

方式 1: var 属性 (最常用)

所有 <darra-*> 控件支持 var:

<darra-led var="M0.0"></darra-led>                         <!-- 位 -->
<darra-numeric var="DB1.Temperature"></darra-numeric> <!-- REAL -->
<darra-input var="DB2.Setpoint"></darra-input> <!-- 双向 -->
<darra-button var="M0.1" mode="toggle"></darra-button> <!-- 写 -->
<darra-tank var="DB1.Level" max="100"></darra-tank> <!-- 液位 -->

控件内部会调:

// <darra-led> 内部伪代码
darra.bind(this.getAttribute('var'), (v) => {
this.style.background = v ? this.getAttribute('color-on') : this.getAttribute('color-off')
})

多变量属性 (vars)

某些控件支持多变量:

<darra-trend vars="DB1.T1,DB1.T2,DB1.T3"></darra-trend>
<darra-dx-bar-gauge vars="DB1.X,DB1.Y,DB1.Z"></darra-dx-bar-gauge>

控件会逐个订阅, 更新时把最新值数组传给 Chart/DevExtreme。

配对变量 (不同用途)

<darra-motor run-var="M10.0" fault-var="M10.1"></darra-motor>
<darra-valve open-var="M11.0" closed-var="M11.1"></darra-valve>
<darra-bullet var="DB1.Actual" target-var="DB1.Target"></darra-bullet>

每种属性名代表不同用途, 由控件内部分别订阅。

方式 2: Alpine 表达式

对于普通 HTML 标签 (非 <darra-*>), 用 Alpine:

x-text (文本)

<div x-data="{ temp: 0 }"
x-init="darra.bind('DB1.Temp', v => temp = v)">
温度: <span x-text="temp.toFixed(2) + ' ℃'"></span>
</div>

x-bind / :attr (属性)

<div x-data="{ running: false, speed: 0 }"
x-init="
darra.bind('M0.0', v => running = v)
darra.bind('DB3.Speed', v => speed = v)">

<!-- class 根据状态切换 -->
<div :class="running ? 'badge-green' : 'badge-gray'"
x-text="running ? '运行' : '停止'"></div>

<!-- style 属性动态 -->
<div class="bar"
:style="`width: ${speed / 3000 * 100}%; background: ${speed > 2500 ? 'red' : 'green'}`">
</div>

<!-- disabled 布尔属性 -->
<button :disabled="!running" @click="darra.write('M0.1', true)">
只在运行时可点
</button>
</div>

x-model (双向绑定 input)

<div x-data="{ sp: 0 }"
x-init="darra.bind('DB2.Setpoint', v => sp = v)">
<input type="number" x-model.number="sp"
@change="darra.write('DB2.Setpoint', sp)" />
</div>

x-show / x-if (显隐)

<div x-data="{ alarm: false }"
x-init="darra.bind('M100.0', v => alarm = v)">

<div x-show="alarm" class="alarm-banner">
⚠ 设备进入报警状态
</div>

<template x-if="alarm">
<div>仅报警时渲染此 DOM (否则不存在)</div>
</template>
</div>

x-show 只切 display: none, x-if 增删 DOM。性能敏感用 x-show, 重逻辑用 x-if

x-for (循环)

<div x-data="{ axes: [] }"
x-init="
const list = []
for (let i = 0; i < 6; i++) list.push({ id: i, pos: 0, enabled: false })
axes = list
axes.forEach((a, i) => {
darra.bind(`DB50.Axes[${i}].Pos`, v => a.pos = v)
darra.bind(`DB50.Axes[${i}].Enabled`, v => a.enabled = v)
})">

<template x-for="axis in axes" :key="axis.id">
<div class="axis-card">
<span x-text="axis.id"></span>:
<span x-text="axis.pos.toFixed(3)"></span> mm
<span :class="axis.enabled ? 'green' : 'gray'"></span>
</div>
</template>
</div>

方式 3: data-darra-* 自定义属性

当需要非 Alpine 的纯 DOM 操作时:

<div class="kpi" data-darra-var="DB1.Temperature" data-darra-format="0.0" data-darra-unit="">
<span class="value">--</span>
</div>

<script>
// 页面脚本 (JsCode) 统一扫描
document.querySelectorAll('[data-darra-var]').forEach(el => {
const varName = el.dataset.darraVar
const format = el.dataset.darraFormat || '0'
const unit = el.dataset.darraUnit || ''
const decimals = (format.split('.')[1] || '').length

darra.bind(varName, (v) => {
const span = el.querySelector('.value')
if (span) span.textContent = Number(v).toFixed(decimals) + ' ' + unit
})
})
</script>

好处: 模板设计师可直接在 HTML 写属性, JS 人员写统一扫描器。

方式 4: 手动 darra.bind + DOM

darra.bind('DB1.Level', (v) => {
const bar = document.querySelector('.level-bar')
bar.style.height = (v / 100 * 200) + 'px'
bar.style.background = v > 90 ? 'red' : '#1E90FF'

const label = document.querySelector('.level-label')
label.textContent = v.toFixed(1) + '%'
})

何时用: Alpine 和 <darra-*> 都不够灵活时, 比如操作 SVG 路径、Canvas 上下文、WebGL 纹理。

组合绑定 (多变量→单显示)

<div x-data="{ a: 0, b: 0, c: 0 }"
x-init="
darra.bindGroup(['DB1.A','DB1.B','DB1.C'], vs => { a=vs['DB1.A']; b=vs['DB1.B']; c=vs['DB1.C'] })">

<!-- 显示总和 -->
总和: <span x-text="(a + b + c).toFixed(2)"></span>

<!-- 显示均值 -->
均值: <span x-text="((a + b + c) / 3).toFixed(2)"></span>

<!-- 条件 -->
<div x-show="a > b && b > c">递减趋势</div>
</div>

数据格式化 (Formatter)

Alpine 内可用普通 JS 函数:

<script>
function fmtTime(v) {
return new Date(v * 1000).toLocaleTimeString()
}
function fmtPercent(v) {
return (v * 100).toFixed(1) + '%'
}
function fmtColor(v) {
if (v < 30) return '#1E90FF'
if (v < 70) return '#F59E0B'
return '#EF4444'
}
</script>

<div x-data="{ t: 0, r: 0 }"
x-init="darra.bind('DB1.Temp', v => t = v); darra.bind('DB1.Ratio', v => r = v)">
更新: <span x-text="fmtTime(Date.now() / 1000)"></span>
温度: <span x-text="t.toFixed(1)" :style="`color:${fmtColor(t)}`"></span>
比例: <span x-text="fmtPercent(r)"></span>
</div>

写入绑定

自动 (控件内)

<darra-input var="DB2.Setpoint"></darra-input>                 <!-- 失焦写入 -->
<darra-slider var="DB3.Speed" debounce="300"></darra-slider> <!-- 去抖 300ms -->
<darra-button var="M0.0" mode="toggle"></darra-button> <!-- 点击写入 -->

Alpine 显式

<input type="range" min="0" max="100" x-data="{ v: 0 }"
x-model.number="v"
@change="darra.write('DB3.Speed', v)" />

原生 JS

document.getElementById('setpoint-btn').addEventListener('click', () => {
const sp = Number(document.getElementById('setpoint-input').value)
darra.write('DB2.Setpoint', sp)
})

绑定性能

方式成本适合
var 属性最低 (控件内部优化)常规
Alpine x-text少量动态
x-for 循环中 (每项都响应)< 200 项
手动 darra.bind + DOM复杂控制
darra.bindGroup最低多变量聚合

超过 500 个绑定时, 建议合并为一个 bindDB, 在回调里统一批量更新。

陷阱

陷阱解法
var="DB1.Temp " (含空格)变量名严格, 去除空格
Alpine x-text 计算表达式里调 darra.write别! 表达式只读, 写入放 @event
一个 var 绑 N 个控件OK, 每个控件独立订阅, 性能无影响
循环里 darra.bind 未用闭包let 而非 var, 或包 .forEach
断开重连后手动 DOM 更新丢失darra.bind 自动重新订阅

相关文档