I have a modal component where I am passing in two-button handler methods which are callbacks.
These two buttons are hooked up to a getter with the intent that I can override them, which I am having trouble doing.
Code Sample:
export default class CreateCaseApp extends LightningElement {
// Default button handlers
_saveHandler = {
cb: this._modalSaveHandler,
label: "Create Case",
variant: "brand",
};
_cancelHandler = {
cb: this._modalCancelHandler,
label: "Cancel",
variant: "netrual",
class: "btn-left",
};
/**
* Getter: Save handler callback and button props
*/
get saveHandler() {
return this._saveHandler;
}
/**
* Getter: Cancel handler callback and button props
*/
get cancelHandler() {
return this._cancelHandler;
}
/**
* Save event send to modal
* @param {*} event
*/
_modalSaveHandler = (event) => {
console.log("Save Clicked");
};
/**
* Handler event sent to modal
* @param {*} event
*/
_modalCancelHandler = (event) => {
console.log("Cancel Clicked");
};
// Questionable code below
/**
* Handler event sent to modal for override
* @param {*} event
*/
_modalOverrideHandler = (event) => {
console.log("Override Clicked");
};
onDispatchedEventFromChild(event) {
if (event.detail == "nodata") {
// Override save handler button
this._saveHandler = {
cb: this._modalOverrideHandler,
label: "Okay",
variant: "brand"
};
}
}
}
In the above code, the initial saveHandler()
and cancelHandler()
are passing the fat arrow methods correctly via the cb
property in the object. I can click on these buttons in the child component and they execute the method from this parent.
In the bottom section of code, I have an event listener onDispatchedEventFromChild()
that receives data from a child component. When the logic in this method is met, I am trying to override the _saveHandler
button by setting it with this._saveHandler={...}
.
This updates the button details such as label and variant, but when I click on it, the new referenced callback isn't being fired.
Is this possible to do, or am I approaching this incorrectly? I tried using @track
on the two variables, but that also didn't work.
Edit:
Parent Component HTML
<template>
<c-modal
modal-header={modalHeader}
modal-save-handler={saveHandler}
>
<div
slot="modalContent"
class="modalContent slds-modal__content slds-p-around_medium"
>
<!-- Wrapper -->
<c-create-case-wrapper
contact-id={recordId}
onmodalheaderupdate={handleModalHeaderEvent}
onmodalhandlerupdate={handleModalHandlerEvent}
></c-create-case-wrapper>
<!-- Wrapper -->
</div>
</c-modal>
<!-- Main Modal Launch Button -->
<lightning-button
variant="brand"
label="Create Enterprise Case"
title="Create Enterprise Case"
onclick={handleClick}
class="slds-m-left_x-small"
></lightning-button>
<!-- Main Modal Launch Button -->
</template>
The parent is providing the callback for the button click within the modal component. When a button is clicked, the parent executes that method (in the parent).
In this example, the <c-create-case-wrapper>
is also loading child components based on logic. When certain logic is met within those components, I need to change the button callback and label. This is where a child would dispatch an event to the parent so that I know I need to update the modal button.
I assumed that since the button was being passed to the modal reactively through the getter that I could just update that object/callback to override its defaults. This works for the label and variant properties just fine, its passing another/different callback that is not working.
Best Answer
There are a few ways to accomplish your requirement as mentioned below. Please note that code snippets are based on the github repo you pointed out and hence, you'll have to tweak it a bit if needed. Also, I've only given the code snippets that are to be added or updated (rest of your code would remain as-is).
Option 1:
In the parent component, introduce a private boolean field
overrideSave
and a method_modalSaveDefaultHandler
. Update_modalSaveHandler
code to execute based on the boolean flag value. UpdateonDispatchedEventFromChild
code to toggle the boolean flag based on your specific conditions met. Now, the child modal component will get the callback reference to both_modalSaveDefaultHandler
&_modalOverrideHandler
through_modalSaveHandler
.Option 2:
In the modal component HTML, you probably have the click event wired like this
<button class="slds-button slds-button_brand save" onclick={modalSaveHandler.cb}>
. Replace this with<button data-id="modalSaveBtn" class="slds-button slds-button_brand save">
. Note that we have removed the declarative event wiring and will be doing it programmatically instead. Also, we have included adata-
attribute to be able to query for the specific button element via code.In the modal component JS, introduce a new private field
handleRef
which will be used to hold the reference to the callback. Modify therenderedCallback
method to pass the callback reference (frommodalSaveHandler
public property) tohandleRef
and programmatically add the click event handler on the specific button. Add a new public methodupdateRegisteredEvent
which will be called by the parent component and this method will be responsible for removing the old event listener as well as adding new event listener.In the parent component JS, update
onDispatchedEventFromChild
code to call the child component's public method (passing the override method as input param).Option 3:
In the modal component JS, define the event handler methods and hook them up in the HTML
onclick
attribute declaratively. The event handler code should only pass the modal component details (or form details) to the parent component via custom event. So, it doesn't know how the data will be processed by the parent component.In the parent component, hook up event handlers corresponding to the custom events (dispatched by modal component) and implement your custom logic in these event handler based on your requirements (and specific conditions to be met).
Not providing any code samples here because this is the straight-forward LWC way of event communication and hence, should be easier to do. I would suggest to take this approach since its inline with LWC standard, defines specific function/ responsibility to each component and can be developed as reusable components.
Additional Info: In your current code, the callback function is passed via
saveHandler
property during the component initialization and both the components go thru connected- and rendered- callback lifecycle hooks. So, the LWC init and render lifecycle hooks wire up the callback to the button element's click event. However, whenonDispatchedEventFromChild
execution updates the callback via object literal (this._savehandler
), only therenderedCallback
lifecycle hook executes on both the components. This basically re-evaluates the UI elements and rerenders them, but doesn't do anything about the callback fn wiring to the button click event. This could be because of the way LWC event registration works internally.