I'm new to LWC. And after going through the entire "Build Lightning Web Components" trail, I must still be missing something fundamental about how the uiRecordApi getRecord
method works.
When using @wire(getRecord, ...)
, I get back an object with data as expected. But I can't access that data in the way I'm used to. Dot notation, Object.keys()
, and even JSON.parse(JSON.stringify(Object))
are failing in ways that suggest that the object is undefined in some cases.
I have in my Javascript controller:
import { LightningElement, api, track, wire } from 'lwc';
import { getRecord, getFieldValue } from 'lightning/uiRecordApi';
// ...
import APP_NAME from '@salesforce/schema/Application__c.Name';
// etc...
export default class myApplicationThing extends LightningElement {
@api recordId; // Application__c.Id
@track app; // Application__c record
@track appName;
// ...
@wire(getRecord, { recordId: '$recordId', fields })
loadRecord ({ error, data }) {
if (data) {
this.app = data;
this.error = undefined;
this.appName = getFieldValue(data, APP_NAME); // I can use the value
// of appName just fine if I get it this way, e.g. with {appName}
// in the HTML template
// ...
} else if (error) {
// ...
}
}
// Called from a {testMe} in the HTML template
get testMe() {
console.log(this.app); // returns a Proxy
console.log(JSON.stringify(this.app)); // returns a JSON as expected:
/*
{
"apiName": "Application__c",
"childRelationships": {},
"fields": {
"Name": {
"displayValue": null,
"value": "A-0801096"
},
"Service_Area__c": {
"displayValue": null,
"value": "UT"
},
...
},
...
} */
console.log(this.app.fields.Name.value); // throws error:
/* afterRender threw an error in 'c:myApplicationThing'
[Cannot read property 'fields' of undefined] */
console.log(this.app.keys()); // throws error:
/* Cannot read property 'keys' of undefined */
console.log(JSON.parse(JSON.stringify(this.app))); // throws error:
/* Unexpected token u in JSON at position 0 */
return '?!';
}
I could theoretically use getFieldValue
to get all the field values on my record, but I'm building up a large form with lots of fields, and trying to nest components within components according to best practice. So in reality this would mean calling out each separate field a whole bunch of times in different places in a way that is super verbose and hard to maintain. I'd much rather pass an object around vs. passing around dozens of primitives.
Also... I read this thread, and while Kevin's answer seems to be getting at something, I'm still thrown by the fact that my first 2 console log examples above do return the value I was expecting.
Am I way off the deep end? What's the right way to go about this?
Solution
By the time I got the answer from @BritishBoyinDC, I had been pondering the nature of ansync wired data, and I solved my problem by wrapping the entire component HTML like so:
<template>
<template if:true={app}>
...
</template>
</template>
Everything inside that if:true directive that relies on the wired record data holds it horses and waits for the data to show up. :-)
By the way, console.log(this.app)
and console.log(JSON.stringify(this.app))
work because you can both log and JSON-stringify an undefined object in JavaScript without incurring an error. On the other hand, if you try to access properties on an undefined object, an error comes up.