Advanced 35 min read

Project: Contact Management Application

By the end of this lesson, you'll be able to:

  • Pass an Account context from a parent to a Contact-list child component
  • Create and delete Contacts scoped to that Account
  • Navigate to a Contact's record page from the list

Prerequisites: "Project: Account Search Tool"

What We're Building

A parent accountDetail component passing its recordId down to a child contactList component (Module 5's @api pattern), which lists, creates, deletes, and navigates to Contacts scoped to that Account.

The Child Component

// contactList.js
import { LightningElement, api, wire } from 'lwc';
import { NavigationMixin } from 'lightning/navigation';
import { createRecord, deleteRecord } from 'lightning/uiRecordApi';
import { refreshApex } from '@salesforce/apex';
import getContactsForAccount from '@salesforce/apex/ContactController.getContactsForAccount';
import CONTACT_OBJECT from '@salesforce/schema/Contact';
import LAST_NAME_FIELD from '@salesforce/schema/Contact.LastName';
import ACCOUNT_FIELD from '@salesforce/schema/Contact.AccountId';

export default class ContactList extends NavigationMixin(LightningElement) {
    @api accountId;
    wiredContactsResult;

    @wire(getContactsForAccount, { accountId: '$accountId' })
    wiredContacts(result) {
        this.wiredContactsResult = result;
    }

    get contacts() {
        return this.wiredContactsResult?.data ?? [];
    }

    handleViewContact(event) {
        this[NavigationMixin.Navigate]({
            type: 'standard__recordPage',
            attributes: {
                recordId: event.currentTarget.dataset.id,
                objectApiName: 'Contact',
                actionName: 'view',
            },
        });
    }

    async handleAddContact(lastName) {
        await createRecord({
            apiName: CONTACT_OBJECT.objectApiName,
            fields: {
                [LAST_NAME_FIELD.fieldApiName]: lastName,
                [ACCOUNT_FIELD.fieldApiName]: this.accountId,
            },
        });
        await refreshApex(this.wiredContactsResult);
    }

    async handleDeleteContact(contactId) {
        await deleteRecord(contactId);
        await refreshApex(this.wiredContactsResult);
    }
}

accountId: '$accountId' (Module 6) makes the wire re-fetch automatically if a parent ever swaps to a different Account entirely — the reactive parameter pattern applied to a value received from a parent, not just a local property.

Exercise

Write the parent template markup passing recordId into contactList's accountId property.

Show hint

Recall Module 5's @api property-passing syntax.

JAVASCRIPT

Exercise

Challenge: explain, as a comment, why ACCOUNT_FIELD is included in the createRecord fields for a new Contact.

Show hint

Think about what actually associates the new Contact with its parent Account.

JAVASCRIPT

Project: Contact Management Application Quiz

1. How does the child component receive which Account it's scoped to?

2. What makes the Contacts wire re-fetch if the parent ever passes a different Account?

3. What navigates to a selected Contact's record page?

4. What field associates a new Contact with the current Account?

5. What refreshes the Contact list after a delete?

Log in to submit the quiz and save your score.

My Notes

Log in to keep private notes on this lesson.

Questions about this lesson

No questions yet — be the first to ask.

Log in to ask a question about this lesson.

Summary

A parent Account context flows down to a child Contact list via @api, tying Module 5's communication pattern together with LDS create/delete and Module 9's navigation.