1

I've a parent component containing a formattedRichText component and an edit button. Edit button onclick() calls a child component that hold a modal component. That modal component contains an inputRichText component.

When the edit button from parent cmp is clicked :

  1. The attribute value from the parent component is set with the formattedRichText ouput.
  2. The child cmp opens the modal with the input rich text editor prepolulated with the attribute value.
  3. When the users click on the modal's save button, the value is sent back to the parent modal via a component event and printed in the formattedRichText cmp.

Evrything works well the first time i execute the process above, but for the next times, i get the error below :

Uncaught Error in $A.getCallback() [Cannot read property 'querySelector' of null] []1

Below is my parent.cmp component :

...
<aura:attribute name="value" type="String"/>

<lightning:buttonIcon iconName="utility:edit" aura:id="Name" ariaLabel="Lorem Ipsum" variant="border-filled"  onclick="{!c.handleEdit}" alternativeText="Editing 'Title'" />
<div class="slds-text-body_regular slds-text-align_left">
    <lightning:formattedRichText class="slds-text-heading_medium" aura:id="fieldNameValue" value="Edited Text" />                            
</div>
         <c:childComponent aura:id="customModalComponent" value="{!v.value}"/>

ParentController.js

//Prepoulate inputRichText child component.
...
component.set('v.value', 'formattedRichTextValue')

And the childComponent.cmp

...
<lightning:inputRichText aura:id="inputRichText" value="{!v.value}" placeholder="Type something interesting"/>

childComponentController.js

//send edited value to parent

...
var value = component.get('v.richTextValue');

var sendField = component.getEvent('updateField');

sendField.setParams({'richTextValue': value});

sendField.fire()

Could you please help me understand what i'm doing wrong so that i can solve this issue?

Thanks a lot!

3

I would suggest to dynamically create your child component when the user clicks the 'Edit' button. And when he clicks 'Save' you can use a Lightning Event to trigger the parent component to fetch the data again from the server and use a component.destroy() to remove your child component from the DOM. That should work all the time.

If needed I can work out a small demo component so you can understand this further. Let me know.

1
  • Thank you @Bram for the reply! So far i've used the workaround below and it seems to do the trick : window.setTimeout( $A.getCallback(function() { component.set(vProvider, value); }), 1000 ); Eventhought i think it's a bit dirty...So i'll really appreciate you demo! that will inspire me to do something real clean!
    – Bryce
    Aug 29 '18 at 23:45
1

Here is a very simple example of a parent component that dynamically creates a component (Popup modal). I've also included a Lightning Application Event to refresh the data as soon as the Popup modal is destroyed. You'll see in the example below.

Consideration: I did not test these components nor functionality. This is also a very minimal example with no actual functionality. That you'll have to write yourself.

Parent Component UI

<!-- MyParentComponent -->
<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId" controller="MyController">
    <!-- Helper attributes -->
    <aura:attribute name="modalContainer" type="Aura.Component" access="private" />

    <!-- Event Listeners -->
    <aura:handler event="c:MyAppEvent" action="{!c.handleApplicationEvent}"/>

    <!-- UI -->
    <lightning:button label="Open Modal" onclick="{!c.openModal}" />

    {!v.modalContainer}
</aura:component>

Parent Component JS Controller

/**
    MyParentComponentController.js
 */
({
    doInit : function(component, event, helper) {
        // Fetch the data from the server and display to the user
        helper.getData(component, event, helper);
    },
    openModal : function(component, event, helper) {
        $A.createComponent("c:MyChildComponent",
            { 
                recordId: component.get('v.recordId'),
                // More input parameters can be given here
            },
            function(popupModal, status, errorMessage) {
                if (status === "SUCCESS") {
                    component.set("v.modalContainer", popupModal);
                } else if (status === "INCOMPLETE") {
                    // Do something on incomplete; like a toast message
                } else if (status === "ERROR") {
                    // Do something on error; like a toast message
                }
         });
    },
    handleApplicationEvent : function(component, event, helper) {
        var target = event.getParam("target");
        var action = event.getParam("action");

        if('child-component' == target && 'refresh-data' == action) {
            helper.getDate(component, event, helper); // Fetch again the updated data from the server reusing the same method as called in the init method         
        }
    }
})

Lightning Application Event

<!-- Lightning Application Event: MyAppEvent -->
<aura:event type="APPLICATION">
    <aura:attribute name="target" type="String" />
    <aura:attribute name="action" type="String" />
</aura:event>

Child Component UI

<!-- MyChildComponent (The modal) -->
<aura:component controller="MyController">
    <!-- Input attributes -->
    <aura:attribute name="recordId" type="String" access="public" description="Record Id that is passed from MyParentController" />
    <!-- Include more input attributes here -->

    <!-- Event Notifiers -->
    <aura:registerEvent name="myAppEvent" type="c:MyAppEvent"/>

    <!-- Handlers -->
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
    <!-- Handler when this component is destroyed. Used to refresh the data in the parent component -->
    <aura:handler name="destroy" value="{!this}" action="{!c.handleDestroy}" />

    <!-- UI -->
    <section role="dialog" tabindex="-1" class="slds-modal slds-fade-in-open">
        <div class="slds-modal__container">
            <header class="slds-modal__header">
                <lightning:buttonIcon iconName="utility:close" alternativeText="Close" onclick="{!c.closeOrCancel}" size="large" class="slds-modal__close" variant="bare-inverse"/>
                <h2 class="slds-text-heading_medium">My Awesome Modal</h2>
            </header>
            <div class="slds-modal__content slds-is-relative" style="min-height: 15rem;">

                <lightning:inputRichText value="" placeholder="Type something interesting"/>

            </div>

            <footer class="slds-modal__footer">
                <lightning:button variant="neutral" label="Cancel" onclick="{!c.closeOrCancel}" />
                <lightning:button variant="brand" label="Save" onclick="{!c.save}" />
            </footer>
        </div>
    </section>
    <div class="slds-backdrop slds-backdrop_open"></div>
</aura:component>

Child Component JS Controller

/**
    MyChildComponentController.js
 */
({
    doInit : function(component, event, helper) {
        // Get the data to show in your lightning:inputRichText
    },
    closeOrCancel : function(component, event, helper) {
        // Destroy the modal. When destroyed the handleDestroy() method will be called to refresh the data
        component.destroy();
    },
    save : function(component, event, helper) {
        // Save to the database and optionally call: component.destroy(); to close the modal.
    },

    handleDestroy : function(component, event, helper) {
        $A.get('e.force:refreshView').fire();
        var appEvent = $A.get("e.c:MyAppEvent");
        appEvent.setParams({
            "target" : "child-component",
            "action" : "refresh-data"
        });
        appEvent.fire();
    }
})
0

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.