1

I am displaying records in html template using LWC in salesforce. My columns containing depended fields like country , state and city. When I am modifying displayed data by changing country, then I need to get depended states in my State Picklist.

Salesforce Implementation

These picklist , say country, state and city that I used here are implemented using custom picklist field method using LWC components. When Component loads first time, I am looping all the records directly from the related tables.

And after loading records, If I am changing country, I implemented onchange method which will load all related states of that selected country. Here from my onchange method I am able to pull all related states and able to update to looping data this.sDetails. But problem is looping data not refreshing after onchnage. Here I need to get refreshed and modified data after onchnage method

onchnage code from .js file

  handleCountryChangeEvent(event) {

      const  selectedCountryValue = event.detail;

      for(var i=0; i< this.countryOptions.length;i++){
          if(this.countryOptions[i].label == selectedCountryValue){
             var key = i;
           }
       }
        this.allStateOptions = this.allStateOptions
                        .filter(option => option.validFor.includes(key));

        this.sDetails.forEach(ele => {
           ele.stateOptionsList = this.allStateOptions;
         })  
        console.log(this.sDetails);
        refreshApex(this.sDetails);
     }

Defined Field in .js file,

{ 
  label : 'State',
  fieldName: 'state__c',
  name : 'State' ,
  placeholder : 'Choose State',
  type: 'statePicklist',
  typeAttributes: {
    value: { fieldName: 'state__c' },
    options: { fieldName: 'stateOptionsList' },
    },
  editable: true,
  wrapText:true,
  context: { fieldName: 'Id' }
},

Code for getting SDetails ,

 @wire(getStockDetails, { pickList: '$countryOptions' })
result(result){
  
  let sDetailsRObj = JSON.parse(JSON.stringify(result));
  this.sDetails = stockDetailsRObj.data;    
    try
      {
        this.sDetails.forEach(ele => {
          ele.countryOptionsList = this.countryOptions;
          ele.stateOptionsList = this.allStateOptions;
          ele.cityOptionsList = this.cityOptions;
          })
      }
    catch(err) {
        console.log(err.message);
    }
} 

Code for getting state and country picklists from .js file,

@wire(getPicklistValues, 
    {recordTypeId: "$objectInfo.data.defaultRecordTypeId",fieldApiName: countryField})
wireCountryPickList({ error, data }) 
    {
      if (data) {
          this.countryOptions = data.values;
      } 
      else if (error) {
          console.log(error);
      }
  }
@wire(getPicklistValues, 
    {
      recordTypeId: "$objectInfo.data.defaultRecordTypeId",
      fieldApiName: stateField
    })
wireStatePickList({ error, data }) 
    {
      if (data) {
        this.allStateOptions = data.values;
         
      } 
      else if (error) {
          console.log(error);
      }
  }

Problem Identified

Here I am properly getting all the states related to selected country and able to properly bind to my looping data this.sDetails. But here after getting data on this.sDetails , the displayed data is not refreshing. Here first time all data loads successfully, And after country change, related states are getting in onchnage method and loading to looping data. But its not refreshing and not showing updated state options while displaying.

In my case looping data not refreshing after onchnage event on picklist field. Can please anyone to guide me to resolve this issue or suggest any documentation to refer field dependency updation ?

Mr.DevEng
  • 2,651
  • 14
  • 57
  • 115

2 Answers2

1

One possible reason the template is not re-rendering to reflect these changes could be due to the fact that the LWC (Lightning Web Components) framework may not be detecting the change in the properties that you are modifying.

As of Spring '20 release, the use of the @track decorator is no longer necessary for detecting changes in the properties of arrays and plain objects. This is because the Lightning Web Components (LWC) engine now defaults to making these types of properties reactive.

However, it is still important to note that the LWC engine tracks changes to the property itself, but not necessarily deeply nested changes within objects or arrays.
If you are making changes to nested properties of an object or elements within an array, it is best to create a new instance of the object or array to ensure that the changes are picked up.

When you are updating an array or an object, create a new instance of that array or object so that LWC can recognize the change:

this.sDetails = this.sDetails.map(detail => ({
    ...detail,
    stateOptionsList: this.allStateOptions
}));

This approach creates a new array with new objects that include the updated stateOptionsList property. It is more likely to trigger the reactivity system of LWC and cause the component to re-render with the updated data.

Finally, you are using refreshApex in your code, but it seems that you are not using it properly. refreshApex is used to refresh the cache of data retrieved via a @wire adaptor. You must pass the original @wire property to refreshApex and not the modified data (see "working with the refreshApex feature in LWC").
For example:

import { refreshApex } from '@salesforce/apex';
// ...

@wire(getStockDetails, { pickList: '$countryOptions' })
wiredStockDetails(result) {
    this.wiredResult = result;
    // ... rest of the code
}

// Later in your handleCountryChangeEvent
refreshApex(this.wiredResult);

As a last resort, for testing, you can force a render by changing some state that you know will cause the component to re-render.

this.timestamp = new Date();

Then in your template, bind this property to some hidden field or use it in such a way that it forces a re-render.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
0

When you try to assign an object or list to the current lwc property, you are changing the pointer of the property that the HTML attached to, which makes the property disconnect from the HTML and not rerender again.

try to change this assignment:

this.allStateOptions = this.allStateOptions
                        .filter(option => option.validFor.includes(key));

to:

this.allStateOptions = [...this.allStateOptions
                        .filter(option => option.validFor.includes(key))];