0

I have an LWC template that has a parent and child template relationship. On the child template I am using the NavigationMixin.Navigate API to edit the record. When a user selects "Save", I would like the entire component to re initialize with the new data.

What I am finding is that with the NavigationMixin.Navigate API, we

Parent HTML Template

<template>
        <lightning-card title={label} icon-name={strIconName}>
            <lightning-button 
            slot="actions" 
            icon-name="action:refresh"
            icon-position ="center"
            title="Refresh Data"
            onclick={refreshData}>
        </lightning-button>
            <div class="slds-p-around_medium">
                <template if:true={cleanedTableData}>
                        <div class="slds-grid slds-wrap slds-p-around_small">

                            <template for:each={cleanedTableData} for:item="record">
                                <c-dynamic-tile onchildevent={getDataFromChild} class={two_tile} tiletype={displayMode} objectapiname={strChildSObjectName} key={record.Id} columns={columns} record={record} > </c-dynamic-tile>
                            </template>
                    </div>
                </template>
            </div>
        </lightning-card>
</template>

Parent JS

import { LightningElement,track,api,wire } from 'lwc';
import { refreshApex } from '@salesforce/apex';
import getFieldsAndRecords from '@salesforce/apex/sObjectDatatableController.getSobjectRecords';
import getSObjectFromID from '@salesforce/apex/sObjectDatatableController.getSObjectFromId'
import getIconLabel from '@salesforce/apex/sObjectDatatableController.getIconName';
import {getRecord, getFieldValue} from 'lightning/uiRecordApi';
import CalloutOptionsAllowMergeFieldsInBody from '@salesforce/schema/NamedCredential.CalloutOptionsAllowMergeFieldsInBody';
import PermissionsLightningConsoleAllowedForUser from '@salesforce/schema/MutingPermissionSet.PermissionsLightningConsoleAllowedForUser';
import Setting from '@salesforce/schema/TenantUsageEntitlement.Setting';
import RefreshTokenValidityPeriod from '@salesforce/schema/ConnectedApplication.RefreshTokenValidityPeriod';
import PrimarySicDesc from '@salesforce/schema/DandBCompany.PrimarySicDesc';
import tileListTemplate from './tileListTemplate.html'
import listTemplate from './paceApexDynamicLWC.html'

async function getSObjectfromRecordId(recordId)
{
    console.log('testgetSObjectfromRecordId Called with this Record ID '+ recordId)
    var URLField = 'https://'+location.host;
    return `https://${location.host}/lightning/r/${await getSObjectFromID({ strRecordId: recordId, })}/${recordId}/view`;
    return URLField;
}


export default class PaceApexDynamicLWC extends LightningElement {
    @api recordId;
    @api parentObjectIdField;
    @api label;
    @api fieldSetName;
    @api strChildSObjectName;
    @api strChildSObjectParentField;
    @api filter;
    @api displayMode;
    @api iconName;

    
    @track columns;   //columns for List of fields datatable
    @track tableData;   //data for list of fields datatable
    @track strIconName;
    @track error;
    @track mapOfLookUpKeys;
    @track rawListOfRecords;
    @track cleanedTableData;
    

    @api
    refresh() {
        return refreshApex(this.tableData);
    }


    get two_tile(){
        
        if(this.displayMode == '2 Tile')
        {
            return "slds-size_6-of-12 slds-p-bottom_small slds-p-right_small"
        }else{
            return "slds-size_12-of-12 slds-p-bottom_small slds-p-right_small"
        }
    }




    get vals() { 

        return { 
            'recordId':this.recordId,
            'parentObjectIdField': this.parentObjectIdField,
            'strSObjectName': this.strChildSObjectName,
            'label': this.label,
            'strFieldsetName': this.fieldSetName,
            'parentFieldAPIName':this.parentFieldAPIName,
            'strChildSObjectParentField':this.strChildSObjectParentField,
            'filter' : this.filter
        }
    } 
    
    connectedCallback() {
        this.fetchData();
        this.refresh();
    }

    flattenObject(ob) {
            var toReturn = {};
            for (var i in ob) {
                if (!ob.hasOwnProperty(i)) continue;
                
                if ((typeof ob[i]) == 'object' && ob[i] !== null) {
                    var flatObject = this.flattenObject(ob[i]);
                    for (var x in flatObject) {
                        if (!flatObject.hasOwnProperty(x)) continue;
        
                        toReturn[i + '.' + x] = flatObject[x];
                    }
                } else {
                    toReturn[i] = ob[i];
                }
            }
            return toReturn;
    }


 /* 
    Rend
 */
    render(){
        console.log(' displayMode  ' +this.displayMode)
        return  this.displayMode === '2 Tile' ? tileListTemplate :
                this.displayMode === '1 Tile' ? tileListTemplate :
                this.displayMode === 'List'  ? listTemplate:
                listTemplate
            
    }

    
    /* 
    in order to refresh your data, 
    execute this function:
    */
    refreshData(){
            console.log('Refresh Data Function Fired');
            this.fetchData();
            eval("$A.get('e.force:refreshView').fire();");

        }


    /*
        Use this to Initialize & Refresh Lightning Datatable
    */
    async fetchData(){
        await getFieldsAndRecords({ mapVals: this.vals })
        .then(data => {
                //get the map of fields and data
                
                //get the entire map
                let objStr = JSON.parse(JSON.stringify(data));
                
                /* retrieve listOfFields from the map,
                here order is reverse of the way it has been inserted in the map */
                let listOfFields= Object.values(objStr)[0];
            
                //retrieve listOfRecords from the map
                let listOfRecords = Object.values(objStr)[1];

                this.rawListOfRecords = listOfRecords;

                // console.log('This is the OBJ Str::'+JSON.stringify(objStr));
                // console.log('This is the listOfFields::'+JSON.stringify( listOfFields));
                // console.log('This is the listOfRecords::'+JSON.stringify( listOfRecords));

                let items = []; //local array to prepare columns
                let mapOfLookUpKeys = {};


                

                listOfFields.map(element=>{
                    if( element.fieldPath == 'Name')
                    {
                        console.log('Name');
                    //prepare Name as hyperlink                                     
                    items = [...items ,
                        {
                            label: element.label, 
                            fieldName: 'URLField',
                            type: 'url', 
                            fieldNameActualText: element.fieldPath,
                            typeAttributes: { 
                                label: {
                                    fieldName: element.fieldPath
                                },
                                target: '_blank'
                            },
                            sortable: true 
                        }];
                    }
                    else if(element.type == 'REFERENCE')
                    {
                        console.log("REFERENCE");
                        let fieldPath = element.fieldPath;
                        // let newMapOfLookUpKeys = { fieldPath : fieldPath};
                        let newMapOfLookUpKeys = {}
                        newMapOfLookUpKeys[fieldPath] =
                            {
                                label: element.label, 
                                fieldName: element.fieldURL,
                                fieldNameActual: element.fieldPath,
                                fieldNameActualText: element.fieldURLTextValue,
                                type: 'url', 
                                typeAttributes: { 
                                    label: {
                                        fieldName: element.fieldURLTextValue
                                    },
                                    target: '_blank'
                                },
                                sortable: true 
                            }
                        this.mapOfLookUpKeys = {...this.mapOfLookUpKeys, ...newMapOfLookUpKeys}

                        

                        //prepare Lookups as hyperlink
                        items = [...items ,
                        {
                            label: element.label, 
                            fieldName: element.fieldURL,
                            fieldNameActual: element.fieldPath,
                            fieldNameActualText: element.fieldURLTextValue, 
                            type: 'url', 
                            typeAttributes: { 
                                label: {
                                    fieldName: element.fieldURLTextValue
                                },
                                target: '_blank'
                            },
                            sortable: true 
                        }];
                    }else{
                        console.log('ELSE ');
                        items = [...items ,{
                            label: element.label, 
                            fieldName: element.fieldPath,
                            type: element.strType
                        }];  

                    }
 
                });

                


    
                //finally assigns item array to columns
                this.columns = items; 
                this.tableData = listOfRecords;
                console.log('This is the columns::'+JSON.stringify( this.columns)); 
                console.log('Table Data::'+ JSON.stringify(this.tableData));


                // console.log('Items::' +JSON.stringify(this.tableData));
            })
        .catch(error => {
                console.log(JSON.stringify(error));
                // this.error = error;
                // console.log(error);
            });
        if(this.iconName == null)
        {
            await getIconLabel({ sObjectName: this.strChildSObjectName})
            .then(data => {
                // console.log('Executing Get Icon Label');
                // console.log(JSON.stringify(data));
                this.strIconName = data

            })
            .catch(error => {
                this.error = error;
                console.log(JSON.stringify(error));
            });
            
        }
        if(this.tableData)
        {
                        /*if user wants to display first column has hyperlink and clicking on the link it will
                naviagte to record detail page. Below code prepare the field value of first column
                */

                //retrieve Id, create URL with Id and push it into the array
                /* Prepare the Org Host */
                let tableDataRes =  await this.tableData.map( async item=>{


                    let URLField = await getSObjectfromRecordId(item.Id)
                    let new_item = this.flattenObject(item);
                    // console.log("Map Of Keys::" + JSON.stringify(this.mapOfLookUpKeys));
                    // console.log("Map Of Keys TYPE::" + typeof this.mapOfLookUpKeys);
                    let new_URLs = []
                    try{ 
                        for (const key in this.mapOfLookUpKeys) 
                        {
                        // new_item[new_item]
                            if(key in item )
                            {
                                console.log("Value ::"+this.mapOfLookUpKeys[key])
                                console.log("key ::"+JSON.stringify(key))

                                // console.log('Item '+item)
                                console.log('Item Key '+item[key])
                                let resp = await getSObjectfromRecordId(item[key])
                                // `https://${location.host}/lightning/r/${await getSObjectFromID({ strRecordId: recordId, })}/${recordId}/view`;
                                console.log("RESP")
                                console.log(resp)
        
                                let mapVal = {}

                                let fieldNameURL  = this.mapOfLookUpKeys[key]['fieldName'];
                                mapVal[fieldNameURL] = resp;
        
                                 new_item = {...new_item, ...mapVal};
                            }

                        }
                    }
                    catch (error) {
                        console.error(error);
                    }
                    return {...new_item, URLField};
                });

                this.tableData = await Promise.all(tableDataRes)

                // this.tableData  = tableDataRes;

                console.log("TABLE DATA:: "+ JSON.stringify(this.tableData))
                this.cleanedTableData = this.tableData;
                this.refresh();
            }
        }
    }

Child HTML Template

    <div class="slds-grid slds-wrap">


        <div class="slds-size_12-of-12 slds-p-bottom_small slds-p-right_small"  >

            <div class="slds-grid slds-wrap" style="line-height: 1.5; width: 100%;">
                <template iterator:datamap={recordColumnMapTransformedData} >



                            <!--  Button END  -->


                            <ul  if:true={datamap.first} key={datamap.value.Id} class="slds-size_12-of-12  slds-list_horizontal slds-has-dividers_right slds-wrap slds-m-top_xx-small">
                                <li>
                                <h3 class="slds-tile__title slds-truncate" title="Salesforce UX">
                                    <template if:true={datamap.value.rec_url}>
                                        <a class="slds-text-title_bold hoverableLink_hoverableLink" href={datamap.value.rec_url} target="_blank">{datamap.value.val}</a>
                                    </template>
                                    <template if:false={datamap.value.rec_url}>
                                        <div>{datamap.value.val}</div>
                                    </template>
                                </h3>
                                </li>
                            </ul>



                                    <dl if:false={datamap.first} key={datamap.value.Id} class="slds-size_12-of-12 slds-has-flexi-truncate slds-dl_horizontal" >
                                        <dt class="slds-col slds-size_2-of-5 slds-float_left slds-truncate slds-dl_horizontal__label" >
                                            {datamap.value.column}
                                        </dt>



                                        <template if:true={datamap.value.rec_url}>
                                            <dd class="slds-col slds-size_3-of-5  slds-float_left slds-truncate slds-p-right_small slds-dl_horizontal__detail" >
                                                    <a href={datamap.value.rec_url} target="_blank">{datamap.value.val}</a>
                                            </dd>
                                        </template>
                                        <template if:false={datamap.value.rec_url}>
                                            <dd class="slds-col slds-size_3-of-5  slds-float_left slds-truncate slds-p-right_small  slds-dl_horizontal__detail" >
                                                <span> {datamap.value.val}</span>
                                            </dd>
                                        </template>



                                    </dl>

                </template>
            </div>
        </div>
    </div>
</template>    

Child Template JS File

import { LightningElement, api , track} from 'lwc';
import {NavigationMixin} from 'lightning/navigation';
import oneTile from './oneTile.html';
import twoTile from './twoTile.html';
import {refreshApex} from '@salesforce/apex';
import { getRecord, getRecordNotifyChange } from 'lightning/uiRecordApi';
import { updateRecord } from 'lightning/uiRecordApi';




export default class DynamicTile extends NavigationMixin(LightningElement) {
    @api record
    @api index;
    @api columns
    @api objectapiname;
    @api tiletype

    @track recordColumnMapTransformedData




    navigateToRecordViewMode(){
        this[NavigationMixin.Navigate]({
            type:"standard__recordPage",
            attributes:{
                recordId: this.record.Id,
                objectApiName: this.objectapiname,
                actionName: 'view'
            }
        })
    }
    navigateToRecordEditMode(){

        this[NavigationMixin.Navigate]({
            type:"standard__recordPage",
            attributes:{
                recordId: this.record.Id,
                objectApiName: this.objectapiname,
                actionName: 'edit'
            }
        })
    }

    render()
    {
        return  this.tiletype === '2 Tile' ? twoTile :
        this.tiletype === '1 Tile' ? oneTile :
        oneTile
    }
    get css_for_tile(){
        
        if(this.tiletype == '2 Tile')
        {
            return "slds-size_6-of-12 slds-p-bottom_small"
        }else{
            return "slds-size_12-of-12 slds-p-bottom_small"
        }
    }

    

    connectedCallback(){
        console.log("Dynamic Tile Columns");
        console.log(JSON.stringify(this.columns));

        console.log("Dynamic Tile record");
        console.log(JSON.stringify(this.record));

        this.createMap();

    }

    createMap(columns, record){
        this.recordColumnMapTransformedData = []
        // this.recordColumnMapTransformedData = this.columns.map((column, index) => {
        //     console.log('Column in map '+ JSON.stringify(column) )
        //     console.log('Index in map '+ index )
        //     return {column : column.label , val:  index + 1}
        // });
        var col_label = ''
        var rec_fieldName = ''
        var rec_url = ''

        for(var i=0; i< this.columns.length; i=i+1){
            console.log('Column[i] = '+ JSON.stringify( this.columns[i]))
            console.log('Record = '+ JSON.stringify( this.record))

            try{
                col_label = this.columns[i]['label']
                rec_fieldName = ( typeof this.columns[i]['fieldNameActualText'] ) !== "undefined" ? this.columns[i]['fieldNameActualText'] : 
                                this.columns[i]['fieldName'] ;
                rec_url = this.columns[i]['type'] === 'url'? this.columns[i]['fieldName'] : undefined

            }
            catch (error) {

            }

            
            console.log('col_label = '+ JSON.stringify(  col_label ))
            console.log('rec_fieldName = '+ JSON.stringify( rec_fieldName ))
            console.log('rec_url = '+ JSON.stringify( rec_url ))

            try{
                this.recordColumnMapTransformedData.push(

                        {
                            column : col_label,
                            columnNumber : i,
                            isColumnNumberEven: i % 2 == 0,
                            val: this.record[rec_fieldName],
                            type: this.columns[i]['type'],
                            rec_url: this.record[rec_url]
                            // ... (typeof rec_url !== undefined ) && { rec_url: this.record[rec_url] }
                        }
               );

            } catch (error){
                console.log("ERROR FOUND")
            }

        }
    }
}

Potential Solutions that I have explored that Have Not Worked

  1. You can use eval JavaScript function to refresh the page from the component. copy and paste the below code after the line from where you wanted to refresh the page.

eval("$A.get('e.force:refreshView').fire();");

  1. Using refreshApex() I am not using a wired function, refreshApex() to refresh the component's data when the component is loaded (i.e. in connectedCallback()), rather than pulling data from the cache.

  2. Sending an event from the child component to the parent component and launching my refresh method.

I have tried numerous solutions. I am sure these are meant to work, I am just not implementing them correctly. The goal is to have the entire component refresh with updated data of the records when a user selects "Save" after the NavigationMixIn Edit.

1 Answer 1

1

force:refreshView is to notify other components in app builder to refresh. This won't help.

#3 should have worked: Sending an event from the child component to the parent component and launching my refresh method.


Have you tried this?

When the record is saved, this[NavigationMixin.Navigate] returns a promise that can be used to send a custom event to parent component. Now in parent component's event handler, call the refreshApex() method to refresh component.

childComponent.js:

// Generate a URL to a User record page
this[NavigationMixin.Navigate]({
    type:"standard__recordPage",
    attributes:{
        recordId: this.record.Id,
        objectApiName: this.objectapiname,
        actionName: 'edit'
    }
}).then((url) => {
    // send a custom event to parent component
    this.dispatchEvent(new CustomEvent('refresh'));
});

parentComponent.html:

<c-dynamic-tile onrefresh={handleRefresh} ...

parentComponent.js:

handleRefresh() {
    this.refresh();
}

refresh() {
    return refreshApex(this.tableData);
}
4
  • is your solution telling me that dispatching events will cause the parent component to refresh? Jan 25, 2022 at 3:38
  • Yes, after edit is finished, custom event will be send from child to parent, parent's handler is responsible to perform refresh using refreshApex() method.
    – Raul
    Jan 25, 2022 at 9:41
  • This does not work for me. I got an Error ` [Cannot read properties of undefined (reading 'then')]`
    – user2018
    Aug 1, 2022 at 19:29
  • @user2018 have you setup lightning-navigation correctly?
    – Raul
    Aug 1, 2022 at 21:18

You must log in to answer this question.

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