# Custom Element With Custom Vue
FIELD API EXTEND FIELDS CUSTOM EDITOR COMPONENTThe Fluent Forms BaseFieldManager
extension allows developers to create custom form fields with custom editor components.
This guide demonstrates how to create a custom field called MyCustomElement
by extending the BaseFieldManager
class, along with implementing custom Vue components for the editor interface.
# Implementation Overview
To create a custom field, you'll need to:
- Create a PHP class that extends
BaseFieldManager
- Register custom Vue components for the editor interface
- Implement the required methods for field rendering and configuration
# PHP Class Implementation
First, create a PHP class that extends BaseFieldManager
:
<?php
namespace MyPlugin\CustomFields;
use FluentForm\App\Services\FormBuilder\BaseFieldManager;
use FluentForm\Framework\Helpers\ArrayHelper as Arr;
class MyCustomElement extends BaseFieldManager
{
/**
* Wrapper class for the element
* @var string
*/
protected $wrapperClass = 'my_custom_element_wrapper';
public function __construct()
{
parent::__construct(
'my_custom_element', // Unique element key
'My Custom Element', // Element title
['custom', 'element', 'advanced'], // Search tags
'advanced' // Position (general/advanced)
);
// Add filter for response rendering
add_filter('fluentform/response_render_' . $this->key, array($this, 'renderResponse'), 10, 4);
add_filter('fluentform/validate_input_item_' . $this->key, array($this, 'validateInput'), 10, 5);
// Enqueue custom editor assets
add_action('fluentform/loading_editor_assets', function () {
wp_enqueue_script(
'my-custom-element-editor',
MY_PLUGIN_URL . 'assets/js/custom-element.js',
[],
'1.0.0',
true
);
});
}
function getComponent()
{
return [
'index' => 25, // Priority in the form editor
'element' => 'my_custom_element',
'attributes' => [
'name' => 'my_custom_element',
'data-type' => 'my_custom_element'
],
'settings' => [
'label' => __('My Custom Element', 'my-plugin'),
'admin_field_label' => '',
'custom_option' => '',
'conditional_logics' => [],
'container_class' => '',
'custom_settings' => [
'option_one' => '',
'option_two' => '',
'show_extra' => 'hide'
],
'validation_rules' => [
'required' => [
'value' => false,
'message' => __('This field is required', 'my-plugin'),
],
],
],
'editor_options' => [
'title' => __('My Custom Element', 'my-plugin'),
'icon_class' => 'el-icon-s-operation',
'template' => 'CustomEditorField', // IMPORTANT: Must be 'CustomEditorField' to use custom Vue components
'componentName' => 'MyCustomElementEditor', // IMPORTANT: Must match the Vue component name in JavaScript
],
];
}
public function getGeneralEditorElements()
{
return [
'label',
'label_placement',
'admin_field_label',
'custom_option',
'custom_settings', // This will use the custom settings component
'validation_rules',
];
}
public function getAdvancedEditorElements()
{
return [
'container_class',
'name',
'conditional_logics'
];
}
public function getEditorCustomizationSettings()
{
return [
'custom_option' => [
'template' => 'selectGroup',
'label' => __('Custom Option', 'my-plugin'),
'options' => [
[
'value' => 'option1',
'label' => __('Option 1', 'my-plugin')
],
[
'value' => 'option2',
'label' => __('Option 2', 'my-plugin')
],
[
'value' => 'option3',
'label' => __('Option 3', 'my-plugin')
]
]
],
'custom_settings' => [
'template' => 'CustomSettingsField', // IMPORTANT: Must be 'CustomSettingsField' to use custom Vue components
'label' => __('Custom Settings', 'my-plugin'),
'componentName' => 'MyCustomSettingsComponent' // IMPORTANT: Must match the Vue component name in JavaScript
],
];
}
/**
* Render the element on the frontend
* @param array $data Element data
* @param object $form Form object
* @return void
*/
public function render($data, $form)
{
$elementName = $data['element'];
// Add default class
$data['attributes']['class'] = trim('ff-el-form-control ' . Arr::get($data, 'attributes.class', ''));
$data['attributes']['id'] = $this->makeElementId($data, $form);
// Add tab index if available
if ($tabIndex = \FluentForm\App\Helpers\Helper::getNextTabIndex()) {
$data['attributes']['tabindex'] = $tabIndex;
}
// Set aria-required attribute
$ariaRequired = Arr::get($data, 'settings.validation_rules.required.value') ? 'true' : 'false';
// Get custom settings
$customOption = Arr::get($data, 'settings.custom_option');
$showExtra = Arr::get($data, 'settings.custom_settings.show_extra', 'hide') === 'show';
// Enqueue frontend assets if needed
wp_enqueue_script(
'my-custom-element-frontend',
MY_PLUGIN_URL . 'assets/js/custom-element-frontend.js',
['jquery'],
'1.0.0',
true
);
// Create element markup
$elMarkup = '<div class="my_custom_element_container">';
$elMarkup .= '<input ' . $this->buildAttributes($data['attributes'], $form) . ' aria-required="' . $ariaRequired . '">';
if ($showExtra) {
$elMarkup .= '<div class="extra_content">Extra content is visible</div>';
}
$elMarkup .= '</div>';
// Build final HTML
$html = $this->buildElementMarkup($elMarkup, $data, $form);
// Output the HTML
echo apply_filters('fluentform/rendering_field_html_' . $elementName, $html, $data, $form);
}
/**
* Format the field value for display in entries, emails, etc.
* @param mixed $data Field value
* @param array $field Field settings
* @param int $form_id Form ID
* @param bool $isHtml Whether HTML output is expected
* @return string
*/
public function renderResponse($data, $field, $form_id, $isHtml)
{
if (is_array($data)) {
return implode(', ', $data);
}
return $data;
}
/**
* Validate the field input
* @param array $errorMessage Error message array
* @param array $field Field settings
* @param array $formData Form data
* @param array $fields All form fields
* @param object $form Form object
* @return array
*/
public function validateInput($errorMessage, $field, $formData, $fields, $form)
{
$fieldName = $field['name'];
if (empty($formData[$fieldName])) {
return $errorMessage;
}
$value = $formData[$fieldName]; // This is the user input value
// Add your custom validation logic here
return $errorMessage;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# Important Notes About Component Names
# In PHP:
- In the
getComponent()
method, set'template' => 'CustomEditorField'
to tell Fluent Forms to use a custom Vue component. - The
componentName
property ineditor_options
must match exactly the name you register in JavaScript. - Similarly, in
getEditorCustomizationSettings()
, use'template' => 'CustomSettingsField'
and setcomponentName
to match your settings component name.
These connections are critical - if the names don't match exactly, Fluent Forms won't be able to find and render your custom components.
# JavaScript Implementation
Next, create a JavaScript file to register your custom Vue components:
// custom-element.js
import MyCustomElementEditor from './components/MyCustomElementEditor.vue';
import MyCustomSettingsComponent from './components/MyCustomSettingsComponent.vue';
// Register the components with EXACTLY the same names used in PHP
window.ffEditorOptionsCustomComponents = window.ffEditorOptionsCustomComponents || {};
// IMPORTANT: These keys must match the componentName values in PHP
window.ffEditorOptionsCustomComponents.MyCustomElementEditor = MyCustomElementEditor;
window.ffEditorOptionsCustomComponents.MyCustomSettingsComponent = MyCustomSettingsComponent;
2
3
4
5
6
7
8
9
10
11
# Vue Component for Editor
Create a Vue component for the main editor interface:
- props
item
The field data
<!-- MyCustomElementEditor.vue -->
<template>
<div class="my-custom-element-editor">
<h3>My Custom Element Editor</h3>
<!-- Add your custom editor UI here -->
</div>
</template>
<script>
export default {
// IMPORTANT: This name must match the componentName in PHP
name: 'MyCustomElementEditor',
props: ['item'],
mounted() {
// The field data is available in this.item
// Do your initialization work here
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Vue Component for Custom Settings
Create a Vue component for the custom settings:
- props
listItem
Field settings options data -object
editItem
Field data -object
form_items
All fields data -array
<!-- MyCustomSettingsComponent.vue -->
<template>
<div class="my-custom-settings">
<!-- Add your custom settings UI here -->
<!-- Example settings -->
<el-form-item label="Option One">
<el-input v-model="editItem.settings.custom_settings.option_one"
placeholder="Enter Option One"></el-input>
</el-form-item>
<el-form-item label="Option Two">
<el-input v-model="editItem.settings.custom_settings.option_two"
placeholder="Enter Option Two"></el-input>
</el-form-item>
<el-form-item>
<el-checkbox v-model="editItem.settings.custom_settings.show_extra"
:true-label="'show'"
:false-label="'hide'">
Show Extra Content
</el-checkbox>
</el-form-item>
</div>
</template>
<script>
export default {
// IMPORTANT: This name must match the componentName in PHP
name: 'MyCustomSettingsComponent',
props: ['listItem', 'editItem', 'form_items'],
mounted() {
// Initialize settings if they don't exist
if (!this.editItem.settings.custom_settings) {
this.$set(this.editItem.settings, 'custom_settings', {
option_one: '',
option_two: '',
show_extra: 'hide'
});
}
}
}
</script>
<style>
.my-custom-settings {
padding: 10px;
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# Registering the Custom Field
Finally, initialize your custom field in your plugin:
<?php
// In your plugin's main file or a separate file
add_action('fluentform/loaded', function () {
require_once 'path/to/MyCustomElement.php';
new \MyPlugin\CustomFields\MyCustomElement();
});
2
3
4
5
6
7
# Debugging Custom Components
If your custom components aren't appearing in the editor, check the following:
- Open your browser's developer console to look for errors
- Verify that your JavaScript file is being loaded (check the Network tab)
- Confirm that the component names match exactly between PHP and JavaScript
- Add console.log statements in your JavaScript to verify component registration
- Check that the Vue components are properly exported and imported
A common debugging technique is to add this to your JavaScript:
console.log('Available components:', Object.keys(window.ffEditorOptionsCustomComponents));
# Complete Example
The complete implementation includes:
- A PHP class that extends
BaseFieldManager
- JavaScript file to register Vue components
- Vue components for the editor interface and settings
- Initialization code to register the custom field
This approach allows you to create fully customized form fields with their own settings and rendering logic, while integrating seamlessly with the Fluent Forms editor.
# Key Points to Remember
- The
getComponent()
method defines the structure of your custom field getGeneralEditorElements()
andgetAdvancedEditorElements()
control which settings appear in the editorgetEditorCustomizationSettings()
defines custom settings components- The
render()
method outputs the HTML for your field on the frontend - Custom Vue components must be registered in the
ffEditorOptionsCustomComponents
global object - The
componentName
property in your field definition must match the name of your Vue component - Use
$set
in Vue components to ensure reactivity when adding new properties - Always initialize your settings objects to prevent "undefined" errors
# Frontend Rendering Considerations
When rendering your custom field on the frontend, consider:
- Accessibility: Use ARIA attributes and proper semantic HTML
- Mobile responsiveness: Test on different screen sizes
- Validation: Implement both client-side and server-side validation
- Error handling: Display meaningful error messages
- Styling: Use CSS that works with the form's theme
# All Together
Your ideal implementation will look like this if you want to use both custom validation and custom response render:
class MyCustomElement extends \FluentForm\App\Services\FormBuilder\BaseFieldManager
{
public function __construct()
{
parent::__construct(
'my_custom_element',
'My Custom Element',
['custom', 'element', 'advanced'],
'general'
);
add_filter('fluentform/response_render_' . $this->key, array($this, 'renderResponse'), 10, 4);
add_filter('fluentform/validate_input_item_' . $this->key, array($this, 'validateInput'), 10, 5);
// Enqueue assets
add_action('fluentform/loading_editor_assets', array($this, 'enqueueEditorAssets'));
}
// Implement required methods...
public function enqueueEditorAssets()
{
wp_enqueue_script(
'my-custom-element-editor',
MY_PLUGIN_URL . 'assets/js/custom-element.js',
[],
'1.0.0',
true
);
}
}
/*
* Initialize the class
*/
add_action('fluentform/loaded', function () {
new MyCustomElement();
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
By following this comprehensive guide, you can create powerful custom form fields that integrate perfectly with the Fluent Forms editor interface.