0

I have a LWC I've built that will get field describe information from the apex controller via a wire function and there is a combobox that will list the fields so that the user can filter one specific field at a time. An additional button called "Show All Fields" will toggle all fields being visible.

The idea of the component is that on load it should get all the describe info, populate the field combobox but not show the datatable. Once a field is selected or the Show all fields button is clicked, the relevant fields will be displayed. The problem I have is that when the first time the combo box changes from --None-- option to a different field, the entire datatable is rendered for one second then the one field is filtered and shown. It should not be showing the full table even for a short time. It should simply render only one line. If anyone can spot where the issue is in my code?

HTML

<template>
    <template if:true={loading}>
        <lightning-spinner alternative-text="Loading" size="medium"></lightning-spinner>
    </template>

    <lightning-card title="Object Metadata Info: Fields" icon-name="standard:data_model">
            <lightning-layout multiple-rows="true" vertical-align="end">
            <lightning-layout-item padding="around-small" >
                <lightning-combobox class="slds-m-left_small" name="fieldSelect" label="Select Field" value={selectedField} options={fieldSelectOptions} onchange={handleSelectedField} ></lightning-combobox>
            </lightning-layout-item>
            <lightning-layout-item padding="around-small" >
                <lightning-button-stateful label-when-off="Show All Fields" label-when-on="Hide All Fields" title="Toggle view all fields" icon-name-when-off="utility:preview" icon-name-when-on="utility:hide" onclick={handleToggleClick} selected={showAllFields}></lightning-button-stateful>
            </lightning-layout-item>      
            <!-- lightning data tables with search results-->
            <template if:true={fieldDescribeList}>    
                <lightning-layout-item padding="around-small" size="12"> 
                    <lightning-datatable key-field="Id" data={fieldDescribeList} columns={columns} column-widths-mode="auto" resize-column-disabled hide-checkbox-column=true ></lightning-datatable>
                </lightning-layout-item>
            </template>
        </lightning-layout>

    </lightning-card>
</template>

JS

import { LightningElement, api, wire } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import getFieldsDescribeList from '@salesforce/apex/ObjectMetadataViewerController.getFieldsDescribeInfo';

export default class objectMetadataViewer extends LightningElement {
    @api recordId;
    title;
    message;
    variant;
    none                    = '--None--';
    showAllFields           = false;
    selectedField           = '';
    fieldSelectOptions      = []
    fieldsDescribeResult    = [];


    @wire(getFieldsDescribeList, {pId: '$recordId', pSelectedField: '$selectedField'})
    wiredFieldDescribe({ error, data }) {
        if (data) {
            //update the datatable
            this.fieldsDescribeResult = data;
            console.log('fieldsDescribeResult...' + this.fieldsDescribeResult);

            if(this.fieldSelectOptions.length === 0){
                //build select options if empty
                this.fieldSelectOptions = this.fieldsDescribeResult.map(field=>{ 
                    let JSONval = {label: field.label, value: field.name};
                    return JSONval;
                });
                this.fieldSelectOptions.unshift({ label: '--None--', value: '' });
            }
                
        } else if (error) {
            this.fieldsDescribeResult = undefined;
            
            //show error toast notification
            this.title = 'Search Results';
            this.message = 'Error fetching field info for record: ' + error.body.message;
            this.variant = 'info';
            this.showNotification();
        }
    }

    get fieldDescribeList (){
        //if the selected field is not none, map to list
        console.log('selectedField: ' + this.selectedField);
        if(this.selectedField != '' || this.showAllFields === true){
            return this.fieldsDescribeResult;
        }else{
            return undefined;
        }
    }

    //Define the datatable columns
    columns = [
        { label: 'Label',     fieldName: 'label',     type:'text' },
        { label: 'API Name',  fieldName: 'name',      type:'text' },
        { label: 'Type',      fieldName: 'fieldtype', type:'text' },
        { label: 'Is Custom', fieldName: 'isCustom',  type:'text'} ,
    ]

    //get the selected field name value
    handleSelectedField(event){
        //get the selected field option
        this.selectedField = event.target.value;
        this.showAllFields = false;
    }

    handleToggleClick (event){
        if(this.selectedField != ''){
            this.selectedField = '';
        }
        this.showAllFields = !this.showAllFields;
    }
    
    //Shows a toast msg with custom values
    showNotification() {
        const evt = new ShowToastEvent({
            title:   this.title,
            message: this.message,
            variant: this.variant,
        });
        this.dispatchEvent(evt);
    }

}

The apex controller is working as expected, it returns a single schemaSObjectfield record or a list of all the field describes for that record. I won't include it to keep the post size down.

1
  • When you change the field from none to something else your wire method takes time to get the new data in the datatable but before that your if condition in get method becomes true and shows the data with the previous data in this.fieldsDescribeResult. I think you should make this.fieldsDescribeResult=undefined; when you are selecting the value so that it won't show the data its holds before fetching the data from wire method. – Shagun Sharma May 30 at 13:14
0

When you @wire an Apex method, the LWC framework will source data for the method call as soon as it has values for all parameters, and again whenever any parameter value changes. "Source data" in this case means it will check its cache for a prior call to your apex method with the same parameter values and use the cached response if found, otherwise it will call your method. However, it will never source data when any parameter is the special javascript value undefined.

Your method has two params: pId and pSelectedField. pId is provided the binding $recordId which maps to this.recordId, which is your @api property- as long as your LWC is placed on a record home page, the LWC framework will populate that property with a value; this happens when the component is loaded into the page. Your second param, pSelectedField, is provided the binding $selectedField, which you have initialized with an empty string (''). That means that as soon as the framework sets recordId, it has a non-undefined value for all params, and will call your Apex method. If you check your apex logs you should see your controller being called before you touch the picklist. This first call will then get an empty response, which passes the if(data) test, and sets fieldsDescribeResult. Then, when you make a selection, get fieldDescribeList() sees that selectedField is no longer the empty string, and immediately renders the table before your apex method returns a result for the selected field.

To avoid this, do not set selectedField to '', but use undefined instead. This will prevent your apex method from being called until the picklist changes. undefined is the default value for class members, so you can just declare it:

selectedField;

Additionally, change any code that sets or tests selectedField with '' to use undefined instead.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.