So I've recently started jumping into LWC's more seriously, and I'm trying to figure it all out. I've created a custom picklist flow component which accepts a collection of strings as options (why this isn't standard I'll never know). I've managed to prevent moving onto the next page if the component is marked as "Required" using @api validate()
.
However, the issue is that if multiple of these components are on the same screen and a single one fails validation; then all of the components (even the valid ones) have their values cleared after showing a very brief loading icon. I'd like to mimic the standard required screen component/field functionality as close as possible to avoid input frustrations and inconsistencies.
Below is my current code:
component.js
import { LightningElement, api, track } from 'lwc';
export default class StringPicklistFlowComponent extends LightningElement {
@api optionsArr;
@api label;
@api isRequired = false;
@api value;
get isNotRequired(){return !this.isRequired;}
get options(){
var arr = [];
for(var i = 0; i < this.optionsArr.length; i++){
var tmp = this.optionsArr[i].split(/,\s*/g);
arr.push({label: tmp[0], value: tmp[1]});
}
return arr;
}
handleSelect(evt){
this.value = evt.detail.value;
}
@api validate(){
return {
isValid: this.isNotRequired || this.value != null,
errorMessage: "Please select an option"
}
}
}
component.html
<template>
<label for="customPicklist" class="slds-form-element__label slds-rich-text-editor__output">
<span class="slds-required" hidden={isNotRequired}>*</span>
{label}
</label>
<lightning-combobox id="customPicklist" value={value} options={options} onchange={handleSelect}>
</lightning-combobox>
</template>
Note that I did create a label manually. This is because the default functionality when the field was directly marked as "required" would constantly show a field error while the component value was null, which is a bit annoying.
Any help on this would be awesome, as this has been nagging at me for the better part of a day.
I managed to find a solution, so I'll post it here in case anybody else comes across this issue. Basically, the problem boils down to the component getting re-rendered, and hence losing it's previous data. To get around this, you can use the native browser sessionStorage
to store values, and then when re-rendered use the connectedCallback
hook to set the component value. Here's what my code looks like now:
component.js
import { LightningElement, api, track } from 'lwc';
export default class StringPicklistFlowComponent extends LightningElement {
@api isRequired = false;
@api optionsArr;
@api label;
@api value;
@api connectedCallback(){
if(!!sessionStorage[this.storageTag])
this.value = sessionStorage[this.storageTag];
else
sessionStorage[this.storageTag] = this.value;
}
get storageTag(){ return this.label + 'myTag5135'; }
get isNotRequired(){ return !this.isRequired; }
get options(){
var arr = [];
for(var i = 0; i < this.optionsArr.length; i++){
var tmp = this.optionsArr[i].split(/,\s*/g);
arr.push({label: tmp[0], value: tmp[1]});
}
return arr;
}
handleSelect(evt){
this.value = evt.detail.value;
sessionStorage[this.storageTag] = this.value;
}
@api validate(){
return {
isValid: this.isNotRequired || !!this.value,
errorMessage: "Please select an option"
};
}
}
component.html
<template>
<div>
<label for="customPicklist" class="slds-form-element__label">
<abbr class="slds-required" hidden={isNotRequired}>* </abbr>{label}
</label>
<lightning-combobox dropdown-alignment="auto" id="customPicklist" data-fieldname={label} variant="label-hidden" value={value} options={options} onchange={handleSelect}>
</lightning-combobox>
</div>
</template>
If you'll notice, I AM using the label property which isn't ideal (no enforcement that it's unique). I'd like to use the components api name in the flow so I can virtually guarantee uniqueness, however I've yet to find how to get this default property in the component. If anybody knows how, I'd really appreciate it. Anyways, I hope this helps anyone whose had similar frustrations with this wierd default limitation.