5

I am struggling to find any syntax documentation related to building a Web Component, and could really use some help.

For example, in my JS file I make a call to a custom APEX api that returns a List of a custom object:

@wire(getCourseData, { studentId: '$recordId' }) studentCourseRelationships;

And then I would like to iterate over that List and display the data in an accordion, but only if the Lists is not empty:

<template>
    <lightning-card title="Courses" icon-name="custom:custom14">
        <div class="slds-m-around_medium">
            <template if:true={studentCourseRelationships.data}>
                <p>HEYO</p>
                <lightning-accordion allow-multiple-sections-open>
                    <template for:each={studentCourseRelationships.data} for:item="rel">
                        <lightning-accordion-section key={rel.Id} name={rel.Name} label={rel.Course__r.Name}>
                            <p>This is the content area for section A.</p>
                            <p>Donec vitae tellus egestas, faucibus ipsum ac, imperdiet erat. Nam venenatis non ante at sagittis. Integer vel purus eget nunc semper placerat. Nam tristique quam leo, et posuere enim condimentum quis. Ut sagittis libero id lectus tempor maximus. Nunc ut tincidunt eros, a hendrerit leo. Suspendisse quis fermentum dolor. Nulla euismod consectetur leo, id condimentum nunc consequat quis.</p>
                        </lightning-accordion-section>
                    </template>      
                </lightning-accordion>
            </template>
            <template if:false={studentCourseRelationships.data}>
                <p>Student is not enrolled in any courses</p>
            </template>
        </div>
    </lightning-card>
</template>

However, the <template if:true={studentCourseRelationships.data}> piece is always true even if the list is empty.

I tried doing a isEmpty(studentCourseRelationships.data) but that throws an error. In my JS I tried creating a boolean to examine it:

get hasCourses() {
    return (this.studentCourseRelationships.data.length > 0);
}

I get the error that this.studentCourseRelationships.data.length is undefined...

I just can't seem to find anything that tells you how to manipulate the data returned by your APEX methods. Do I need to convert studentCourseRelationships.data to a different data type before evaluating in JS? I have been able to find some syntax information on aura components, but not web components...

  • 1
    there is a migration guide on LWC syntax for expressions Migrate Conditionals, not sure this is what you are looking for – glls Feb 21 at 1:16
4

UPDATE

My initial understanding of the wired property was incorrect.

While trying to reproduce the issue, I had used data.length and thus had come to the conclusion that unless the wired property is referred in HTML, it does not get populated. Whereas in reality, once the component is rendered, the wired property does get populated.

Upon testing further found that the actual issue is that data is returned as JSON, and that length is not a valid property on the JSON being returned, which causes an error. So to verify if the property is blank or not, the approach that should be used here should be as:

if(this.studentCourseRelationships.data !== undefined) {
    if(JSON.stringify(this.contacts.data).length > 0);
          return true; // data populated
} else {
    return false; // no data
}

OR

Just use undefined

if(this.studentCourseRelationships.data !== undefined) {
    return true; // data populated
} else {
    return false; // no data
}

UPDATE

Calling Apex Method Imperatively

While you can wire an Apex method, you can also call it imperatively. To do so, you will need to utilize it in your hasCourses() function as below, and then verify it on HTML. This way you don't need to go through the approach mentioned earlier in the answer.

// declare a tracked variable
@track myStudentCourseRelationships;

get handleCourses() {

    // invoke the Apex method here
    getCourseData()
        .then(result => {
            this.myStudentCourseRelationships = result;
        })
        .catch(error => {
            this.error = error;
        });

    // myStudentCourseRelationships will be returned as a JSON
    // verify if you get a valid JSON, then return accordingly
}

Contents not valid have been striked-off and updated with relevant findings.

Utilizing wired Apex method

After trying to reproduce the scenario, it seems the wired service does get invoked only when it is referred in the HTML. So in your scenario studentCourseRelationships gets populated as JSON only when it's referred in the HTML when it's referred as {studentCourseRelationships.data}. So if you try to find the length of this attribute before it getting populated, that's when you will receive the error.

The option that I could see working is to display the contents only when length is not 0, is by using something as below. The idea is to ensure that studentCourseRelationships gets populated before you call hasCourses which checks on a valid data.length.

<template if:true={studentCourseRelationships.data}>

    <template if:true={hasCourses}>
        ...
    </template>
</template>

I will continue to see if there's a better option.

  • Hmm, that's an interesting concept, thanks for sharing. It seems there must be an alternative, because what about that case that I need to make another wired service call based off of the results of the first wired service? Is such a thing possible? – BlondeSwan Feb 21 at 15:53
  • I haven't really explored to that extent, but it seems to be this case of as now. I will try to see how can you actually call the method without the need of referring it on HTML. – Jayant Das Feb 21 at 15:55
  • @BlondeSwan You can actually invoke an apex method imperatively. I have added some details in the answer. I think that should be your approach here. – Jayant Das Feb 21 at 16:16
  • @BlondeSwan I have found some interesting behavior which was not known at the time of writing this answer. I have updated it with the details, if that's still relevant to you. – Jayant Das Feb 25 at 20:34
  • so if you just change it to that getter, what do you do in the HTML? Just put like, <template if:true={handleCourses}>? – BlondeSwan Feb 26 at 18:51
2

You need to check if the objects are yet populated:

get hasCourses() {
  return this.studentCourseRelationships && 
         this.studentCourseRelationships.data && 
         this.studentCourseRelationships.data.length > 0;
}

The && operator evaluates the right-hand value only if the left-side is "truthy", and the > operator only returns true if the result is a number and is greater than 0.

  • So looking at @jayantdas's answer, it appears the wired service only gets invoked when it's referred in HTML. Are you suggesting that going through this process will populate the data as needed? – BlondeSwan Feb 21 at 15:50
  • @BlondeSwan Jayant is saying that the first time the getter is called, there won't be any data there yet. I'm saying that you need to check if there's any data there before attempting to use the data. Both solutions should be valid for your use case. – sfdcfox Feb 21 at 16:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.