Open ps91 opened 1 month ago
Could you possibly provide a codesandbox to illustrate what you are experiencing? Also, what version of formio.js are you using?
I was about to open a ticket with the same issue. I'm using:
Custom components work well in forms, subforms, inside datagrids in the main form, but when you add the custom component to a datagrid and reference that form as a subform inside another form, the custom component is not rendered. If you put a breakpoint in the ngOnChanges event of the custom component, you'll see that the breakpoint isn't hit.
<div class="mb-3">
<p-listbox [options]="items" [(ngModel)]="selected" [optionLabel]="bindLabel" [optionValue]="bindValue"
[filter]="filter" [multiple]="multipleValues" [checkbox]="checkbox" [disabled]="disabled"
[listStyle]="{'max-height':maxHeight}" (onChange)="onChange($event)">
</p-listbox>
</div>
import { Component, EventEmitter, Input, Output, SimpleChanges } from '@angular/core';
import { ApiService } from '@hyperflowX/core';
import { FormioCustomComponent } from '@formio/angular';
@Component({
selector: 'hfx-listbox',
templateUrl: './listbox.component.html',
styleUrls: ['./listbox.component.scss']
})
export class ListboxComponent implements FormioCustomComponent<any> {
@Input() value: any;
@Output() valueChange = new EventEmitter<any>();
@Input() disabled = false;
@Input() label: string;
@Input() bindLabel: string;
@Input() bindValue: string;
@Input() endpoint: string;
@Input() method: string;
@Input() dataPath: string;
@Input() checkbox: boolean;
@Input() multipleValues: boolean;
@Input() filter: boolean;
@Input() maxHeight: string;
selected: any;
items = [];
constructor(private apiService: ApiService<any>) {
}
ngOnChanges(changes: SimpleChanges): void {
if (changes.endpoint) {
this.getData();
}
if (this.value) {
if (this.multipleValues) {
// To avoid the validation error "value must not be an array" the value can't be an array
// so the value is saved in an object in the property 'items'
this.selected = this.value.items || [];
} else {
this.selected = this.value;
}
}
}
onChange(data) {
this.updateValue(this.selected);
}
updateValue(newValue: any) {
// To avoid the validation error "value must not be an array" the value can't be an array
// so the value is saved in an object in the property 'items'
this.value = this.multipleValues ? { items: newValue } : newValue;
this.valueChange.emit(this.value);
}
getData(data?: any) {
if (this.endpoint) {
this.apiService.getData(this.method, this.endpoint, null)
.subscribe(response => {
if (response) {
this.items = this.dataPath && response.data[this.dataPath]
? response.data[this.dataPath]
: response.data;
}
});
} else {
let itemsVariable = this.dataPath ? data[this.dataPath] : data;
// Verifica se a propriedade é composta
if (this.dataPath.includes('.')) {
const props = this.dataPath.split('.');
let value = data;
for (const prop of props) {
value = value[prop];
}
itemsVariable = value;
}
if (itemsVariable != null) {
this.items = [...itemsVariable];
}
}
}
}
import { Injector } from '@angular/core';
import { FormioCustomComponentInfo, registerCustomFormioComponent, Components } from '@formio/angular';
import { ListboxComponent } from './listbox.component';
const COMPONENT_OPTIONS: FormioCustomComponentInfo = {
type: 'listbox', // custom type. Formio will identify the field with this type.
selector: 'hfx-listbox', // custom selector. Angular Elements will create a custom html tag with this selector
title: 'Listbox', // Title of the component
group: 'basic', // Build Group
icon: 'fa fa-dot-circle-o', // Icon
editForm: customEditForm,
// template: 'input', // Optional: define a template for the element. Default: input
// changeEvent: 'valueChange', // Optional: define the changeEvent
// when the formio updates the value in the state. Default: 'valueChange'
// editForm: Components.components.textfield.editForm,
// Optional: define the editForm of the field. Default: the editForm of a textfield
// documentation: '', // Optional: define the documentation of the field
// weight: 0, // Optional: define the weight in the builder group
// schema: {}, // Optional: define extra default schema for the field
// extraValidators: [], // Optional: define extra validators for the field
// emptyValue: null, // Optional: the emptyValue of the field
};
export function registerListboxComponent(injector: Injector) {
try {
registerCustomFormioComponent(COMPONENT_OPTIONS, ListboxComponent, injector);
} catch (error) {
// already registered
}
}
export function customEditForm() {
const form = Components.components.textfield.editForm();
form.components[0].components.push(
{
label: 'CONFIG',
key: 'selectConfiguration',
weight: 30,
components: [
{
weight: 10,
type: 'textfield',
key: 'customOptions.endpoint',
label: 'Endpoint Url',
placeholder: 'Endpoint',
input: true,
}, {
weight: 20,
type: 'select',
key: 'customOptions.method',
label: 'Method',
input: true,
dataSrc: 'values',
data: {
values: [
{ value: 'get', label: 'Get' },
{ value: 'post', label: 'Post' },
],
},
}, {
weight: 30,
type: 'textfield',
key: 'customOptions.dataPath',
label: 'Data Path',
placeholder: 'Data Path',
tooltip: 'The property within the source data where iterable items reside. For example: result.items',
input: true,
}, {
weight: 40,
type: 'textfield',
key: 'customOptions.bindLabel',
label: 'Bind Label',
placeholder: 'Bind Label',
input: true,
validate: {
required: true,
},
}, {
weight: 50,
type: 'textfield',
key: 'customOptions.bindValue',
label: 'Bind Value',
placeholder: 'Bind Value',
input: true,
validate: {
required: true,
},
}, {
weight: 60,
type: 'checkbox',
key: 'customOptions.filter',
label: 'Filter',
input: true,
}, {
weight: 70,
type: 'checkbox',
key: 'customOptions.multipleValues',
label: 'Multiple values',
input: true,
}, {
weight: 80,
type: 'checkbox',
key: 'customOptions.checkbox',
label: 'Checkbox',
input: true,
'conditional': {
'eq': 'true',
'when': 'customOptions.multipleValues',
'show': 'true',
},
}, {
weight: 90,
type: 'textfield',
key: 'customOptions.maxHeight',
label: 'Max height',
tooltip: 'Max height css style',
input: true
}
],
});
return form;
}
Create a form, add a datagrid and add the component the datagrid. Create a second form and reference the previous form as a subform.
If we had a reproducible example from something like stackblitz it would help me get a meaningful review.
Environment
Please provide as many details as you can:
Steps to Reproduce
Expected behavior
The custom component should display correctly.
Observed behavior
The custom component does not display, and after debugging, I found that the custom component is never triggered.