# How to Implement Data Binding and DataSource API Calls in Luban H5

> Learn how to implement data binding and DataSource API calls in Luban H5 using its reactive Vuex store and bindData utility. Synchronize UI elements with REST endpoint data.

- Repository: [小小鲁班/luban-h5](https://github.com/ly525/luban-h5)
- Tags: how-to-guide
- Published: 2026-03-06

---

**Luban H5 implements data binding through a reactive Vuex store architecture where the `bindData` utility in [`utils/data-binding.js`](https://github.com/ly525/luban-h5/blob/main/utils/data-binding.js) synchronizes UI element properties with DataSource values fetched via REST endpoints.**

The luban-h5 repository provides a low-code H5 page builder that separates UI presentation from data management through a robust data binding system. Understanding how to implement data binding and DataSource API calls in Luban H5 requires examining the reactive connection between visual elements and the Vuex store modules that manage remote data synchronization.

## Understanding the Data Binding Architecture

Luban H5 separates UI elements from underlying data through four distinct layers that manage reactive synchronization.

**Utility Layer**: The `bindData` function in [`front-end/h5/src/utils/data-binding.js`](https://github.com/ly525/luban-h5/blob/main/front-end/h5/src/utils/data-binding.js) registers Vue reactive watchers that connect element properties to the global store.

**Model Layer**: Each visual element defined in [`front-end/h5/src/components/core/models/element.js`](https://github.com/ly525/luban-h5/blob/main/front-end/h5/src/components/core/models/element.js) imports `bindData` and invokes it for bindable fields like `text` or `value`, storing a `dataSourceId` that references a specific DataSource definition.

**Store Layer**: The Vuex module in [`front-end/h5/src/components/core/store/modules/data-source.js`](https://github.com/ly525/luban-h5/blob/main/front-end/h5/src/components/core/store/modules/data-source.js) maintains DataSource definitions and their runtime values, listening to API changes and automatically updating bound elements.

**Component Layer**: Core components consume these reactive values through `v-bind` or `v-model` directives, rendering the synchronized data without manual fetch logic.

When `bindData` establishes a two-way subscription, changes from the Vuex store propagate to the UI automatically, while user edits push updates back through the store to trigger API persistence calls.

## DataSource API Implementation

The backend service exposes CRUD endpoints for DataSource management, while the frontend handles reactive state updates.

Available Endpoints:

- `GET /datasource/:id` - Retrieves DataSource definitions (fields, SQL, or REST configuration) from [`back-end/h5-api/api/datasource/services/datasource.js`](https://github.com/ly525/luban-h5/blob/main/back-end/h5-api/api/datasource/services/datasource.js)
- `GET /datasource/:id/value` - Fetches runtime values executed against the configured query or remote request
- `POST /datasource` - Creates new DataSource definitions
- `PUT /datasource/:id` - Updates configurations and persists cached data changes
- `DELETE /datasource/:id` - Removes DataSource definitions

The frontend utilizes [`front-end/h5/src/utils/http.js`](https://github.com/ly525/luban-h5/blob/main/front-end/h5/src/utils/http.js) as a thin Axios wrapper to consume these endpoints. The Vuex module dispatches actions such as `fetchDataSource(id)` and `saveDataSource(id, payload)`, storing fetched values under `state.values[id]` as the single source of truth for the binding layer.

## Implementing Two-Way Data Binding

Connecting UI elements to remote data requires configuring the model, store, and component layers.

### Binding Element Properties

In [`front-end/h5/src/components/core/models/element.js`](https://github.com/ly525/luban-h5/blob/main/front-end/h5/src/components/core/models/element.js), the Element class constructor checks for `dataSourceId` and initializes bindings:

```javascript
// front-end/h5/src/components/core/models/element.js
import { bindData } from '@/utils/data-binding.js'

export default class Element {
  constructor (props) {
    this.id = props.id
    this.type = props.type            // e.g., 'text'
    this.dataSourceId = props.dataSourceId   // optional
    this.text = props.text || ''
    // If a data source is defined, bind the text property
    if (this.dataSourceId) {
      bindData(this, 'text', this.dataSourceId)
    }
  }
}

```

The `bindData` function attaches a Vue reactive watcher listening to `store.state.dataSource.values[dataSourceId]`, automatically updating `element.text` when the remote data changes.

### Configuring the Vuex Store Module

The [`front-end/h5/src/components/core/store/modules/data-source.js`](https://github.com/ly525/luban-h5/blob/main/front-end/h5/src/components/core/store/modules/data-source.js) file manages API communication and state mutations:

```javascript
// front-end/h5/src/components/core/store/modules/data-source.js
import http from '@/utils/http.js'

export default {
  namespaced: true,
  state: {
    definitions: {},   // { id: { name, config, … } }
    values: {}         // { id: actual runtime value }
  },
  actions: {
    async fetchDefinition ({ commit }, id) {
      const res = await http.get(`/datasource/${id}`)
      commit('SET_DEFINITION', { id, def: res.data })
    },
    async fetchValue ({ commit }, id) {
      const res = await http.get(`/datasource/${id}/value`)
      commit('SET_VALUE', { id, value: res.data })
    },
    async saveValue ({ dispatch }, { id, payload }) {
      await http.put(`/datasource/${id}`, payload)
      // Refresh cached value after update
      dispatch('fetchValue', id)
    }
  },
  mutations: {
    SET_DEFINITION (state, { id, def }) {
      state.definitions = { ...state.definitions, [id]: def }
    },
    SET_VALUE (state, { id, value }) {
      state.values = { ...state.values, [id]: value }
    }
  }
}

```

This module handles the complete lifecycle: fetching definitions, retrieving runtime values via `/datasource/:id/value`, and persisting changes through PUT requests.

### Consuming Bound Data in Components

Components access bound values through reactive properties, as demonstrated in [`front-end/h5/src/components/core/editor/data-source/variable-pool.vue`](https://github.com/ly525/luban-h5/blob/main/front-end/h5/src/components/core/editor/data-source/variable-pool.vue):

```html
<!-- front-end/h5/src/components/core/editor/data-source/variable-pool.vue -->
<template>
  <div class="variable-pool">
    <!-- The element's `text` is automatically reactive -->
    <input v-model="element.text" />
    <p>Current value: {{ element.text }}</p>
  </div>
</template>

<script>
import { mapState } from 'vuex'
export default {
  props: { elementId: String },
  computed: {
    ...mapState('work', ['elements']),
    element () {
      return this.elements[this.elementId]
    }
  }
}
</script>

```

When users edit the input field, the `v-model` binding updates `element.text`, which the `bindData` watcher propagates back to the Vuex store and subsequently to the backend API through the `saveValue` action.

## Summary

- **Data binding** in luban-h5 relies on the `bindData` utility in [`utils/data-binding.js`](https://github.com/ly525/luban-h5/blob/main/utils/data-binding.js) to establish reactive connections between element properties and Vuex store values.
- The **DataSource API** exposes REST endpoints at `/datasource/:id` and `/datasource/:id/value` to manage definitions and runtime data, implemented in [`back-end/h5-api/api/datasource/services/datasource.js`](https://github.com/ly525/luban-h5/blob/main/back-end/h5-api/api/datasource/services/datasource.js).
- **Two-way synchronization** occurs automatically: store updates flow to the UI via Vue reactivity, while user edits trigger PUT requests to persist changes.
- The **Vuex store module** in [`components/core/store/modules/data-source.js`](https://github.com/ly525/luban-h5/blob/main/components/core/store/modules/data-source.js) serves as the central hub for API calls and state management, consumed by the Element model and UI components.

## Frequently Asked Questions

### What is the role of the `bindData` function in Luban H5?

The `bindData` function in [`front-end/h5/src/utils/data-binding.js`](https://github.com/ly525/luban-h5/blob/main/front-end/h5/src/utils/data-binding.js) establishes a reactive subscription between a UI element's property and a specific DataSource ID in the Vuex store. It creates a two-way binding that automatically updates the element when the store changes and propagates user edits back to the store for API persistence.

### How does the DataSource API handle runtime values versus definitions?

The backend distinguishes between static definitions and dynamic values through separate endpoints. The `GET /datasource/:id` endpoint retrieves configuration metadata from [`back-end/h5-api/api/datasource/services/datasource.js`](https://github.com/ly525/luban-h5/blob/main/back-end/h5-api/api/datasource/services/datasource.js), while `GET /datasource/:id/value` executes the configured query or remote request to fetch current runtime data that binds directly to UI components.

### Can custom DataSource types be integrated into the Luban H5 architecture?

Yes. Developers can extend the DataSource service by modifying [`back-end/h5-api/api/datasource/services/datasource.js`](https://github.com/ly525/luban-h5/blob/main/back-end/h5-api/api/datasource/services/datasource.js) to handle additional connection types (GraphQL, WebSocket, etc.) and updating the Vuex store module to manage new data structures. The `bindData` utility remains agnostic to the data source type, requiring only that values are stored in `state.values[id]`.

### What triggers API updates when bound data changes?

User interactions with bound form elements update the reactive property through `v-model`, which the `bindData` watcher detects and propagates to the Vuex store. The store then dispatches the `saveValue` action, issuing a PUT request to `/datasource/:id` to persist the change, followed by a fresh `fetchValue` call to ensure UI consistency.