Gửi một biểu mẫu
Trong ví dụ này, chúng tôi sẽ trình bày cách gửi một biểu mẫu bằng cách sử dụng API OneEntry.
✅ Mục đích của kịch bản:
- Lấy cấu hình biểu mẫu từ Nền tảng OneEntry
- Thu thập dữ liệu đầu vào của người dùng từ một biểu mẫu liên hệ.
- Đảm bảo dữ liệu được định dạng và xác thực đúng trước khi gửi.
- Gửi dữ liệu đã thu thập đến API OneEntry.
✅ Những gì bạn cần:
- Một PROJECT_URL và APP_TOKEN hợp lệ để xác thực với API OneEntry.
- Một biểu mẫu đã được cấu hình trước trong OneEntry được xác định bằng một mã (ví dụ: 'contact_us').
- Các trường biểu mẫu đã được cấu hình trước.
📌 Quan trọng:
- Xử lý các lỗi tiềm ẩn trong quá trình yêu cầu API một cách nhẹ nhàng để cung cấp phản hồi cho người dùng.
- Duy trì quyền riêng tư và bảo mật dữ liệu, đặc biệt khi xử lý thông tin nhạy cảm như địa chỉ email.
- Giữ cho các trường biểu mẫu và mã của chúng đồng bộ với cấu hình backend để tránh sự không khớp.
- Những ví dụ này không bao gồm xử lý lỗi.
- Bạn có thể quản lý lỗi bằng cách sử dụng khối try-catch hoặc bằng cách sử dụng một cấu trúc như await Promise.catch((error) => error).
📚 Xem trong tài liệu:
📦 Tham khảo SDK:
Thử nghiệm trực tiếp
Chạy phương thức này một cách tương tác trong JS SDK sandbox — kết nối URL Dự án và Mã ứng dụng của bạn khi truy cập lần đầu, sau đó mở:
- Gửi một biểu mẫu — Trong ví dụ này, chúng tôi sẽ trình bày cách gửi một biểu mẫu bằng cách sử dụng API OneEntry.
Kịch bản
1. Nhập defineOneEntry từ SDK và xác định PROJECT_URL và APP_TOKEN
Ví dụ:
import { defineOneEntry } from 'oneentry';
const PROJECT_URL = 'your-project-url';
const APP_TOKEN = 'your-app-token';
2. Tạo một khách hàng API với defineOneEntry()
Ví dụ:
const { Forms, FormData } = defineOneEntry(PROJECT_URL, {
token: APP_TOKEN,
});
3. Lấy biểu mẫu với Forms.getFormByMarker()
Ví dụ:
useEffect(() => {
Forms.getFormByMarker('contact_us').then((data: any) => {
setForm(data);
const initial: Record<string, string> = {};
data?.attributes?.forEach((a: any) => { initial[a.marker] = ''; });
setValues(initial);
console.log('Form Entity ', data);
}).catch(console.error);
}, []);
Kết quả:
{
"id": 3,
"attributeSetId": 2,
"type": "data",
"localizeInfos": {
"title": "Contact us",
"titleForSite": "",
"successMessage": "Message about successful data processing",
"unsuccessMessage": "Message about unsuccessful data processing",
"urlAddress": "",
"database": "0",
"script": "0"
},
"version": 5,
"position": 1,
"identifier": "contact_us",
"processingType": "script",
"templateId": null,
"attributes": [
{
"type": "string",
"marker": "first_name",
"position": 1,
"settings": {},
"isVisible": true,
"listTitles": [],
"validators": {
"requiredValidator": {
"strict": true
}
},
"initialValue": null,
"localizeInfos": {
"title": "First name"
},
"additionalFields": {}
},
{
"type": "string",
"marker": "email",
"position": 2,
"settings": {},
"isVisible": true,
"listTitles": [],
"validators": {
"requiredValidator": {
"strict": true
},
"emailInspectionValidator": true
},
"initialValue": null,
"localizeInfos": {
"title": "Email"
},
"additionalFields": {}
},
{
"type": "string",
"marker": "surname",
"position": 3,
"settings": {},
"isVisible": true,
"listTitles": [],
"validators": {
"stringInspectionValidator": {
"stringMax": 0,
"stringMin": 0,
"stringLength": 0
}
},
"initialValue": null,
"localizeInfos": {
"title": "Surname"
},
"additionalFields": {}
},
{
"type": "list",
"marker": "topic",
"position": 4,
"settings": {},
"isVisible": true,
"listTitles": [
{
"title": "Article",
"value": "article",
"extended": {
"type": null,
"value": null
},
"position": 1
},
{
"title": "Article-2",
"value": "article-2",
"extended": {
"type": null,
"value": null
},
"position": 2
}
],
"validators": {
"requiredValidator": {
"strict": true
}
},
"initialValue": null,
"localizeInfos": {
"title": "Topic"
},
"additionalFields": {}
},
{
"type": "text",
"marker": "text",
"position": 5,
"settings": {},
"isVisible": true,
"listTitles": [],
"validators": {},
"initialValue": null,
"localizeInfos": {
"title": "Text"
},
"additionalFields": {}
},
{
"type": "button",
"marker": "send",
"position": 7,
"settings": {},
"isVisible": true,
"listTitles": [],
"validators": {},
"initialValue": null,
"localizeInfos": {
"title": "Send"
},
"additionalFields": {}
}
],
"total": "1",
"moduleFormConfigs": [
{
"id": 2,
"moduleIdentifier": "content",
"isGlobal": false,
"isClosed": false,
"viewOnlyUserData": false,
"commentOnlyUserData": false,
"entityIdentifiers": [
{
"id": "services",
"isNested": false
}
],
"ratingCalculation": "average",
"allowHalfRatings": null,
"maxRatingScale": null,
"isAnonymous": null,
"allowRerating": null,
"isRating": null
}
]
}
4. Lấy các cài đặt bổ sung cho việc gửi biểu mẫu
Ví dụ:
const moduleConfig = form.moduleFormConfigs?.[0];
const sortedFields = [...(form.attributes || [])].sort(
(a: any, b: any) => a.position - b.position,
);
5. Lấy dữ liệu trường từ biểu mẫu frontend
Ví dụ:
const formData = sortedFields
.filter((f: any) => values[f.marker])
.map((f: any) => ({
marker: f.marker,
type: f.type,
value: prepareValue(f.type, values[f.marker] || ''),
}));
Kết quả:
[
{
"marker": "first_name",
"type": "string",
"value": "test"
},
{
"marker": "email",
"type": "string",
"value": "test@example.com"
},
{
"marker": "surname",
"type": "string",
"value": "test"
},
{
"marker": "topic",
"type": "list",
"value": [
"article"
]
},
{
"marker": "text",
"type": "text",
"value": [
{
"plainValue": "test"
}
]
}
]
6. Đăng FormsData với FormData.postFormsData()
Ví dụ:
const result = await FormData.postFormsData({
formIdentifier: 'contact_us',
formData,
formModuleConfigId: moduleConfig?.id || 0,
moduleEntityIdentifier: moduleConfig?.entityIdentifiers?.[0]?.id || '',
replayTo: null,
status: 'sent',
}).catch((e: any) => e);
Kết quả:
{
"formData": {
"formIdentifier": "contact_us",
"time": "2026-06-06T10:10:05.844Z",
"formData": [
{
"marker": "first_name",
"type": "string",
"value": "test"
},
{
"marker": "email",
"type": "string",
"value": "test@example.com"
},
{
"marker": "surname",
"type": "string",
"value": "test"
},
{
"marker": "topic",
"type": "list",
"value": [
"article"
]
},
{
"marker": "text",
"type": "text",
"value": [
{
"plainValue": "test"
}
]
}
],
"entityIdentifier": "services",
"fingerprint": "UQ_zhfcsr_mpdtr691jq7d",
"isUserAdmin": false,
"formModuleId": 2,
"userIdentifier": null,
"parentId": null,
"id": 172
},
"actionMessage": "Message about successful data processing"
}
Ví dụ cuối cùng
import React from 'react';
// Note: React is a sandbox global — in your project use named imports from 'react'
const { useState, useEffect } = React;
// 1. Import defineOneEntry from SDK and define PROJECT_URL and APP_TOKEN
import { defineOneEntry } from 'oneentry';
const PROJECT_URL = 'your-project-url';
const APP_TOKEN = 'your-app-token';
// 2. Creating an API client with [defineOneEntry()](/docs/index/#Installation)
const { Forms, FormData } = defineOneEntry(PROJECT_URL, {
token: APP_TOKEN,
});
// Prepare value for API submission by field type
function prepareValue(type: string, value: string) {
if (type === 'text') return [{ plainValue: value }];
if (type === 'list' || type === 'radioButton') return [value];
return value;
}
const typeMap: Record<string, string> = {
integer: 'number',
real: 'number',
float: 'number',
date: 'date',
time: 'time',
dateTime: 'datetime-local',
};
const ContactForm = () => {
const [form, setForm] = useState<any>(null);
const [values, setValues] = useState<Record<string, string>>({});
const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
const [errorMsg, setErrorMsg] = useState('');
// 3. Get form with [Forms.getFormByMarker()](/docs/forms/getFormByMarker)
useEffect(() => {
Forms.getFormByMarker('contact_us').then((data: any) => {
setForm(data);
const initial: Record<string, string> = {};
data?.attributes?.forEach((a: any) => { initial[a.marker] = ''; });
setValues(initial);
console.log('Form Entity ', data);
}).catch(console.error);
}, []);
if (!form) return <div>Loading...</div>;
const sortedFields = [...(form.attributes || [])].sort(
(a: any, b: any) => a.position - b.position,
);
const handleSubmit = async (e: any) => {
e.preventDefault();
if (!form) return;
setStatus('loading');
// 4. Get additional settings for the form submission
const moduleConfig = form.moduleFormConfigs?.[0];
const sortedFields = [...(form.attributes || [])].sort(
(a: any, b: any) => a.position - b.position,
);
// 5. Get fields data from frontend Form
const formData = sortedFields
.filter((f: any) => values[f.marker])
.map((f: any) => ({
marker: f.marker,
type: f.type,
value: prepareValue(f.type, values[f.marker] || ''),
}));
console.log('Form Data ', formData);
// 6. post FormsData with [FormData.postFormsData()](/docs/forms-data/postFormsData)
const result = await FormData.postFormsData({
formIdentifier: 'contact_us',
formData,
formModuleConfigId: moduleConfig?.id || 0,
moduleEntityIdentifier: moduleConfig?.entityIdentifiers?.[0]?.id || '',
replayTo: null,
status: 'sent',
}).catch((e: any) => e);
if (result?.statusCode >= 400) {
setErrorMsg(result.message || 'Submission failed');
setStatus('error');
} else {
setStatus('success');
console.log('Submit result: ', result);
}
};
return (
<form onSubmit={handleSubmit}>
<h2>{form.localizeInfos?.title || 'Contact Form'}</h2>
{sortedFields.map((field: any) => {
const label = field.localizeInfos?.title || field.marker;
const value = values[field.marker] || '';
const required = !!field.validators?.requiredValidator?.strict;
const placeholder = field.additionalFields?.placeholder?.value || '';
const onChange = (val: string) =>
setValues((prev) => ({ ...prev, [field.marker]: val }));
if (field.type === 'text') {
return (
<div key={field.marker}>
<label htmlFor={field.marker}>{label}</label>
<textarea
id={field.marker}
value={value}
placeholder={placeholder}
required={required}
onChange={(e: { target: { value: string; }; }) => onChange(e.target.value)}
/>
</div>
);
}
if (field.type === 'list') {
return (
<div key={field.marker}>
<label htmlFor={field.marker}>{label}</label>
<select
id={field.marker}
value={value}
required={required}
onChange={(e: { target: { value: string; }; }) => onChange(e.target.value)}
>
<option value="">— Select —</option>
{(field.listTitles || []).map((opt: any) => (
<option key={opt.value} value={opt.value}>{opt.title}</option>
))}
</select>
</div>
);
}
if (field.type === 'radioButton') {
return (
<fieldset key={field.marker}>
<legend>{label}</legend>
{(field.listTitles || []).map((opt: any) => (
<label key={opt.value}>
<input
type="radio"
name={field.marker}
value={opt.value}
checked={value === opt.value}
onChange={() => onChange(opt.value)}
/>
{opt.title}
</label>
))}
</fieldset>
);
}
if (field.type === 'button') {
return (
<button
key={field.marker}
type="submit"
disabled={status === 'loading'}
>
{label}
</button>
);
}
return (
<div key={field.marker}>
<label htmlFor={field.marker}>{label}</label>
<input
id={field.marker}
type={typeMap[field.type] || 'text'}
value={value}
placeholder={placeholder}
required={required}
onChange={(e: { target: { value: string; }; }) => onChange(e.target.value)}
/>
</div>
);
})}
{status === 'error' && <p role="alert">{errorMsg}</p>}
{status === 'success' && <p role="status">Form submitted successfully!</p>}
</form>
);
};
export default ContactForm;