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

Luban H5 implements data binding through a reactive Vuex store architecture where the bindData utility in 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 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 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 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
  • 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 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, the Element class constructor checks for dataSourceId and initializes bindings:

// 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 file manages API communication and state mutations:

// 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:

<!-- 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 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.
  • 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 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 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, 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →