Kishore
Kishore I am a Salesforce developer and a so-called blogger at SalesforceLwc.in. I love learning and sharing. SalesforceLWC aims to share relevant knowledge, examples and scenarios for understanding Salesforce Lightning Web Components and AURA.

Create a Custom Component to Send an Email in Salesforce Lightning


Create a Custom Component to Send an Email in Salesforce Lightning

Summary

SCENARIO

Create a custom email wizard which allows the users to notify respective contacts and other users about some event in contact record detail page. Show toast messages on success and failure of sending an email.

In this post we are going to see a custom component which can send emails with attachments. We will use Apex SingleEmailMessage. When we pass a list of contact or user record Id’s, it automatically fetches emails for those record, making our life easy.

Create a Custom Component to Send an Email in Salesforce Lightning

Controllers:

  • NotifyController.apxc

Components:

  • NotifyModal.cmp
  • NotifyModal.js

  • CustomLookUpComp.cmp
  • CustomLookUpComp.js

  • selectAttachments.cmp
  • selectAttachments.js

  • AttachmentUploader.cmp
  • AttachmentUploader.js

Events:

  • CustomLookUpFinalValueEvent.evt
  • selectedAttachmentsNotifyEvent.evt

Create a Custom Component to Send an Email in Salesforce Lightning

NotifyModal.cmp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
<!-- NotifyModal -->
<aura:component controller="NotifyController" implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,force:lightningQuickActionWithoutHeader" access="global" >
    
    
    
    <aura:attribute name="recordId" type="Id"/>
    <aura:attribute name="open" type="Boolean" default="false"/>
    <aura:attribute name="ownerId" type="Map[]"/>
    <aura:attribute name="selected" type="Map[]"/>
    <aura:attribute name="fvalues" type="Map[]"/>
    <aura:attribute name="cCUser" type="Map[]"/>
    <aura:attribute name="cCContact" type="Map[]"/>
    <aura:attribute name="toUser" type="Map[]"/>
    <aura:attribute name="toContact" type="Map[]"/>
    <aura:attribute name="subject" type="String"/>
    <aura:attribute name="body" type="String"/>
    
    <aura:handler name="init" value="{!this}" action="{!c.doinit}"/>
 
    
    <!-- Handling to get Final values of lookups -->
    <aura:handler name="CustomLookUpFinalValueEvent" event="c:CustomLookUpFinalValueEvent" action="{!c.finalValueAction}"/>
    <!-- handling onselect values while attaching docs -->
    <aura:handler name="selectedAttachmentsNotifyEvent" event="c:selectedAttachmentsNotifyEvent" action="{!c.selectedAction}"/>
    
    <header class="slds-modal__header slds-p-bottom_large">
        
        <h2 id="modal-heading-01" class="slds-text-heading_medium slds-hyphenate">Notify</h2>
    </header>
    
    
    <div class="slds-grid slds-gutters slds-p-bottom_large slds-p-top_large"> 
        <div class="slds-col slds-size_2-of-12">
            <div class="slds-text-heading_small">To:</div>
        </div>
        <div aura:id="toContact" class="slds-col slds-size_5-of-12">
            <c:CustomLookUpComp uniqueName="toContact"
                                iconName="standard:contact"
                                methodName="c.getContacts"
                                singleSelect="false"
                                sObject="Contact"
                                />   
        </div>
        <div class="slds-col slds-size_5-of-12">
            <c:CustomLookUpComp uniqueName="toUser"
                                iconName="standard:user"
                                methodName="c.getUsers"
                                singleSelect="false"
                                sObject="User"
                                />   
        </div>
    </div>
    <div class="slds-grid slds-gutters"> 
        <div class="slds-col slds-size_2-of-12">
            <div class="slds-text-heading_small">cC:</div>
        </div>
        <div class="slds-col slds-size_5-of-12">
            <c:CustomLookUpComp uniqueName="cCContact"
                                iconName="standard:contact"
                                methodName="c.getContacts"
                                singleSelect="false"
                                sObject="Contact"
                                />   
        </div>
        <div class="slds-col slds-size_5-of-12">
            <c:CustomLookUpComp aura:id="Record"
                                uniqueName="cCUser"
                                iconName="standard:user"
                                methodName="c.getUsers"
                                singleSelect="false"
                                sObject="User"
                                selected="{!v.ownerId}"
                                />   
        </div>
    </div>
    
    <div class="slds-grid slds-gutters slds-p-top_large slds-p-bottom_large"> 
        <div class="slds-col slds-size_2-of-12">
            <div class="slds-text-heading_small">Subject:</div>
        </div>
        <div class="slds-col slds-size_10-of-12">
            <lightning:input variant="label-hidden" value="{!v.subject}" placeholder="Subject"/>
        </div>
    </div>
    
    <div class="slds-grid slds-gutters slds-p-top_large"> 
        <div class="slds-col slds-size_2-of-12">
            <div class="slds-text-heading_small">Message:</div>
        </div>
        <div class="slds-col slds-size_10-of-12">
            <lightning:inputRichText value="{!v.body}" placeholder="Email body...."/>
        </div>
    </div>
    
    <div class="slds-p-left_medium">
        <aura:iteration items="{!v.selected}" var="l">
            <li class="slds-listbox-item slds-truncate" role="presentation"> 
                <aura:if isTrue="{! v.selected}">
                    <lightning:pill class="" label="{!l.Title}" name="{!l}" onremove="{! c.handleRemoveOnly }" href="{!'/'+(l.Id)}"/>
                    
                </aura:if>
            </li>
        </aura:iteration>
    </div>
    

    
    <div class="slds-button-group slds-m-top_large slds-p-right_medium slds-float_right" style="margin-top: 2rem;" role="group">
		<lightning:button class="slds-m-top_medium slds-float_right" label="Attach" onclick="{!c.toOpenAttachments}"/>
        <lightning:button class="slds-m-top_medium slds-float_right" variant="brand" label="Notify" onclick="{!c.sendMail}"/>
    </div>
    
    <div class="slds-button slds-m-top_medium slds-float_right">
        <c:AttachmentUploader recordId="{!v.recordId}"/>
    </div>
    
    
    <c:selectAttachments isOpen="{!v.open}" recordId="{!v.recordId}"/> 
    <lightning:notificationsLibrary aura:id="notifLib"/>
    
</aura:component>


NotifyModal.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
({
    doinit : function(component, event, helper) {
        
        console.log("In Notify Doinit");
        var recid = component.get("v.recordId");
        console.log("recId:: "+recid);
        if(recid){
            var action = component.get("c.getContactOwners");
            action.setParams({
                recId : recid
            });
            
            action.setCallback(this, function(response){
                console.log("In call back doinit notify");
                var state = response.getState();
                console.log(state);
                if(state === "SUCCESS"){
                    var result = response.getReturnValue();
                    console.log("Result");
                    console.log(JSON.stringify(result));
                    component.set("v.ownerId", result);
                    
                    console.log("owner result");
                    console.log(result);
                    var childComp = component.find("Record");
                    console.log("childComp");
                    console.log(childComp);
                    childComp.prePopulateMethod(result);
                    
                }
               
            });
            $A.enqueueAction(action);
        }
    },
    
    finalValueAction : function(component, event, helper) {
        console.log("Handling Final value event");
        var values = event.getParams();
        console.log(JSON.stringify(values));
        
        //debugger;
        
        if(values.uniqueName === "cCUser"){
            console.log(values.finalValue.length);
            var cUser = [];
            for(var i = 0; i < values.finalValue.length; i++){
                
                cUser.push(values.finalValue[i].Id);
            }
            
            component.set("v.cCUser", cUser);   
            console.log("cUser");
            console.log(JSON.stringify(cUser));
        }
        
        if(values.uniqueName === "cCContact"){
            
            var cContact = [];
            for(var i = 0; i < values.finalValue.length; i++){
                
                cContact.push(values.finalValue[i].Id);
            }
            
            component.set("v.cCContact", cContact);  
            console.log("cContact");
            console.log(JSON.stringify(cContact));
        }
        
        if(values.uniqueName === "toUser"){
            
            var tUser = [];
            for(var i = 0; i < values.finalValue.length; i++){
                
                tUser.push(values.finalValue[i].Id);
            }
            
            component.set("v.toUser", tUser);
            console.log("tUser");
            console.log(JSON.stringify(tUser));
        }
        
        if(values.uniqueName === "toContact"){
            
            var tContact = [];
            for(var i = 0; i < values.finalValue.length; i++){
                
                tContact.push(values.finalValue[i].Id);
            }
            
            component.set("v.toContact", tContact);
            console.log("tContact");
            console.log(JSON.stringify(tContact));
        }
        
    },
    
    sendMail : function(component, event, helper) {
        //debugger;
        var cU = component.get("v.cCUser");
        console.log(JSON.stringify(cU));
        var cC = component.get("v.cCContact");
        console.log(JSON.stringify(cC));
        var tU = component.get("v.toUser");
        console.log(JSON.stringify(tU));
        var tC = component.get("v.toContact");
        console.log(JSON.stringify(tC));
        
        console.log("In send mail action");
        let c = [];
        if(cU.length > 0){
            for(var i = 0; i < cU.length; i++){
                c.push(cU[i]);
            }
        }
        if(cC.length > 0){
            for(var i = 0; i < cC.length; i++){
                c.push(cC[i]);
            }
        }
        
        let t = [];
        if(tU.length > 0){
            for(var i = 0; i < tU.length; i++){
                t.push(tU[i]);
            }
        }
        if(tC.length > 0){
            for(var i = 0; i < tC.length; i++){
                t.push(tC[i]);
            }
        }
        console.log(JSON.stringify(c));
        console.log(JSON.stringify(t));
        var emailSubject = component.get("v.subject");
        console.log(emailSubject);
        var emailBody = component.get("v.body");
        console.log(emailBody);
        var fileIds = component.get("v.selected");
        var file = [];
        for(var i = 0; i < fileIds.length; i++){
            file.push(fileIds[i].Id);
        }
        console.log("file");
        console.log(file);
        
        var action = component.get("c.sendEmailApex");
        action.setParams({
            "toAddress" : t,
            "ccAddress" : c,
            "subject" : emailSubject,
            "body" : emailBody,
            "files" : file
        });
        
        action.setCallback(this, function(response){
            console.log("In call back of notify component on send button");
            var state = response.getState();
            console.log(state);
            if(state === "SUCCESS"){
                console.log("In success");
                /*var toast = $A.get("e.force:showToast");
                toast.setParams({
                    "title" : "Success!",
                    "message" : "Notify message sent successfully sent",
                    "type" : "success"
                });
                toast.fire();*/
                var message = component.find("notifLib").showNotice({
                    variant : "success",
                    header : "Message sent successfully",
                    message : "Notify message was sent successfully"
                    
                });
                 $A.get("e.force:closeQuickAction").fire();
                
            }else if(state === "ERROR"){
                console.log("In error");
                var errors = response.getError();
                console.log(errors);
                
                /*var toastEvent = $A.get("e.force:showToast");
                toastEvent.setParams({
                    "title" : "Error!",
                    "message" : errors[0].message,
                    "type" : "error"
                });
                toastEvent.fire();*/
                var message = component.find("notifLib").showNotice({
                    variant : "error",
                    header : "Error sending the message",
                    message : errors[0].message
                    
                });
                
            }
        });
        $A.enqueueAction(action);
    },
    
    toOpenAttachments : function(component, event, helper) {
        console.log("Opened small modal to select attachments");
        component.set("v.open", true);
    },
    
    selectedAction : function(component, event, helper) {
        console.log("Opened selectedAction");
        
        var select = event.getParam("selectedIds");
        component.set("v.selected", select);
        
    },
    
    handleRemoveOnly : function(component, event, helper) {
        console.log("in remove");
        var sel = event.getSource().get("v.name");
        console.log(JSON.stringify(sel));
        var lis = component.get("v.selected");
        console.log(JSON.stringify(lis));
        for(var i = 0; i < lis.length; i++){
            console.log(JSON.stringify(lis[i]));
            console.log(lis[i].Id);
            console.log(sel.Id);
            console.log(lis[i].Id == sel.Id);
            if(lis[i].Id == sel.Id){
                
                lis.splice(i,1);
            }
        }
        
        component.set("v.selected", lis);
        console.log(JSON.stringify(lis));
    },

})


CustomLookUpComp.cmp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
<!-- CustomLookUpComp -->
<aura:component controller="ContactController" implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" access="global" >
    
    <!-- Main attributes Start -->
    <aura:attribute name="uniqueName" type="String" required="true" default="lookup1"/><!--If not entered it takes default value-->
    <aura:attribute name="iconName" type="String" default="standard:contact" required="true"/>
    <aura:attribute name="methodName" type="String" default="c.getContacts" required="true"/>
    <aura:attribute name="singleSelect" type="Boolean" default="true"/>
    <aura:attribute name="sObject" type="String" default="Contact" required="true"/>
    <aura:attribute name="selected" type="Map[]"/><!--Optional If you want any pre-population can use this to pass that record value-->
    <!-- Main attributes End -->
    
    <aura:attribute name="sTerm" type="string" default=""/>
    <aura:attribute name="conList" type="Map[]"/>
    <aura:attribute name="blurTimeout" type="Integer"/>
    
    <!-- Register Final value Event -->
    <aura:registerEvent name="CustomLookUpFinalValueEvent" type="c:CustomLookUpFinalValueEvent"/>
    
    <!-- Handlers -->
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <aura:handler name="LookupEventToParent" event="c:LookupEventToParent" action="{!c.handleEvent}"/>
    
    <!-- Method to send pre-populate lookUp value to the requested component: parent to lookup-->
    <aura:method name="prePopulateMethod" action="{!c.render}">
        <aura:attribute name="populatedRecord" type="Map[]"/>
    </aura:method>
    
    <aura:if isTrue="{!v.singleSelect}">
        <div class="slds-form-element">
            <div class="slds-form-element__control">
                <div class="slds-combobox_container slds-has-selection">
                    <div aura:id="toOpen" class="slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click" aria-expanded="true" aria-haspopup="listbox" role="combobox">
                        <div class=" slds-input-has-icon slds-input-has-icon_right" role="none">
                            
                            <div>
                                <div aura:id="input" class="slds-p-top_none" >
                                    <lightning:input class="slds-p-top_none" variant="label-hidden" name="Search" value="{!v.sTerm}" onblur="{!c.onblur}" onclick="{!c.onfocus}" onchange="{!c.onchange}" placeholder="{!'Select ' + (v.sObject)}"/>
                                    
                                    <span class="slds-icon_container slds-icon-utility-search slds-input__icon slds-input__icon_right ">
                                        <span class="slds-icon slds-icon slds-icon_x-small slds-icon-text-default" aria-hidden="true">
                                            <lightning:icon class="" iconName="utility:search" size="x-small" alternativeText="Search" />
                                        </span>
                                    </span>
                                </div>
                                <aura:if isTrue="{!v.selected}">
                                    <div aura:id="lookup-pill" class="slds-pill-container slds-hide">
                                        <aura:iteration items="{!v.selected}" var="l">
                                            <lightning:pill class="pillSize" label="{!l.Name}" name="{!l}" onremove="{! c.handleRemoveOnly }" href="{!'/'+(l.Id)}">
                                                <aura:set attribute="media">
                                                <lightning:icon iconName="{!v.iconName}"  alternativeText="{!v.iconName}"/>
                                                </aura:set>
                                            </lightning:pill>
                                        </aura:iteration>
                                    </div>
                                </aura:if>
                            </div>
                            
                        </div>
                        <div onfocus="{!c.onfocus}" id="listbox-id-1" class="slds-dropdown slds-dropdown_length-with-icon-7 slds-dropdown_fluid" role="listbox">
                            <ul class="slds-listbox slds-listbox_vertical" role="presentation">
                                <aura:iteration items="{!v.conList}" var="listItem">
                                    <span ><c:childLookUp myContact="{!listItem}" icon="{!v.iconName}" object="{!v.sObject}"/></span>
                                </aura:iteration>
                                
                            </ul>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        
        <!-- Multi Select Start-->
        <aura:set attribute="else">
            <div class="slds-form-element">
                <div class="slds-form-element__control">
                    <div class="slds-combobox_container slds-has-selection">
                        <div aura:id="toOpen" class="slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click" aria-expanded="true" aria-haspopup="listbox" role="combobox">
                            <div class=" slds-input-has-icon slds-input-has-icon_right" role="none">
                                
                                <div>
                                    
                                    <lightning:input class="slds-p-top_none" variant="label-hidden" name="Search" value="{!v.sTerm}" onblur="{!c.onblur}" onclick="{!c.onfocus}" onchange="{!c.onchange}" placeholder="{!'Select ' + (v.sObject)}"/>
                                    
                                    <span class="slds-icon_container slds-icon-utility-search slds-input__icon slds-input__icon_right">
                                        <span class="slds-icon slds-icon slds-icon_x-small slds-icon-text-default" aria-hidden="true">
                                            <lightning:icon iconName="utility:search" size="x-small" alternativeText="Search" />
                                        </span>
                                    </span>     
                                    
                                </div>
                                
                            </div>
                            <div onfocus="{!c.onfocus}" id="listbox-id-1" class="slds-dropdown slds-dropdown_length-with-icon-7 slds-dropdown_fluid" role="listbox">
                                <ul class="slds-listbox slds-listbox_vertical" role="presentation">
                                    
                                    <aura:iteration items="{!v.conList}" var="listItem">
                                        <span ><c:childLookUp myContact="{!listItem}" icon="{!v.iconName}" object="{!v.sObject}"/></span>
                                    </aura:iteration>
                                    
                                </ul>
                            </div>
                        </div>
                    </div>
                </div>
                
                <!--div class="slds-listbox_selection-group slds-listbox-toggle"-->
                    
                    <ul class="slds-listbox slds-listbox_horizontal" role="listbox" aria-label="Selected Options:" aria-orientation="horizontal">
                        <aura:iteration items="{!v.selected}" var="l">
                            <li class="slds-listbox-item slds-truncate" role="presentation"> 
                                <aura:if isTrue="{! v.selected}">
                                    <lightning:pill class="" label="{!l.Name}" name="{!l}" onremove="{! c.handleRemoveOnly }" href="{!'/'+(l.Id)}">
                                        <aura:set attribute="media">
                                        <lightning:icon iconName="{!v.iconName}" alternativeText="{!v.iconName}"/>
                                        </aura:set>
                                    </lightning:pill>
                                </aura:if>
                            </li>
                        </aura:iteration>
                    </ul>
                   
                <!--/div-->
            </div>
        </aura:set> 
        <!-- Multi Select End-->
    </aura:if>
</aura:component>


CustomLookUpCompController.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
({
    doInit : function(component, event, helper) {
		helper.doInit(component, event, helper);
    },
    
    render : function(component, event, helper) {
		helper.render(component, event, helper);
    },
    
    onchange : function(component, event, helper) {
        helper.onchange(component, event, helper);
    },
    
    onblur : function(component, event, helper) {
        helper.onblur(component, event, helper);
    },
    
    onfocus : function(component, event, helper) {
        helper.onfocus(component, event, helper);
    },
    
    handleRemoveOnly : function(component, event, helper) {
        helper.handleRemoveOnly(component, event, helper);
    },
    
    onOptionClick : function(component, event, helper) {
        helper.onOptionClick(component, event, helper);
    },
    
    handleEvent : function(component, event, helper) {
        helper.handleEvent(component, event, helper);
    },
})


CustomLookUpCompHelper.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
({	
    doInit : function(component, event, helper) {
   		//As of now does nothing
		
    },
    
    render : function(component, event, helper) {
     //This is to send the prepopulated value through event to parent
        console.log("In render");
        var args = event.getParam("arguments");
        console.log(JSON.stringify(args));
        console.log(JSON.stringify(args.populatedRecord));
        if(args){
            // Firing Final value event
            var finalEvent = component.getEvent("CustomLookUpFinalValueEvent");
            var uName = component.get("v.uniqueName");
            console.log("Final Event fired:: "+uName);
            finalEvent.setParams({
                uniqueName : uName,
                finalValue : args.populatedRecord                
            });
            finalEvent.fire();
        }else{
            console.log("Didnt get any record to pre-populate");
        }
    },
    
    onchange : function(component, event, helper) {
        
        console.log("Onchange");
        var icon = component.get("v.iconName");
        var Object = component.get("v.sObject");
        
        console.log("icon:: "+icon);
        console.log("Object:: "+Object);
        // Setting method name dynamically
        var methodName = component.get("v.methodName");
        console.log(methodName);
        /*Send this value to server to get values other than in this list*/
        var selected = component.get("v.selected");
        console.log("selected");
        console.log(JSON.stringify(selected));
        
        var action = component.get(methodName);
        var term = component.get("v.sTerm");
        
        action.setParams({
            "searchTerm" :  term,
            "selectedOptions" : selected
        });
        
        if(term.length > 0){
            action.setCallback(this, function(response){
                var state = response.getState();
                console.log(state);
                if(state === "SUCCESS")  {
                    var result = response.getReturnValue();
                    console.log(JSON.stringify(result));
                    console.log(result);
                    component.set("v.conList", result);
                    if(term != "" && result.length > 0){
                        var ToOpen = component.find("toOpen");
                        $A.util.addClass(ToOpen, "slds-is-open");
                    }else{
                        var ToOpen = component.find("toOpen");
                        $A.util.removeClass(ToOpen, "slds-is-open");
                    }
                }
                
            });
            
            $A.enqueueAction(action);
        }
    },
    
    onblur : function(component, event, helper) {
        
        
        //Setting timeout so that we can capture the value onclick
        const blurTimeout = window.setTimeout(
            $A.getCallback(() => {
                var ToOpen = component.find("toOpen");
                $A.util.removeClass(ToOpen, "slds-is-open");
            }),
            300
        );
        component.set('v.blurTimeout', blurTimeout);
    },
    
    onfocus : function(component, event, helper) {
        var term = component.get("v.sTerm");
        var returnedResults = component.get("v.conList");
        console.log("in onfocus");
        console.log(term);
        if(term && returnedResults.length > 0){
            var ToOpen = component.find("toOpen");
            $A.util.addClass(ToOpen, "slds-is-open");
        }
        
    },
    
    handleRemoveOnly : function(component, event, helper) {
        
        var singleSel = component.get("v.singleSelect");
        if(singleSel){
            console.log("in remove");
            var sel = event.getSource().get("v.name");
            console.log(JSON.stringify(sel));
            var lis = component.get("v.selected");
            console.log(JSON.stringify(lis));
            for(var i = 0; i < lis.length; i++){
                console.log(JSON.stringify(lis[i]));
                console.log(lis[i].Id == sel.Id);
                if(lis[i].Id == sel.Id){
                    
                    lis.splice(i,1);
                }
            }
            
            component.set("v.selected", lis);
            console.log(JSON.stringify(lis));
            
            var Input = component.find("input");
            $A.util.removeClass(Input, "slds-hide");
            
            var lookupPill = component.find("lookup-pill");
            $A.util.addClass(lookupPill, "slds-hide");
            
            // Firing Final value event
            var finalEvent = component.getEvent("CustomLookUpFinalValueEvent");
            var uName = component.get("v.uniqueName");
            console.log("Final Event fired "+uName);
            finalEvent.setParams({
                uniqueName : uName,
                finalValue : lis                
            });
            finalEvent.fire();
            
        }else{
            console.log("in remove");
            var sel = event.getSource().get("v.name");
            console.log(JSON.stringify(sel));
            var lis = component.get("v.selected");
            console.log(JSON.stringify(lis));
            for(var i = 0; i < lis.length; i++){
                console.log(JSON.stringify(lis[i]));
                console.log(lis[i].Id);
                console.log(sel.Id);
                console.log(lis[i].Id == sel.Id);
                if(lis[i].Id == sel.Id){
                    
                    lis.splice(i,1);
                }
            }
            
            component.set("v.selected", lis);
            console.log(JSON.stringify(lis));
            
            // Firing Final value event
            var finalEvent = component.getEvent("CustomLookUpFinalValueEvent");
            var uName = component.get("v.uniqueName");
            console.log("Final Event fired "+uName);
            finalEvent.setParams({
                uniqueName : uName,
                finalValue : lis                
            });
            finalEvent.fire();
        }
    },
    
    handleEvent : function(component, event, helper) {
        
        var lookupEventToParent = event.getParam("selectedItem");
        /* selectedValue attribute is used to de duplicate the list box options in dropdown after selecting a value. so that the value does not repeat in box after selecting once*/

        console.log("In event handler");
        console.log(JSON.stringify(lookupEventToParent));
        var singleSel = component.get("v.singleSelect");
        
        if(!singleSel){
            var selectedList = [];
            var existing = component.get("v.selected");
            if(existing.length > 0){
                for(var i = 0; i < existing.length; i++){
                    selectedList.push(existing[i]);
                }
            }
            selectedList.push(lookupEventToParent);
            console.log(JSON.stringify(selectedList));
            component.set("v.selected", selectedList);
            
            const blurTimeout = component.get('v.blurTimeout');
            if (blurTimeout) {
                clearTimeout(blurTimeout);
            }
            var ToOpen = component.find("toOpen");
            $A.util.removeClass(ToOpen, "slds-is-open");
            
            //Empty Search string
            component.set("v.sTerm", "");
            
            // Firing Final value event
            var finalEvent = component.getEvent("CustomLookUpFinalValueEvent");
            var uName = component.get("v.uniqueName");
            console.log("Final Event fired "+uName);
            finalEvent.setParams({
                uniqueName : uName,
                finalValue : selectedList                
            });
            finalEvent.fire();
            
            
        }else{
            var selectedList = [];
            //var existing = component.get("v.selected");
            selectedList.push(lookupEventToParent);
            console.log(JSON.stringify(selectedList));
            component.set("v.selected", selectedList);
            
            var Input = component.find("input");
            $A.util.addClass(Input, "slds-hide");
            
            var lookupPill = component.find("lookup-pill");
            $A.util.removeClass(lookupPill, "slds-hide");
            
            const blurTimeout = component.get('v.blurTimeout');
            if (blurTimeout) {
                clearTimeout(blurTimeout);
            }
            var ToOpen = component.find("toOpen");
            $A.util.removeClass(ToOpen, "slds-is-open");
            //Empty Search string
            component.set("v.sTerm", "");
            
            // Firing Final value event
            var finalEvent = component.getEvent("CustomLookUpFinalValueEvent");
            var uName = component.get("v.uniqueName");
            console.log("Final Event fired "+uName);
            finalEvent.setParams({
                uniqueName : uName,
                finalValue : selectedList                
            });
            finalEvent.fire();
        }
    }
})


selectAttachments.cmp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
<!-- selectAttachments -->
<aura:component controller="ContactAttachments" implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,force:lightningQuickAction" access="global" >
    <aura:attribute name="isOpen" type="boolean"/>
    <aura:attribute name="fileIds" type="Object[]"/>
    <aura:attribute name="sNo" type="Integer" default="1"/>
    <aura:attribute name="isChecked" type="Boolean" default="false"/>
    <aura:attribute name="box" type="object[]"/>
    <aura:attribute name="recordId" type="Id"/>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <aura:handler event="c:DeleteAttachmentsEvent" action="{!c.handleDeleteEvent}"/>
    
    <aura:registerEvent name="deleteRow" type="c:DeleteRowEvent"/>
    <aura:registerEvent name="DeleteNotifyComponentEvent" type="c:DeleteNotifyComponentEvent"/>
    <aura:handler name="deleteRow" event="c:DeleteRowEvent" action="{!c.deleteRow}"/>

    <aura:handler name="updateAttachmentsAppEvent" event="c:updateAttachmentsAppEvent" action="{!c.handleUpload}"/>
    
    <aura:registerEvent name="selectedAttachmentsNotifyEvent" type="c:selectedAttachmentsNotifyEvent"/>
    <!--aura:method name="childmethod" action="{!c.handleDeleteEvent}"> 
        <aura:attribute name="fileIds" type="Object"/> 
    </aura:method-->
    
    <aura:if isTrue="{!v.isOpen}">
        <div class="demo-only" style="height: 200px;">
            <section role="dialog" tabindex="-1" aria-labelledby="modal-heading-01" aria-modal="true" aria-describedby="modal-content-id-2" class="slds-modal slds-fade-in-open slds-modal_medium">
                <div class="slds-modal__container">
                    <header class="slds-modal__header">
                        <div class="slds-button slds-button_icon slds-modal__close slds-button_icon-inverse" title="Close">
                            <lightning:buttonIcon iconName="utility:close" 
                                                  onclick="{! c.closeModal }" 
                                                  variant="bare-inverse"
                                                  alternativeText="close" />
                        </div>
                        <h2 id="modal-heading-01" class="slds-text-heading_medium slds-hyphenate">Attachments</h2>
                    </header>
                    <div class="slds-modal__content slds-p-around_medium" id="modal-content-id-1">
                        <table class="slds-table slds-table_cell-buffer slds-table_bordered">
                            <thead>
                                <tr class="slds-line-height_reset">
                                    <th class="slds-text-title_caps" scope="col">
                                        <div class="slds-truncate" title="Selected">Selected</div>
                                    </th>
                                    <th class="slds-text-title_caps" scope="col">
                                        <div class="slds-truncate" title="S.No">S.No</div>
                                    </th>
                                    <th class="slds-text-title_caps" scope="col">
                                        <div class="slds-truncate" title="Attachment Name">Attachment Name</div>
                                    </th>
                                    <th class="slds-text-title_caps" scope="col">
                                        <div class="slds-truncate" title="Delete">Delete</div>
                                    </th>
                                </tr>
                            </thead>
                            <tbody>
                                <aura:iteration items="{!v.fileIds}" var="f"  indexVar="i">
                                    <tr class="slds-hint-parent">
                                        <th data-label="Opportunity Name" scope="row">
                                            <div class="slds-truncate" title="Selected">
                                            	<lightning:input type="checkbox" label="" value="{!f}" checked="false" onchange="{!c.onChan}"/>
                                            </div>
                                        </th>
                                        <th data-label="Opportunity Name" scope="row">
                                            <div class="slds-truncate" title="Cloudhub">{!v.sNo + i}</div>
                                        </th>
                                        <td data-label="Account Name">
                                            <div class="slds-truncate" title="Attachment Title"><lightning:formattedText value="{!f.Title}"/></div>
                                        </td>
                                        <td data-label="Close Date">
                                            <div class="slds-truncate" title="DeleteIcon"><lightning:buttonIcon iconName="utility:delete" value="{!f.ContentDocumentId}" size="large" variant="bare" onclick="{!c.deleteFile }" alternativeText="delete" /></div>
                                        </td>
                                    </tr>
                                </aura:iteration>  
                            </tbody>
                        </table>
                    </div>
                    <footer class="slds-modal__footer">
                        <lightning:button label="Ok" onclick="{!c.onOk}"/>
                    </footer>
                </div>
            </section>
            <div class="slds-backdrop slds-backdrop_open"></div>
        </div>
    </aura:if>
    
    <!--lightning:button label="Delete files" onclick="{!c.openModal}"/-->
</aura:component>


selectAttachmentsController.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
({
    doInit : function(component, event, helper) {
        var rId = component.get("v.recordId");
        console.log("In Init");
		var action = component.get("c.getAttachments");
        action.setParams({
            conId : rId
        });
        action.setCallback(this, function(response){
            var state = response.getState();
            console.log(state);
            if(state === "SUCCESS"){
                var result = response.getReturnValue();
                console.log("result of Attach display component");
                console.log(result);
                //console.log(result[0].Id);
                component.set("v.fileIds", result);
            }
        });
        $A.enqueueAction(action);
	},
    
    handleDeleteEvent : function(component, event, helper) {
        console.log("In handle delete event in child");
        /*var eventName = event.getSource().getElement();
        console.log("eventName");
        console.log(eventName);*/
        
        var deleteFiles = event.getParam("toDeleteIds");
        console.log("deleteFiles");
        console.log(deleteFiles);
        component.set("v.fileIds", deleteFiles);
        if(deleteFiles === null){
            
            //Toast message on deletion if there are no records in list
            var toastEvent = $A.get("e.force:showToast");
            
            toastEvent.setParams({
                "title": "Attention!",
                "message": "There are no files to delete",
                "type" : "ERROR"
            });
            toastEvent.fire();
        }else{
        component.set("v.isOpen", true);
        }
        /*var deleteFiles = component.get("v.fileIds");
        console.log("deleteFiles");
        console.log(deleteFiles);*/
        
    },
    
    closeModal : function(component, event, helper) {
        component.set("v.isOpen", false);
    },
    
    openModal : function(component, event, helper) {
        component.set("v.isOpen", true);
    },
    
    deleteFile : function(component, event, helper) {
        console.log("In delte file method delete attachments modal");
        var fileToDelete = event.getSource().get("v.value");
        console.log("ContentDocumentId");
        console.log(fileToDelete);
        var action = component.get("c.deleteAttachments");
        action.setParams({
            attachId : fileToDelete
        });
        action.setCallback(this, function(response){
            console.log("In call back");
            var state = response.getState();
            console.log(state);
            if(state === "SUCCESS"){
                var result = response.getReturnValue();
                console.log(result);
                if(result === true){
                    
                    //alert(component.get("v.rowIndex"));
                    var index = component.getEvent("deleteRow");
                    index.setParams({
                        "rowIndex" : component.get("v.sNo")
                    });
                    index.fire();
                }
            }
        });
        $A.enqueueAction(action);
    },
    
    deleteRow : function(component, event, helper) {
        var index = event.getParams("v.sNo");
        //alert(index);
        var AllRowsList = component.get("v.fileIds");
        AllRowsList.splice(index, 1);
        component.set("v.fileIds", AllRowsList);
        
        //Close Modal
        component.set("v.isOpen", false);
        
        var delEvntNotify = component.getEvent("DeleteNotifyComponentEvent");
        delEvntNotify.fire();
        
        //Toast message on deletion
        var toastEvent = $A.get("e.force:showToast");
        
        toastEvent.setParams({
            "title": "Success!",
            "message": "File deleted successfully.",
            "type" : "SUCCESS"
        });
        toastEvent.fire();
    },
    
    onChan : function(component, event, helper) {
        
        console.log("In on change ");
        var change = event.getSource().get("v.value");
        var val = event.getSource().get("v.checked");
        console.log(JSON.stringify(change));
        console.log(JSON.stringify(val));
        var selected = component.get("v.box");
        if(val === true){
        selected.push(change); 
        }else{
            selected.pop(change);
        }
        console.log(JSON.stringify(selected));
        component.set("v.box", selected);  
    },
    
    onOk : function(component, event, helper) {
        console.log("In on onOk ");
        var selected = component.get("v.box");
        console.log(JSON.stringify(selected));
        var evnt = component.getEvent("selectedAttachmentsNotifyEvent");
        
        evnt.setParams({
            selectedIds : selected
        });
        evnt.fire();
        console.log("Event fired");
        component.set("v.isOpen", false);
        component.set("v.box", []);
    },
    
            
    handleUpload : function(component, event, helper) {
        
        var evnt = $A.get("updateAttachmentsAppEvent");
        evnt.getParam("Uploaded");
        var init = component.get("c.doInit");
        $A.enqueueAction(init);
    },
})


CustomLookUpFinalValueEvent.evt:

1
2
3
4
5
6
<!-- CustomLookUpFinalValueEvent -->
<aura:event type="COMPONENT" description="Event template">
    
    <aura:attribute name="uniqueName" type="String"/>
    <aura:attribute name="finalValue" type="Map[]"/>
</aura:event>


selectedAttachmentsNotifyEvent.evt:

1
2
3
4
<!-- selectedAttachmentsNotifyEvent -->
<aura:event type="COMPONENT" description="Event template" >
    <aura:attribute name="selectedIds" type="Map[]"/>
</aura:event>


NotifyController.apxc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public with sharing class NotifyController {
    
    
    @AuraEnabled
    public static List<User> getContactOwners(String recId){
        
        Contact con = [Select Id, Name, Email, OwnerId, CreatedById, LastModifiedById from Contact where Id =:recId];
        system.debug('con:::'+con);
        String OwnerId = con.OwnerId;

        User owner = [Select Id, Name from User where Id =: OwnerId];
        List<User> userList = new List<User>();
        userList.add(owner);
        system.debug('owner:::'+owner);
        system.debug('userList:::'+userList);
        return userList;
    }
    
    @AuraEnabled
    public static void sendEmailApex(List<String> toAddress, List<String> ccAddress, String subject, String body, List<String> files) {
        /*List<Id> ids = new List<Id>();
        ids.add('0687F000008zGL5QAM');*/
        
        Messaging.reserveSingleEmailCapacity(1);
        try{
        messaging.SingleEmailMessage mail = new messaging.SingleEmailMessage();
        
        //toAddress.add('[email protected]');
        //ccAddress = new List<String>(); //{'[email protected]'};
                
        mail.setToAddresses(toAddress);
        mail.setCcAddresses(ccAddress);
        mail.setReplyTo('[email protected]');
        mail.setSenderDisplayName('Kishore');
        mail.setSubject(subject);
        mail.setHtmlBody(body);
        mail.setEntityAttachments(files);
        Messaging.sendEmail(new List<messaging.SingleEmailMessage> {mail});
        }
        catch (exception e){
            throw new AuraHandledException(e.getMessage());
            //return null;
        }
    }
}


ContactAttachments.apxc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public with sharing class ContactAttachments {
    
    @AuraEnabled
    public static List<ContentVersion> getAttachments(String conId){
        
        List<ContentDocumentLink> CDLs = [Select ContentDocumentId 
                                          from ContentDocumentLink 
                                          where LinkedEntityId = : conId];
        
        System.debug('CDLs::::'+CDLs);
        if(CDLs != NULL && CDLs.size() > 0){
        Set<Id> CDIdList = new Set<Id>();
        for (ContentDocumentLink nextCDL : CDLs) {
            CDIdList.add(nextCDL.ContentDocumentId); 
        }
        System.debug('CDIdList::::'+CDIdList);        
        
        List<ContentDocument> entries = [SELECT Id, Title, FileType FROM ContentDocument WHERE ContentDocument.Id IN :CDIdList];
        System.debug('entries::::'+entries);
            
        List<ContentVersion> CVmainList = new List<ContentVersion>();  
        List<ContentVersion> CVList = [SELECT Id, ContentDocumentId, isLatest, Title 
                                       FROM ContentVersion 
                                       WHERE ContentDocumentId = :CDIdList AND isLatest = true];

            return CVList;
        }
        else{
            List<ContentVersion> CV = new List<ContentVersion>();
            CV = NULL;
            return CV;
        }
    }
    
    
    //Attachment delete method
    @AuraEnabled
    public static boolean deleteAttachments(Id attachId){
        
        if(attachId != NULL){
            ContentDocument file = [Select Id from ContentDocument where Id = :attachId];
            delete file;
            return true;
        }
        else{
            return false;
        }
    }
}


Hope this post helped you gain some knowledge, If you like the content please don’t step back to like my page and leave your feedback, It will motivate me to make more posts.

Do subscribe, for getting latest updates directly in your inbox.