Simple Confirmation Modals in Lightning

If you’re writing custom Lightning components you’ll eventually need to get a confirmation from the user before proceeding with some action. Perhaps you’ll need to ask approval before deleting a record, or maybe you’ll need final confirmation before launching the missiles. Either way - it’s going to happen and Salesforce isn’t helping you get the job done.

While Salesforce does provide a ton of super useful lighting components they don’t have any out of the box option that gives you power over the YES and the NO.

Don’t Fret!

Fear not - with a little JavaScript and some CSS styling (via the Lightning Design System) we can cook up a pretty spicy confirmation modal that’s rich with functionality and loaded to the top with configuration. Even better, our modal won’t leave your feeling bloated, stuffed, frustrated, or confused like those highly-processed modals you get on the sampler plate from your local fixer site.

Check this bad boy out…

As Easy as Pie

This thing isn’t a 12-speed Cuisinart, but some basic instructions are still needed. If you’re already a master chef at your keyboard, feel free to jump ahead and get the complete source for the component at the end.

The markup to add the confirmation modal to your component is simple…

<c-confirmation-modal
    title="Confirm"
    message="Are you sure you want to continue?"
>
</c-confirmation-modal>

Presenting the modal is just as easy. First get a handle on the component, then invoke the present method…

this.template.querySelector('c-confirmation-modal').present();

Options

The modal’s title, message, and button labels can all be customized. All options are.. uhh.. optional.

They can either be configured in the markup…

<c-confirmation-modal
    title="Confirm sanity to continue"
    message="Do you like pizza?"
    cancel-label="Nope"
    confirm-label="Yep"
>
</c-confirmation-modal>

… or in javascript when the modal is presented…

this.template.querySelector('c-confirmation-modal').present({
    title: 'Confirm sanity to continue',
    message: 'Do you like pizza?',
    cancelLabel: 'Nope',
    confirmLabel: 'Yep'
});

Handling the Modal Response

Lesser modals might require you to pass a function callback or some complicated state attributes to the component, making handling the user’s interaction complicated or kludgy. The real power in this component is that we simply invoke the present() method and wait for a response. If the user pressed the confirm button, the modal’s promise resolves with a true value. If the user canceled the modal it resolves with a false value.

this.template.querySelector('c-confirmation-modal').present()
.then((response) => {
	if (response) {
		// user confirmed
    } else {
		// user canceled
    }
});

If you really want to impress your friends you can get fancy and use async/await…

async getConfirmation() {
	const response = await this.template.querySelector('c-confirmation-modal').present();
	if (response) {
		// user confirmed
	} else {
		// user canceled
	}
}

Multiple Modals in the same component

Have a use case that requires multiple confirmation modals? I’ve got you covered, boss. Everything works the same except that you’ll first have to give each confirmation-modal component a unique data-id

<c-confirmation-modal
    data-id="pizzaModal"
    title="Confirm sanity to continue"
    message="Do you like pizza?"
>
</c-confirmation-modal>

<c-confirmation-modal
    data-id="caffeineModal"
    title="Caffinated?"
    message="Have you had your fresh pots today?"
>
</c-confirmation-modal>

You then simply use a different querySelector to get a handle on the appropriate confirmation modal…

const response = await this.template.querySelector('[data-id="pizzaModal"]').present();

Show Me the Code!

Ok, enough of my jibber-jabber. You’re ready for those sweet bits and bytes. Enjoy and code responsibly!

<template>
	<lightning-card if:true={visible}>
		<div class="slds-container_small">
			<section role="dialog" tabindex="-1" class="slds-modal slds-fade-in-open">
				<div class="slds-modal__container">
					<header class="slds-modal__header">
						<h2 class="slds-text-heading_medium slds-hyphenate">{title}</h2>
					</header>
					<div class="slds-modal__content slds-p-around_medium">
						<p>{message}</p>
					</div>
					<footer class="slds-modal__footer">
						<lightning-button variant="neutral"
						                  name="cancel"
						                  label={cancelLabel}
						                  title={cancelLabel}
						                  onclick={handleCancel} ></lightning-button>

						<lightning-button variant="brand"
						                  name="confirm"
						                  label={confirmLabel}
						                  title={confirmLabel}
						                  class="slds-m-left_x-small"
						                  onclick={handleConfirm} ></lightning-button>
					</footer>
				</div>
			</section>
			<div class="slds-backdrop slds-backdrop_open"></div>
		</div>
	</lightning-card>
</template>

/**
 * MIT License
 *
 * Copyright (c) 2021 Restless Labs, LLC
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
import {LightningElement, api} from 'lwc';

export default class ConfirmationModal extends LightningElement {
	visible;                       // used to hide/show modal
	@api title = 'Confirm';        // modal title
	@api message = 'Continue?';    // modal message
	@api confirmLabel = 'Ok';      // confirm button label
	@api cancelLabel = 'Cancel';   // cancel button label

	resolvePromise;
	rejectPromise;


	/***************** METHODS *****************/
	/**
	 * Displays the confirmation modal with any passed parameters
	 * @param options {Object} - optional parameters to be set on the modal
	 * @returns {Promise} - a promise that resolves when the modal is closed
	 */
	@api
	async present(options) {
		return new Promise((resolve, reject) => {
			this.resolvePromise = resolve;
			this.rejectPromise = reject;

			if (options) {
				this.setPropertyFromOptions(options, 'title');
				this.setPropertyFromOptions(options, 'message');
				this.setPropertyFromOptions(options, 'confirmLabel');
				this.setPropertyFromOptions(options, 'cancelLabel');
			}

			this.visible = true;
		});
	}


	/**
	 * Closes the modal and resets default values
	 */
	@api
	close() {
		this.visible = false;
	}


	/**
	 * Utility method that sets a property on this component only if it exists in the options object
	 * @param options {Object} - the options object to pull the property value form
	 * @param property {String} - the property to pull from the options and set on this component
	 */
	setPropertyFromOptions(options, property) {
		if (options.hasOwnProperty(property)) {
			this[property] = options[property];
		}
	}


	/**
	 * Resolves the modal promise with a true value and closes the modal
	 * @param event {Event} - the user event that triggered the callback
	 */
	handleConfirm(event) {
		this.resolvePromise(true);
		this.close();
	}


	/**
	 * Resolves the modal promise with a false value and closes the modal
	 * @param event {Event} - the user event that triggered the callback
	 */
	handleCancel(event) {
		this.resolvePromise(false);
		this.close();
	}
}

Who We Are

At Restless Labs we have spent a significant part of the past decades offering digital consulting services to companies in a variety of industries - with a specialization in custom software solutions.

For more information about how we can help you, contact us today and discover what awesome things we can build together.

Brad Bisinger Principal Consultant