2

I'm trying to write a really simple component that takes the field names from the design attributes and displays only the field values, not the standard lightning-output-field.

There are two(?) ways to approach this, getRecord or return the lightning-record-form and OnLoad do some Javascript to get the field values.

I tried @sfdcfox's idea here using getRecord but no matter what I did the '$fieldsFormatted' would not work ('$fieldsFormatted' returned the exact array needed, and hard coding the array returned the exact data needed, but the two together just returned undefined).

I have gotten almost there with the lightning-record-form this great example but I am missing the last two steps (returning the field value without specifying the name, and displaying the field value). Getting the Field Value came from this example and both were from @salesforce-sas.

HTML

<template>
    <lightning-card title="Design Attribute Demo" icon-name="custom:custom19">
        <template if:true={fieldsFormatted}>
            <lightning-record-form record-id={recordId} object-api-name={objectApiName} mode="view" 
                fields={fieldsFormatted} onload={handleOnLoad}>
                <template for:each={fieldsFormatted} for:item="fld">
                    <lightning-output-field key={fld} field-name={fld}></lightning-output-field>
                    <p key={fld}>{fValue}</p> <!-- Q2: Does not render -->
                </template>
            </lightning-record-form>
        </template>
    </lightning-card>
</template>

JS

import { LightningElement, wire, api } from 'lwc';
import { getRecord } from 'lightning/uiRecordApi';

export default class DesignerFields extends LightningElement {
    @api indFields = 'Industry;NumberOfEmployees';
    @api objectApiName = 'Account';
    @api recordId;
    @api record;
    fieldsFormatted = ['Id'];

    connectedCallback() {
        this.fieldsFormatted = (this.indFields || 'Id').split(';').map(field => field);
        console.log(JSON.stringify(this.fieldsFormatted));
    }

    handleOnLoad(event) {
        const fValue = event.detail.records[this.recordId].fields.Industry.value;
        //Q1: fValue = "Education" the correct value. But "Industry" Field Name is still hard coded. 
        //How do I get the value for the field in {fld} variable?
        //Q2: How do I get fValue to render back in the HTML instead of the lightning-output-field
        //I tried this.dispatchEvent(fValue); and it had an error
    }    
}

Meta

<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>48.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__RecordPage</target>
    </targets>
    <targetConfigs>
        <targetConfig targets="lightning__RecordPage">
            <property name="indFields" label="Field Names" type="String" required="true" default="Industry;NumberOfEmployees" description="The Field Names." />            
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>

How do you suggest I get the value rendered instead of showing the lightning-output-field please?

  • NOTE: There has been some discussion below about the end result of why I want to display the field values only, but that is just getting away from the questions here, which is part of me trying to learn how to do this, not just copy and paste code. I just started this from someone saying "It is possible to get the field value from a lightning-output-field—you just have to capture the load event on the parent lightning-record-view-form".... but now I think using getRecord is going to be the way to go. If only I could get the @sfdcfox example working – JodieM May 23 at 10:26
  • OK, I have the answer, @sfdcfox example is working. I will paste the completed answer here shortly. So the answer is definitely to use getRecord. – JodieM May 23 at 14:29
3

Here is an approach that leverages lightning/uiRecordApi to get all relevant data and does not rely on a lightning-record-edit/view-form. All visible styling provided is custom styling and can be removed/changed.

@op: please ignore fieldNames in screenshot, I am aware they were explicitly not requested.

preview of component

XML

<?xml version="1.0" encoding="UTF-8" ?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>48.0</apiVersion>
    <isExposed>true</isExposed>
    <masterLabel>Section with Fields</masterLabel>
    <targets>
        <target>
            lightning__RecordPage
        </target>
    </targets>
    <targetConfigs>
        <targetConfig targets="lightning__RecordPage">
            <property name="fieldNames" label="Field Names" type="String" required="true" default="Name,Industry,NumberOfEmployees" description="Commma seperated list of fully qualified API fieldnames that should be displayed in this section" />
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>

HTML

<template>
  <lightning-card title="Get Field Values" icon-name="standard:question_best">
    <div class="slds-card__body_inner">
      <h2 class="slds-text-heading_small">
        Current Settings
      </h2>
      <p>
        recordId: {recordId} / objectApiName: {objectApiName} /
        configuredFields: {fieldNames}
      </p>
      <h2 class="slds-text-heading_small slds-var-m-top_medium">
        Field Values
      </h2>
      <template for:each={results} for:item="field">
        <p key={field.fieldName}>{field.fieldName}: {field.value}</p>
      </template>
    </div>
    <template if:true={errorMessage}>
      <pre>{errorMessage}</pre>
    </template>
  </lightning-card>
</template>

JS

import { LightningElement, api, wire } from "lwc";
import { getRecordUi } from "lightning/uiRecordApi";

export default class RecordpageSection extends LightningElement {
  @api
  fieldNames;

  @api
  recordId;

  @api
  objectApiName;

  apiFieldnames = [];

  errorMessage;

  results = [];

  connectedCallback() {
    this.fieldNames.split(",").forEach(fieldName => {
      this.apiFieldnames.push(this.objectApiName + "." + fieldName);
    });
  }

  @wire(getRecordUi, {
    recordIds: "$recordId",
    layoutTypes: "Compact",
    modes: "View",
    optionalFields: "$apiFieldnames"
  })
  recordInformation({ error, data }) {
    if (data) {
      let fieldInfo = data.records[this.recordId].fields;
      let matchingFields = [];
      Object.keys(fieldInfo).forEach(fieldName => {
        let qualifiedFieldname = this.objectApiName + "." + fieldName;
        //originalIndex is used to sort results as configured
        let originalIndex = this.apiFieldnames.indexOf(qualifiedFieldname);
        if (originalIndex > -1) {
          matchingFields[originalIndex] = {
            fieldName: fieldName,
            value: fieldInfo[fieldName].value
          };
        }
      });
      this.results = matchingFields;
    } else if (error) {
      this.errorMessage = JSON.stringify(error);
    }
  }
}
| improve this answer | |
  • Oh that looks nice... now that I know partly what I'm doing, I can almost understand that. I will post my answer for completeness and I will try yours tomorrow. – JodieM May 23 at 14:46
  • Wow, thank you! My learning from this is a) I must use optionalFields so as to not cause an error if the user does not have access and b) how to loop through arrays. I probably would stick to using getRecord rather than getRecordUI, but this gives me hope of how to also get the help text, which is for my next component. – JodieM May 25 at 12:19
1

For Q1: event.detail.records[this.recordId].fields['TheFieldName'].value

Now, for Q2, you can't have two tags with key as a child for for:each. You should wrap the lightning-output-field and the p into in a div for that. Now, if you try to display the values for every configured field, then you would need the same number of variables for that. A generic fValue won't work.

| improve this answer | |
  • Ah @muenzpraeger I was so interested when it could be that easy, but it's not. I do not know the value of 'The Field Name' to pass in. And it works fine just by passing in the actual field name with fields.Industry.value. Yes for Q2 I know I need to do a For-Each I was just trying to get somewhere with one field first. Oh this should all be so easy, it's just trying to do something so simple. – JodieM May 23 at 6:27
1

EDIT

To show/hide the child component based on field value dynamically based on fields. Follow the below steps.

Update JS with below code

Define one attribute named fieldsData like

fieldsData = [];

handleOnLoad(event) {
    const fValue = event.detail.records[this.recordId].fields.Industry.value;
    this.fieldsData = [];
    for (let [key, data] of Object.entries(event.detail.records[this.recordId].fields)) {
        console.log(JSON.stringify(data));
        this.fieldsData.push({fieldApiName: key, ...data});
    }
}

In HTML use the below code to show/hide your child component. This code will show the child component only if there is a value in the field.

<template if:true={fieldsData} for:each={fieldsData} for:item="field">
    <c-child-lwc key={field.fieldApiName} if:true={field.value}>
    </c-child-lwc>
</template>

Use lightning-record-edit-form/lightning-record-view-form so the fields don't render by default, and you have freedom to show/hide the fields.


Before Edit

You dont need to use the lightning-output-field with lightning-record-form, record-form will automatically do it for you. Also you don't need to get the record using getRecord API, this is also auto done by record-form.

JS

import { LightningElement, wire, api } from 'lwc';
import { getRecord } from 'lightning/uiRecordApi';

export default class DesignerFields extends LightningElement {
    @api indFields = 'Industry;NumberOfEmployees';
    @api objectApiName = 'Account';
    @api recordId;
    @api record;
    fieldsFormatted = ['Id'];

    connectedCallback() {
        this.fieldsFormatted = (this.indFields || 'Id').split(';').map(field => field);
        console.log(JSON.stringify(this.fieldsFormatted));
    }

    handleOnLoad(event) {
        const fValue = event.detail.records[this.recordId].fields.Industry.value;
        //Q1: fValue = "Education" the correct value. But "Industry" Field Name is still hard coded. 
        //How do I get the value for the field in {fld} variable?
        //Q2: How do I get fValue to render back in the HTML instead of the lightning-output-field
        //I tried this.dispatchEvent(fValue); and it had an error
    }    
}

HTML

<template>
    <lightning-card title="Design Attribute Demo" icon-name="custom:custom19">
        <template if:true={fieldsFormatted}>
            <lightning-record-form record-id={recordId} object-api-name={objectApiName} mode="view" 
                fields={fieldsFormatted} onload={handleOnLoad}>
            </lightning-record-form>
        </template>
    </lightning-card>
</template>
| improve this answer | |
  • Ah yes, the getRecord is there (greyed out) from a previous attempt at this code. Unfortunately you seemed to miss the extra bits that I'm trying to achieve. Yes, I can get the fields displaying on the lightning-record-form with no issues. However, I want only the field value, not the field name, help text, or any additional CSS that comes default with fields displayed on lightning-record-form. – JodieM May 23 at 6:25
  • This can be easily achieved using, lightning-record-edit-form/view-form instead of record-form. Using lightning-record-edit-form/view-form you can display the input fields/output fields of your wish. You can also hide the labels of the output fields using variant='label-hidden'. Also, you can customize the standard CSS up to some level using static resources. – rahul gawale May 23 at 6:46
  • Sorry, it's just not enough to hide the label and the CSS. I need the value only to pass into a child component. – JodieM May 23 at 7:42
  • @JodieM is there any specific reason or that? – rahul gawale May 23 at 7:57
  • I have a child component that will not display if the boolean field value is false. So I need the field value to control the display of the child component. – JodieM May 23 at 8:22
1

I know it's not acceptable to answer your own question, but just in case anyone comes across this later, this is what I got to work based on my original question of what is the simplest way to display just field values where the names come from a design attribute. It is similar to @Szandor's and I got it working about 5 mins before Szandor posted his. This is based on @sfdcfox's post linked above, and this post to return the data from the wire.

META

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>48.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__RecordPage</target>
    </targets>
    <targetConfigs>
        <targetConfig targets="lightning__RecordPage">
            <property name="indFields" label="Field Names" type="String" required="true" default="Name;Ownership" description="The Field Names to display the value for." />            
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>

JS

import { LightningElement, api, wire } from 'lwc';
import { getRecord } from 'lightning/uiRecordApi';

export default class DesignerFieldsGetRecords extends LightningElement {
    @api indFields;
    @api objectApiName = 'Account';
    @api recordId;
    record = '';
    error = '';
    fieldsFormatted = ['Id'];

    @wire(getRecord, { recordId: '$recordId', fields: '$fieldsFormatted' })
    wiredRecord({data, error}) {
        if (data) {
            this.record = data;
            this.fValues = [];
            Object.keys(data.fields).forEach((field) => {
                this.fValues.push(data.fields[field].value);
            });
        } else if (error) {
            this.error = error;
        }
    }

    connectedCallback() {
        this.fieldsFormatted = (this.indFields || 'Id').split(';').map(field => this.objectApiName+'.'+field);
    }
}

HTML

<template>
    <lightning-card icon-name="custom:custom18" title="A Card"> 
        <div class="slds-var-m-left_medium">
            <template if:true={record}>
                <template for:each={fValues} for:item='fld'>
                    <p key={fld}>{fld}</p>
                </template>
            </template>
        </div>
    </lightning-card>
</template>

IMAGE enter image description here

| improve this answer | |
  • 1
    It is very much acceptable to answer your own question. Have you maybe tested this against cross object fields like Account.Owner.Name? From the docs it seems that getRecord can do this. – Christian Szandor Knapp May 23 at 15:30

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.