Cached items are set as read-only (because otherwise you could corrupt the cache). If you want a modifiable object, you need to clone it.
this.wiredContact = Object.assign({}, data);
Based on comments, you can also use the rest parameter syntax in LWC:
this.wiredContact = {...data};
If you don't care about some mobile browsers, and you're using Lightning Web Security instead of Locker Service, you can also use the structuredClone
function, which can handle even recursive structures and other things that the JSON.parse(JSON.stringify(value))
trick doesn't support. You can check the status of structuredClone on Can I Use.
here is a solution that works perfectly. I display a list of opportunity and add a hyperlink on the column name of the data table.
To do that, you have to create a wrapper class in you apex controller like this:
public with sharing class GetAllOpportunities {
@AuraEnabled(cacheable=true)
public static List<DataTableWrapper> getAllOpps() {
List<Opportunity> listOpp = [SELECT Id, Name ,StageName, CloseDate
FROM Opportunity Order By Name asc];
List<DataTableWrapper> response = new List<DataTableWrapper>();
for(Opportunity opp : listOpp){
DataTableWrapper obj = new DataTableWrapper();
obj.oppId = opp.Id;
obj.name = opp.Name;
obj.nameUrl = '/'+opp.Id;
obj.stageName = opp.StageName;
obj.closeDate = opp.CloseDate;
response.add(obj);
}
return response;
}
private class DataTableWrapper {
@AuraEnabled
public Id oppId {get;set;}
@AuraEnabled
public String name {get;set;}
@AuraEnabled
public String nameUrl {get;set;}
@AuraEnabled
public String stageName {get;set;}
@AuraEnabled
public Date closeDate {get;set;}
}
}
note the nameUrl attribute, I'll use it in the js file.
here is the html file:
<template>
<lightning-card title="Opportinity List">
<lightning-datatable data={opportunities} columns={columns} key-field="Id"></lightning-datatable>
</lightning-card>
</template>
And Finally, the js file:
import { LightningElement ,wire,track} from 'lwc';
import getAllOpps from '@salesforce/apex/GetAllOpportunities.getAllOpps';
export default class OpportunityList extends LightningElement {
@track columns = [
{
label: 'Opportunity name',
fieldName: 'nameUrl',
type: 'url',
typeAttributes: {label: { fieldName: 'name' },
target: '_blank'},
sortable: true
},
{
label: 'Stage Name',
fieldName: 'stageName',
type: 'text',
sortable: true
},
{
label: 'Close date',
fieldName: 'closeDate',
type: 'date',
sortable: true
}
];
@track error;
@track opportunities = [];
@wire(getAllOpps)
wiredOpps({error,data}) {
if (data) {
this.opportunities = data;
this.error = undefined;
} else if (error) {
this.error = error;
this.opportunities = undefined;
}
}
}
==========================================================================
Update
=========================================================================
This code below does the same thing without creating any wrapper class. We can only use JS.
import { LightningElement ,wire,track} from 'lwc';
import getAllOpps from '@salesforce/apex/GetAllOpportunities.getAllOpps';
export default class OpportunityList extends LightningElement {
@track columns = [
{
label: 'Opportunity name',
fieldName: 'nameUrl',
type: 'url',
typeAttributes: {label: { fieldName: 'Name' },
target: '_blank'},
sortable: true
},
{
label: 'Stage Name',
fieldName: 'StageName',
type: 'text',
sortable: true
},
{
label: 'Close date',
fieldName: 'CloseDate',
type: 'date',
sortable: true
}
];
@track error;
@track opportunities = [];
@wire(getAllOpps)
wiredOpps(result) {
const { data, error } = result;
if(data) {
let nameUrl;
this.opportunities = data.map(row => {
nameUrl = `/${row.Id}`;
return {...row , nameUrl}
})
this.error = null;
}
if(error) {
this.error = error;
this.opportunities = [];
}
}
}
Apex class
public with sharing class GetAllOpportunities {
@AuraEnabled(cacheable=true)
public static List<Opportunity> getAllOpps() {
return [SELECT Id, Name ,StageName, CloseDate FROM Opportunity Order By Name asc];
}
}
Best Answer
...
is only a shallow copy; the individual elements ofrealRecords
are also write-protected. We can use{...obj1, ...obj2}
as a type of Object.assign. This reduces your code to just: