Skip to content

[Custom Components] Ability to optionally select multiple values in Custom Component  #1055

Open

Description

@muneebahmad0600

I have a dropdown-like component that loads data from my back end. I need to have different view types for my component which I am able to achieve (i.e. Single-select dropdown, Multi-select dropdown, Radio, Checkbox).
Single-select dropdown and Radio would mean the user can select 1 value and of course Multi-select and Checkbox meaning user can select multiple values.

image

when I select Multi-select in View Type dropdown and try to select the default value I get this.

image

I will paste the entire LOV component.

`
export class CustomLovComponent
implements FormioCustomComponent<string[]>, OnInit, OnChanges, OnDestroy
{
@input() value: any = '';
@input() disabled!: boolean;
@input() lovType!: string;
@input() viewType!: string;

@output() valueChange = new EventEmitter();
@output() formioEvent = new EventEmitter();

private destroy$ = new Subject();

loading = false;
baseURL: string = this.configProvider.appSettings.coreApiUrl;
lovOptionsResponse: Options[] = [];
lovOptions: Options[] = [];
showUI = true;

constructor(
private readonly configProvider: ConfigurationProvider,
private readonly http: HttpClient,
private readonly lovOptionService: LovOptionsService,
public cdr: ChangeDetectorRef
) {}

ngOnInit() {
this.getAllLovs();
}

ngOnChanges(changes: SimpleChanges): void {
this.showUI = false;
this.lovType = changes['lovType']?.currentValue;
if (this.viewType == 'dropdownMultiSelect' || this.viewType == 'checkbox') {
this.value = typeof this.value == 'string' ? [] : this.value;
}
console.log('Insomniac: value', typeof this.value, this.value);
this.showUI = true;
this.getLOVOptions();
}

ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}

private getLOVOptions(): void {
if (!this.isObjectEmpty(this.lovType)) {
this.loading = true;
this.cdr.detectChanges();
this.lovOptionService
.getOptions(this.lovType)
.pipe(debounceTime(500), takeUntil(this.destroy$))
.subscribe((res: Options[]) => {
this.lovOptionsResponse = res;
this.lovOptions = res;
this.loading = false;
this.cdr.detectChanges();
});
}
}

private getAllLovs(): void {
if (AllLOVs.length) return;
this.http
.get(${this.baseURL}api/data/v1/lovtype?pageNo=1&pageSize=999)
.pipe(takeUntil(this.destroy$))
.subscribe((res: any) => {
AllLOVs = res.items;
});
}

private isObjectEmpty(obj: any) {
if (!obj) return true;
return Object.keys(obj).length === 0;
}

filterLovOptions(filterString: any): void {
this.lovOptions = this.lovOptionsResponse.filter((lov) =>
lov.text.toLowerCase().includes(filterString.toLowerCase())
);
this.cdr.detectChanges();
}

valueUpdate(value: any): void {
this.value = value;
this.valueChange.emit(value);
this.cdr.detectChanges();
// this.formioEvent.emit({
// eventName: 'refresh',
// });
}

checkBoxUpdate(index: number, event: any): void {
const isChecked = event.target.checked;
if (isChecked) {
this.value.push(this.lovOptions[index].value);
} else {
this.value.splice(this.value.indexOf(this.lovOptions[index].value), 1);
}
this.valueChange.emit(this.value);
this.cdr.detectChanges();
}

getValueForMultiSelect(): any {
return typeof this.value == 'string' ? [] : this.value;
}
}

export let AllLOVs: LovItems[] = [];

export const COMPONENT_OPTIONS: FormioCustomComponentInfo = {
type: 'casexLov',
selector: 'casex-lov',
title: 'LOV',
group: 'customBasic',
icon: 'fa-thin fa-bars',
editForm: editForm,
};

export function registerLovComponent(injector: Injector) {
if (!customElements.get(COMPONENT_OPTIONS.selector))
registerCustomFormioComponent(
COMPONENT_OPTIONS,
CustomLovComponent,
injector
);
}

export function editForm() {
const components: ExtendedComponentSchema[] = [
{
type: 'tabs',
key: 'tabs',
components: [
{
key: 'data',
components: [
// {
// weight: 0,
// type: 'checkbox',
// label: 'Multiple Values',
// tooltip: 'Allows multiple values to be entered for this field.',
// key: 'multiple',
// input: true,
// },
{
weight: 3,
type: 'select',
input: true,
key: 'customOptions.lovType',
label: 'Lov Type',
placeholder: 'enter lov type',
tooltip:
'Enter the LOV type to be used for this field. This is the LOV type that will be used to populate the dropdown list.',
validate: {
required: true,
},
dataSrc: 'custom',
valueProperty: 'value',
data: {
custom: function custom(context: any) {
return AllLOVs.map((lov) => {
return { label: lov.id, value: lov.id };
});
},
},
},
{
type: 'select',
label: 'View Type',
key: 'customOptions.viewType',
weight: 5,
placeholder: 'View Type',
tooltip: 'View Type will determine how the options will be shown',
input: true,
defaultValue: 'dropdownSingleSelect',
data: {
values: [
{
label: 'Dropdown (Single Select)',
value: 'dropdownSingleSelect',
},
{
label: 'Dropdown (Multi Select)',
value: 'dropdownMultiSelect',
},
{
label: 'Radio',
value: 'radio',
},
{
label: 'Checkbox',
value: 'checkbox',
},
],
},
},
{
type: 'select',
label: 'Default Value',
key: 'defaultValue',
weight: 8,
placeholder: 'Default Value',
tooltip:
'The Default Value will be the value for this field, before user interaction. Having a default value will override the placeholder text.',
input: true,
},
],
label: 'Data',
weight: 10,
},
],
},
{
type: 'hidden',
key: 'type',
},
];
return {
components: components,
};
}
`

`<div *ngIf="showUI">

Select an LOV Type to view options!

{{ lov.text }}

Select an LOV Type to view options!

{{ lov.text }}

<ng-template #singleSelectDropdown>
<kendo-dropdownlist
fillMode="outline"
[data]="lovOptions"
[value]="value"
textField="text"
valueField="value"
[valuePrimitive]="true"
[filterable]="true"
[disabled]="loading"
(valueChange)="valueUpdate($event)"
(filterChange)="filterLovOptions($event)"

`

Activity

  1. muneebahmad0600 commented on Mar 5, 2024

    @muneebahmad0600
    Author

    @travist would appreciate a response on this.

  2. gyanendrasinghpanwar commented on Mar 22, 2024

    @gyanendrasinghpanwar

    @muneebahmad0600 If i remember correctly, this has to do with the property multiple. I presume u need to set it true in custom component.

  3. muneebahmad0600 commented on May 30, 2024

    @muneebahmad0600
    Author

    HI @gyanendrasinghpanwar, sorry for replying after a long time.. got sidetracked.
    I am taking 'multiple' as Input and marking it as true when viewType is 'Multiselect'.
    the problem is, even if I set the value as an array i.e. ["PH"], when the component rebuilds it gives the value as a string i.e. "PH". hence mapping this with a dropdown that accepts an array is causing issue..
    Also the original error below the component is still coming.

    image

    image

    image

    would appreciate any urgent help I can get.

  4. muneebahmad0600 commented on Jun 6, 2024

    @muneebahmad0600
    Author

    Hi @gyanendrasinghpanwar @travist @peterblazejewicz @randallknutson.
    Reaching out again to see if anyone can help on this.
    Also, I have a requirement for which I need to a dropdown of the current form Components, same as we get on "Redraw On".
    Any ideas? any help will be much appriciated.

  5. deleted a comment from lane-formio on Feb 5, 2025
  6. ZenMasterJacob20011 commented on Feb 10, 2025

    @ZenMasterJacob20011
    Contributor

    "Also, I have a requirement for which I need to a dropdown of the current form Components, same as we get on "Redraw On"."
    You can get the form components in the form.createForm callback
    Formio.createForm(document.getElementById('formio'), {
    components: [
    {
    type: 'textfield'
    }
    ]
    }, {}).then((form) => {
    console.log(form.components);
    })

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions