Form.io 自定义布局组件
Form.io Custom Layout Component
我正在使用 Form.io v3.27.1,我正在尝试创建一个自定义布局组件 - 特别是手风琴 - 我大部分时间都在使用 CheckMatrix 组件示例中提供的概念。
我可以让手风琴组件显示在工具箱中,我可以将它拖到表单上,用自定义编辑表单配置它等等。我可以保存它并呈现 Bootstrap 主题手风琴完美。
然而,它不做的是允许我将其他组件拖放到内容区域,类似于其他布局组件的行为(即 Tabs
、Columns
、Fieldset
等)。
我假设通过浏览其他布局控件的源代码我需要扩展 NestedComponent
来代替 BaseComponent
,但我还没有能够做到这一点。
我觉得我忽略了一些小事。我似乎无法弄清楚如何呈现接受其他 Form.io 组件作为子组件的布局组件。
任何人有一个工作示例或建议我可以尝试让它工作吗?提前感谢您的帮助!
import BaseComponent from 'formiojs/components/base/Base';
import NestedComponent from 'formiojs/components/nested/NestedComponent';
import Components from 'formiojs/components/Components';
import * as editForm from './Accordian.form';
export default class AccordionComponent extends BaseComponent {
/**
* Define what the default JSON schema for this component is. We will derive from the BaseComponent
* schema and provide our overrides to that.
* @return {*}
*/
static schema() {
return BaseComponent.schema({
type: 'accordion',
label: 'Sections',
input: false,
key: 'accordion',
persistent: false,
components: [{
label: 'Section 1',
key: 'section1',
components: []
}]
});
}
/**
* Register this component to the Form Builder by providing the "builderInfo" object.
*/
static get builderInfo() {
return {
title: 'Accordion',
group: 'custom',
icon: 'fa fa-tasks',
weight: 70,
schema: AccordionComponent.schema()
};
}
/**
* Tell the renderer how to build this component using DOM manipulation.
*/
build() {
this.element = this.ce('div', {
class: `form-group formio-component formio-component-accordion ${this.className}`
}, [
this.ce('app-formio-accordian', {
components: JSON.stringify(this.component.components)
})
]);
}
elementInfo() {
return super.elementInfo();
}
getValue() {
return super.getValue();
}
setValue(value) {
super.setValue(value);
}
}
// Use the table component edit form.
AccordionComponent.editForm = editForm.default;
// Register the component to the Formio.Components registry.
Components.addComponent('accordion', AccordionComponent);
<div class="accordion" id="formioAccordionPreview" *ngIf="components">
<div class="card" *ngFor="let component of components; first as isFirst">
<div class="card-header" id="heading-{{component.key}}">
<h2 class="mb-0">
<button type="button" class="btn btn-link" data-toggle="collapse" data-target="#collapse-{{component.key}}">{{component.label}}</button>
</h2>
</div>
<div id="collapse-{{component.key}}" class="collapse" [class.show]="isFirst" aria-labelledby="heading-{{component.key}}" data-parent="#formioAccordionPreview">
<div class="card-body">
<p>I should be able to 'Drag and Drop a form component' here.</p>
</div>
</div>
</div>
</div>
Accordion
在功能上等同于tabs控件,即headered content
方便切换和选择。构造 accordion control
的答案是扩展 Form.io
中内置的 TabsComponent
并覆盖通过 DOM 操作构造元素的 createElement
方法。其他几个覆盖(schema
和 builderInfo
)将元数据提供回 FormBuilder
和 voila!
accordion.js
import TabsComponent from 'formiojs/components/tabs/Tabs';
import * as editForm from './Accordian.form';
export default class AccordionComponent extends TabsComponent {
/**
* Define what the default JSON schema for this component is. We will derive from the BaseComponent
* schema and provide our overrides to that.
* @return {*}
*/
static schema() {
return TabsComponent.schema({
type: 'accordion',
label: 'Sections',
input: false,
key: 'accordion',
persistent: false,
components: [{
label: 'Section 1',
key: 'section1',
type: 'tab',
components: []
}]
});
}
/**
* Register this component to the Form Builder by providing the "builderInfo" object.
*/
static get builderInfo() {
return {
title: 'Accordion',
group: 'custom',
icon: 'fa fa-tasks',
weight: 70,
schema: AccordionComponent.schema()
};
}
/**
* Tell the builder how to build this component using DOM manipulation.
*/
createElement() {
this.tabs = [];
this.tabLinks = [];
this.bodies = [];
this.accordion = this.ce('div', {
id: `accordion-${this.id}`
});
var _this = this;
this.component.components.forEach(function (tab, index) {
var isFirst = index === 0;
var tabLink = _this.ce('a', {
class: 'card-link',
data_toggle: 'collapse',
href: `#collapse-${tab.key}`
}, tab.label);
_this.addEventListener(tabLink, 'click', function (event) {
event.preventDefault();
_this.setTab(index);
});
var header = _this.ce('div', {
class: 'card-header'
}, [tabLink]);
var tabPanel = _this.ce('div', {
class: 'tab-pane',
role: 'tabpanel',
tabLink: tabLink
});
var tabContent = _this.ce('div', {
class: 'tab-content'
}, [tabPanel]);
var body = _this.ce('div', {
class: 'card-body',
id: tab.key
}, [tabContent]);
var content = _this.ce('div', {
id: `collapse-${tab.key}`,
class: 'collapse'.concat(isFirst ? ' show' : ''),
data_parent: `#accordion-${_this.id}`
}, [body]);
var card = _this.ce('div', {
class: 'card'
}, [header, body]);
_this.tabLinks.push(header);
_this.tabs.push(tabPanel);
_this.bodies.push(body);
_this.accordion.appendChild(card);
});
if (this.element) {
this.appendChild(this.element, [this.accordion]);
this.element.className = this.className;
return this.element;
}
this.element = this.ce('div', {
id: this.id,
class: this.className
}, [this.accordion]);
this.element.component = this;
return this.element;
}
setTab(index, state) {
super.setTab(index, state);
var _this = this;
if (this.bodies) {
this.bodies.forEach(function (body) {
body.style.display = 'none';
});
_this.bodies[index].style.display = 'block';
}
}
}
AccordionComponent.editForm = editForm.default;
手风琴在编辑表单中需要一些不同的配置,所以我还包括了编辑表单的显示选项卡的定义:
Accordion.edit.display.js
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _default = [{
key: 'components',
type: 'datagrid',
input: true,
label: 'Sections',
weight: 50,
reorder: true,
components: [{
type: 'textfield',
input: true,
key: 'label',
label: 'Label'
}, {
type: 'textfield',
input: true,
key: 'key',
label: 'Key',
allowCalculateOverride: true,
calculateValue: {
_camelCase: [{
var: 'row.label'
}]
}
}]
}];
exports.default = _default;
然后是引用自定义显示选项卡元素的表单定义覆盖:
Accordion.form.js
"use strict";
require("core-js/modules/es.array.concat");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = _default;
var _NestedComponent = _interopRequireDefault(require("../../../../../../../../../node_modules/formiojs/components/nested/NestedComponent.form"));
var _AccordianEdit = _interopRequireDefault(require("./editForm/Accordian.edit.display"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _default() {
for (var _len = arguments.length, extend = new Array(_len), _key = 0; _key < _len; _key++) {
extend[_key] = arguments[_key];
}
return _NestedComponent.default.apply(void 0, [[{
key: 'display',
components: _AccordianEdit.default
}]].concat(extend));
}
Accordion component
的文件结构如下所示:
然后我只需要在我的 Angular 项目中注册该组件:
app.component.ts
import { Component } from '@angular/core';
import { Formio } from 'formiojs';
import AccordionComponent from './modules/utility/form-shell/cap-forms/cap-form-designer/components/accordian/accordian';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
title = 'app';
constructor() {
Formio.registerComponent('accordion', AccordionComponent);
}
}
我正在使用 Form.io v3.27.1,我正在尝试创建一个自定义布局组件 - 特别是手风琴 - 我大部分时间都在使用 CheckMatrix 组件示例中提供的概念。
我可以让手风琴组件显示在工具箱中,我可以将它拖到表单上,用自定义编辑表单配置它等等。我可以保存它并呈现 Bootstrap 主题手风琴完美。
然而,它不做的是允许我将其他组件拖放到内容区域,类似于其他布局组件的行为(即 Tabs
、Columns
、Fieldset
等)。
我假设通过浏览其他布局控件的源代码我需要扩展 NestedComponent
来代替 BaseComponent
,但我还没有能够做到这一点。
我觉得我忽略了一些小事。我似乎无法弄清楚如何呈现接受其他 Form.io 组件作为子组件的布局组件。
任何人有一个工作示例或建议我可以尝试让它工作吗?提前感谢您的帮助!
import BaseComponent from 'formiojs/components/base/Base';
import NestedComponent from 'formiojs/components/nested/NestedComponent';
import Components from 'formiojs/components/Components';
import * as editForm from './Accordian.form';
export default class AccordionComponent extends BaseComponent {
/**
* Define what the default JSON schema for this component is. We will derive from the BaseComponent
* schema and provide our overrides to that.
* @return {*}
*/
static schema() {
return BaseComponent.schema({
type: 'accordion',
label: 'Sections',
input: false,
key: 'accordion',
persistent: false,
components: [{
label: 'Section 1',
key: 'section1',
components: []
}]
});
}
/**
* Register this component to the Form Builder by providing the "builderInfo" object.
*/
static get builderInfo() {
return {
title: 'Accordion',
group: 'custom',
icon: 'fa fa-tasks',
weight: 70,
schema: AccordionComponent.schema()
};
}
/**
* Tell the renderer how to build this component using DOM manipulation.
*/
build() {
this.element = this.ce('div', {
class: `form-group formio-component formio-component-accordion ${this.className}`
}, [
this.ce('app-formio-accordian', {
components: JSON.stringify(this.component.components)
})
]);
}
elementInfo() {
return super.elementInfo();
}
getValue() {
return super.getValue();
}
setValue(value) {
super.setValue(value);
}
}
// Use the table component edit form.
AccordionComponent.editForm = editForm.default;
// Register the component to the Formio.Components registry.
Components.addComponent('accordion', AccordionComponent);
<div class="accordion" id="formioAccordionPreview" *ngIf="components">
<div class="card" *ngFor="let component of components; first as isFirst">
<div class="card-header" id="heading-{{component.key}}">
<h2 class="mb-0">
<button type="button" class="btn btn-link" data-toggle="collapse" data-target="#collapse-{{component.key}}">{{component.label}}</button>
</h2>
</div>
<div id="collapse-{{component.key}}" class="collapse" [class.show]="isFirst" aria-labelledby="heading-{{component.key}}" data-parent="#formioAccordionPreview">
<div class="card-body">
<p>I should be able to 'Drag and Drop a form component' here.</p>
</div>
</div>
</div>
</div>
Accordion
在功能上等同于tabs控件,即headered content
方便切换和选择。构造 accordion control
的答案是扩展 Form.io
中内置的 TabsComponent
并覆盖通过 DOM 操作构造元素的 createElement
方法。其他几个覆盖(schema
和 builderInfo
)将元数据提供回 FormBuilder
和 voila!
accordion.js
import TabsComponent from 'formiojs/components/tabs/Tabs';
import * as editForm from './Accordian.form';
export default class AccordionComponent extends TabsComponent {
/**
* Define what the default JSON schema for this component is. We will derive from the BaseComponent
* schema and provide our overrides to that.
* @return {*}
*/
static schema() {
return TabsComponent.schema({
type: 'accordion',
label: 'Sections',
input: false,
key: 'accordion',
persistent: false,
components: [{
label: 'Section 1',
key: 'section1',
type: 'tab',
components: []
}]
});
}
/**
* Register this component to the Form Builder by providing the "builderInfo" object.
*/
static get builderInfo() {
return {
title: 'Accordion',
group: 'custom',
icon: 'fa fa-tasks',
weight: 70,
schema: AccordionComponent.schema()
};
}
/**
* Tell the builder how to build this component using DOM manipulation.
*/
createElement() {
this.tabs = [];
this.tabLinks = [];
this.bodies = [];
this.accordion = this.ce('div', {
id: `accordion-${this.id}`
});
var _this = this;
this.component.components.forEach(function (tab, index) {
var isFirst = index === 0;
var tabLink = _this.ce('a', {
class: 'card-link',
data_toggle: 'collapse',
href: `#collapse-${tab.key}`
}, tab.label);
_this.addEventListener(tabLink, 'click', function (event) {
event.preventDefault();
_this.setTab(index);
});
var header = _this.ce('div', {
class: 'card-header'
}, [tabLink]);
var tabPanel = _this.ce('div', {
class: 'tab-pane',
role: 'tabpanel',
tabLink: tabLink
});
var tabContent = _this.ce('div', {
class: 'tab-content'
}, [tabPanel]);
var body = _this.ce('div', {
class: 'card-body',
id: tab.key
}, [tabContent]);
var content = _this.ce('div', {
id: `collapse-${tab.key}`,
class: 'collapse'.concat(isFirst ? ' show' : ''),
data_parent: `#accordion-${_this.id}`
}, [body]);
var card = _this.ce('div', {
class: 'card'
}, [header, body]);
_this.tabLinks.push(header);
_this.tabs.push(tabPanel);
_this.bodies.push(body);
_this.accordion.appendChild(card);
});
if (this.element) {
this.appendChild(this.element, [this.accordion]);
this.element.className = this.className;
return this.element;
}
this.element = this.ce('div', {
id: this.id,
class: this.className
}, [this.accordion]);
this.element.component = this;
return this.element;
}
setTab(index, state) {
super.setTab(index, state);
var _this = this;
if (this.bodies) {
this.bodies.forEach(function (body) {
body.style.display = 'none';
});
_this.bodies[index].style.display = 'block';
}
}
}
AccordionComponent.editForm = editForm.default;
手风琴在编辑表单中需要一些不同的配置,所以我还包括了编辑表单的显示选项卡的定义:
Accordion.edit.display.js
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _default = [{
key: 'components',
type: 'datagrid',
input: true,
label: 'Sections',
weight: 50,
reorder: true,
components: [{
type: 'textfield',
input: true,
key: 'label',
label: 'Label'
}, {
type: 'textfield',
input: true,
key: 'key',
label: 'Key',
allowCalculateOverride: true,
calculateValue: {
_camelCase: [{
var: 'row.label'
}]
}
}]
}];
exports.default = _default;
然后是引用自定义显示选项卡元素的表单定义覆盖:
Accordion.form.js
"use strict";
require("core-js/modules/es.array.concat");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = _default;
var _NestedComponent = _interopRequireDefault(require("../../../../../../../../../node_modules/formiojs/components/nested/NestedComponent.form"));
var _AccordianEdit = _interopRequireDefault(require("./editForm/Accordian.edit.display"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _default() {
for (var _len = arguments.length, extend = new Array(_len), _key = 0; _key < _len; _key++) {
extend[_key] = arguments[_key];
}
return _NestedComponent.default.apply(void 0, [[{
key: 'display',
components: _AccordianEdit.default
}]].concat(extend));
}
Accordion component
的文件结构如下所示:
然后我只需要在我的 Angular 项目中注册该组件:
app.component.ts
import { Component } from '@angular/core';
import { Formio } from 'formiojs';
import AccordionComponent from './modules/utility/form-shell/cap-forms/cap-form-designer/components/accordian/accordian';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
title = 'app';
constructor() {
Formio.registerComponent('accordion', AccordionComponent);
}
}