0

I have a component (container) with a lightning-tabset with a single lightning-tab. Inside the tab is a component (dataComponent) which pulls "Item" records via Apex and assigns them to a tracked variable - @track contextRecords = []. Each Item in that variable appears as a card. In that same dataComponent is a modal, which is shown conditionally when the "Add Item" button is clicked.

Inside each card there is a dynamicRecordForm component. That component is a simple text input field, a Save button, and a Cancel button. Once a value is entered into the field and the Save button is clicked, the native createRecord from the lightning/uiRecordApi library is called, creating a new Item record. An event is triggered, which passes the newly created record (stringified since it isn't officially accessible via Apex yet) up to the dataComponent.

In the refreshTab function in dataComponent the newly created record is parsed into an object, then added to the initial tracked variable - contextRecords.

Example: When the component loads, one Item record is retrieved via Apex and displayed as a single card within the tab. The Add Item button is clicked, displaying the modal with the dynamicRecordForm. A new Item name is entered and the Save button is clicked.

Expected results: The new Item record is added to the database and to the contextRecords variable, which then displays a new card for a total of two on the tab.

Actual results: A new Item record is added to the database, but nothing happens on the tab. Only one card shows still.

If I refresh the entire page, however, the new card displays as it is retrieved via the initial Apex callout.

Any solutions? I can't find anything in the documentation, developer forums, or StackExchange forums on this.

SEE CODE BELOW:

container.html:

<template>
    <lightning-tabset variant="scoped" active-tab-value="1">
        <lightning-tab>
            <c-data-component></c-data-component>
        </lightning-tab>
    </lightning-tabset>
</template>

container.js:

import { LightningElement } from 'lwc';

export default class Container extends LightningElement {}

dataComponent.html

<template>
    <lightning-layout>
        <lightning-layout-item size="12">
            <template for:each={contextRecords} for:item="contextItem">
                <div key={contextItem.Id} class="customCard">
                    <article class="slds-card">
                        <div class="slds-card__body slds-card__body_inner topSpace">
                            <c-dynamic-record-form onrefreshtab={refreshTab} oncancel={cancel}></c-dynamic-record-form>
                        </div>
                    </article>
                </div>
            </template>
        </lightning-layout-item>
    </lightning-layout>

    <div class={modalClass}>
        <section role="dialog" tabindex="-1" aria-labelledby="modal-heading-01" aria-modal="true"
            class="slds-modal slds-fade-in-open modal">
            <div class="slds-modal__container">
                <button class="slds-button slds-button_icon slds-modal__close slds-button_icon-inverse" aria-label="">
                    <lightning-icon icon-name="utility:close" alternative-text="Close" class="closeIcon"
                        onclick={closeModal}></lightning-icon>
                </button>
                <div class="slds-modal__header">
                    <h1 class="slds-modal__title slds-hyphenate">Add Item</h1>
                </div>
                <div class="slds-modal__content slds-p-around_medium" id="modal-content-id-1">
                    <c-dynamic-record-form onrefreshtab={refreshTab} oncancel={cancel}></c-dynamic-record-form>
                </div>
            </div>
        </section>
        
        <div class="centered">
            <lightning-button-group>
                <lightning-button variant="brand" label="Add Item" onclick={addItem}></lightning-button>
            </lightning-button-group>
        </div>
    </div>
</template>

dataComponent.js

import { LightningElement, api, track } from 'lwc';
import getItems from '@salesforce/apex/Item_Controller.getItems';

export default class DataComponent extends LightningElement {
    @track contextRecords = [];
    modalClass = 'slds-hide';

    connectedCallback(){
        this.getContextItems();
    }
    
    getContextItems(){
        getItems()
        .then(existingItems => {
            this.contextRecords = existingItems;
        })
    }
    
    addItem(){
        this.modalClass = '';
    }
    
    cancel(event){
        this.modalClass = event.detail.modalClass;
    }
    
    refreshTab(event){
        // this gives us a new record, Id included
        let newRecord = JSON.parse(event.detail.newRecord);
        this.contextRecords.push(newRecord);
    }
}

dynamicRecordForm.html

<template>
    <lightning-layout>
        <lightning-layout-item size="12">
            <div class="slds-m-around_medium">
                <lightning-input type="text" label="Enter Item Name" data-name="nameField">
                </lightning-input>
            </div>
        </lightning-layout-item>
    </lightning-layout>
    <lightning-layout>
        <lightning-layout-item size="12">
            <div class="centered">
                <lightning-button-group>
                    <lightning-button label="Cancel" onclick={cancel}></lightning-button>
                    <lightning-button variant="brand" label="Save" onclick={save}></lightning-button>
                </lightning-button-group>
            </div>
        </lightning-layout-item>
    </lightning-layout>
</template>

dynamicRecordForm.js

import { LightningElement, api, wire } from 'lwc';
import { createRecord } from 'lightning/uiRecordApi';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';

export default class DynamicRecordForm extends LightningElement {
    itemName;
    
    save(){
        let nameField = this.template.querySelector(`lightning-input[data-name="nameField"]`);
        let itemName = nameField.value;         
        let fields = [];
        field['Name'] = itemName;
        
        // code for adding default values is here, but irrelevant to this example
        
        const recordInput = {
            apiName: 'ItemObject',
            fields: fields
        };
        
        createRecord(recordInput)
            .then((record) => {
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Success',
                        message: 'Item created successfully!',
                        variant: 'success'
                    })
                    
                    this.cancel();
                    
                    // newRecord below is stringified because, though the object is created with an id,
                    // the async nature hasn't completed the commit to the database, making it inaccessible
                    // to Apex
                    
                    let event = new CustomEvent('refreshtab', {
                        detail: {
                            refreshTab: true,
                            newRecord: JSON.stringify(newRecord)
                        }
                    });
                    this.dispatchEvent(event);
                )
                .catch(error => {
                    this.spinnerDivClass = 'slds-hide';
                    this.dispatchEvent(
                        new ShowToastEvent({
                            title: 'Error creating ' + this.objectLabel + ' record',
                            message: error.body.message,
                            variant: 'error'
                        })
                    )
                })
            }
        
        cancel(){                       
            let event = new CustomEvent('cancel', {
                detail: {
                    modalClass: 'slds-hide'
                }
            });
            this.dispatchEvent(event);
        }
    }
1
  • 1
    I would suggest editing your question to add your actual code - at least the relevant parts of it.
    – Moonpie
    Jan 21, 2022 at 21:33

1 Answer 1

0

Ignoring the typos, you are probably running into problems because the createRecord method imported from lightning/uiRecordApi returns a object with a different shape and properties than a record pulled from a SOQL query in apex and sent to a lightning component.

For example, from Apex an sObject will have a parameter Id, but from createRecord you get id. There are many other differences besides that. When I used your example code above (after fixing many typos) and I pulled out the ID from the record returned by createRecord and put it into an object like {Id: record.id} and returned that as the record in the refreshtab event, I was able to get the record to show up dynamically after the event fires.

2
  • Hey Kyle, thanks for the response. You're correct, 2, 3, and 4 were typos from me trying to greatly simplify my code example to fit on the page. :-) However, #1 didn't fix the issue. :-( Any other thoughts? (I corrected the code in the post to fix the typos) Jan 26, 2022 at 14:20
  • @AdamDrissel I updated my answer above. I believe the problem is most likely because createRecord returns an object with different properties than your apex method.
    – Kyle
    Jan 27, 2022 at 18:28

You must log in to answer this question.

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