0

I am writing a component that allows the user to designate the API names of the fields they want to see in a related list. I am trying to pass these API field names into the "fields" parameter of a lightning record component, but I am getting the following error. If I hard code the field names in, everything works fine. It seems that in JavaScript I need to do something to convert the @api values to a string, even though it seems to be working just fine as a string when I am successfully passing it to my aura enabled Apex class.

Basically it will not accept const fields in my JS.

Error: "[Cannot read property 'fieldApiName' of undefined]"

It seems to me that "fieldName1" is getting defined by "@api fieldName1", but I must be missing something. What am I missing?

Many thanks

enter image description here

<template>  
      
    <lightning-card title={strTitle} icon-name="standard:record"> 

        <lightning-button class="slds-m-around_medium" label="New" onclick={navigateToNew}></lightning-button>

        <div class="slds-m-around_medium">  
  
            <div if:true={records.data}>  
  
                <template for:each={records.data} for:item="rec">    
  
                    <div key={rec.Id} class="slds-box">  
  
                        <lightning-record-form 
                            record-id={rec.Id}   
                            object-api-name={objectName}   
                            fields={fields}    
                            mode="view"  
                            columns="3">                                                
                        </lightning-record-form> 
  
                    </div>  
                      
                </template>  
  
            </div>  
  
        </div>  
  
    </lightning-card>  
      
</template>

JS

import { LightningElement, api, wire } from 'lwc';  
import fetchRecords from '@salesforce/apex/RelatedListController.fetchRecords';
import { NavigationMixin } from 'lightning/navigation'; 
  
export default class RelatedList extends NavigationMixin(LightningElement) {   
  
    @api objectName;   
    @api parentFieldAPIName;  
    @api recordId;  
    @api strTitle;
    @api fieldName1; 
    @api fieldName2;
    @api fieldName3;
    @api fieldName4;
    @api fieldName5;
    @api fieldName6;  

    @track vals;

 connectedCallback() {
    const fields = [];
    
    if (this.fieldName1) {
        fields.push(this.fieldName1);
    }
    if (this.fieldName2) {
        fields.push(this.fieldName2);
    }
    if (this.fieldName3) {
        fields.push(this.fieldName3);
    }       
    
    if (fields.length) {
        this.vals = this.recordId + ',' + this.objectName + ',' +   
           this.parentFieldAPIName + ',' + this.fieldName1 + ',' +
           this.fieldName2 + ',' + this.fieldName3;
    }
 }
      
    @wire(fetchRecords, {listValues: '$vals'})  
    records;

    navigateToNew() {
        this[NavigationMixin.Navigate]({
            type: 'standard__objectPage',
            attributes: {
                objectApiName: 'POC_Use_Case__c',
                actionName: 'new'
            }        
        });
    }
}

Meta

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="RelatedList">
    <apiVersion>45.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__RecordPage</target>
    </targets>
    <targetConfigs>
        <targetConfig targets="lightning__RecordPage">
            <property name="strTitle" type="String" label="Title" description="Enter the title"/>
            <property name="objectName" type="String" label="Object Name" description="Enter the object API name"/>
            <property name="parentFieldAPIName" type="String" label="Parent Field API Name" description="Enter the parent field API Name"/>
            <property name="fieldName1" type="String" label="Field 1 API Name" description="Enter the field API name"/>
            <property name="fieldName2" type="String" label="Field 2 API Name" description="Enter the field API name"/>
            <property name="fieldName3" type="String" label="Field 3 API Name" description="Enter the field API name"/>
            <property name="fieldName4" type="String" label="Field 4 API Name" description="Enter the field API name"/>
            <property name="fieldName5" type="String" label="Field 5 API Name" description="Enter the field API name"/>
            <property name="fieldName6" type="String" label="Field 6 API Name" description="Enter the field API name"/>
        
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>

Apex

public class RelatedListController {  
  
    @AuraEnabled(cacheable=true)  
    public static List <sObject> fetchRecords(String listValues){      
        
        List <String> strList = listValues.split(',');  
        system.debug('values are ' + strList);  
          
        if (strList.size() == 6) {  
          
            String recordId = strList.get(0);  
            String objectName = strList.get(1);  
            String parentFieldAPIName = strList.get(2);  
            String fieldName1 = strList.get(3);  
            String fieldName2 = strList.get(4); 
            String fieldName3 = strList.get(5); 
              
            String strSOQL = 'SELECT Id, '+fieldName1+', '+fieldName2+', '+fieldName3+
                             ' FROM '+objectName+' WHERE '+parentFieldAPIName+' = \''+recordId+'\' ';
            
            system.debug('-------->strSOQL = '+strSOQL);                        
            
            return Database.query(strSOQL);  
              
        } else   
            return null;            
    }          
}

1 Answer 1

1

Fundamentally, API property values are assigned asynchronously. What you have tried to do won't work.

However, the API properties are guaranteed to have been set up by the time the connectedCallback life cycle hook is called.

You should therefore postpone setting up the fields array until the lifecycle hook is invoked, and ensure that this also sets up "vals". The latter is appropriate to ensure that "vals" is undefined until that point so as to prevent the wire from being prematurely executed (all dynamic properties to a wire must have non-undefined values for the wire to be invoked.

You're also using "fields" in the form in the template as well, so we need to make sure that gets set up right.

@track vals;

connectedCallback() {
    if (!this.objectName) {
        // Need the object name in order to fetch the data
        return;
    }

    const fields = [];

    if (this.fieldName1) {
        fields.push(this.fieldName1);
    }

    if (this.fieldName2) {
        fields.push(this.fieldName2);
    }

    if (this.fieldName3) {
        fields.push(this.fieldName3);
    }

    if (fields.length) {
        // Generate the parameter for the wire. The join ensures that only
        // those fields actually defined are included in the "fetchRecord"
        // parameters. This assumes the wire takes a comma separated string
        // of parameters, but if it actually takes an array then this assignment
        // needs to be changed
        this.vals = this.recordId + ',' + this.objectName + ',' +   
           this.parentFieldAPIName + ',' + fields.join(',');

        // Need to set up the "fields" for the rendering too
        this.fields = fields;
    }
}

At this point the "vals" dynamic property is set up and therefore the wire will then fire.

11
  • Thanks, I changed the code as you suggested but I'm still getting "Cannot read property 'fieldApiName' of undefined", slightly different error but less clear. I updated my code in the description above.
    – number41
    Sep 9, 2020 at 4:02
  • @number41 sorry, I wasn't clear. The "vals" needs to be generated using the "fields" array. I have updated the answer. If this still doesn't work it could be that the way "vals" is generated isn't appropriate for your wire. Does the wire take an array of parameters or a comma separated string? The way the code works here, a single parameter is passed that is a string rather than array.
    – Phil W
    Sep 9, 2020 at 6:18
  • Thanks, still getting the same error though. I did update the JavaScript to use the "field" array. I added my Apex class to the description so that you can see it, it does take a comma separated string, so it would seem that it should be working. The "vals" never seemed to be the problem though, I can see in the developer console that the Apex is running properly, so "vals" is getting successfully constructed and passed to the Apex class.
    – number41
    Sep 9, 2020 at 15:58
  • It seems that the problem is still in the way that the "fields" is being constructed, in that the error is still "Cannot read property 'fieldApiName' of undefined" in the lightning app builder. Interestingly though, I only get the error when I add all six properties in the UI lightning app builder.
    – number41
    Sep 9, 2020 at 15:58
  • Have you considered having a single API property for all field names that is a comma-separated list string? The original implementation (not using the connectedCallback) would definitely not have worked in populating "vals" and would have caused inappropriate invocation of the wire without the correct value. There's nowhere in the code you have posted where "fieldApiName" is accessed. Are you sure this isn't in some other code not shown here?
    – Phil W
    Sep 9, 2020 at 16:05

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .