Thursday, January 16, 2020

Create Modal Window In Lightning Web Component

What is a modal window?
 It creates a mode that disables the main window but keeps it visible, with the modal window as a child window in front of it.
It forces the user to interact with it before taking action on the main Window. It is also called popup window or dialog window.
It is commonly used for scenarios like confirming a deletion, set a value.



 In this post, I will demo how to create a modal window in LWC and how to extend it.

This base component is called "modalConfirmation".
It has four properties:
Title - displaying the title of popup window. i.e "Delete File?"
Message - displaying the message on popup. i.e. "Deleting a file also removes it from any records or posts it's attached to."
FirstButton - Action on the popup.  ie. "Cancel".
SecondButton.  - Action on the popup. i.e. "Delete"
For the button actions, I use Event to pass values to parent window, the actual process is in parent window.

 1. modalConfirmation.html
This is very similar to Aura Component implementation. Lightning Design System CSS is used.

 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
<template>
    <div style="height: 640px;">
            <section role="dialog" tabindex="-1" aria-labelledby="heading" aria-modal="true" aria-describedby="content" class="slds-modal slds-fade-in-open">
                <div class="slds-modal__container">
                    <header class="slds-modal__header">
                        <button class="slds-button slds-button_icon slds-modal__close slds-button_icon-inverse" title="Close" onclick={closeModal}>
                            <lightning-icon icon-name="utility:close" size="medium">
                            </lightning-icon>
                            <span class="slds-assistive-text">Close</span>
                        </button>
                        <h2 class="slds-text-heading_medium slds-hyphenate">{title}</h2>
                    </header>
                    <div class="slds-modal__content slds-p-around_medium">
                        <center><h2><b>{message}</b></h2><br/>
                        </center>
                    </div>
                    <footer class="slds-modal__footer">
                        <lightning-button label={firstButton} variant="neutral" onclick={firstButtonAction}></lightning-button>&nbsp;&nbsp;&nbsp;&nbsp;
                        <lightning-button label={secondButton} variant="brand" onclick={secondButtonAction}></lightning-button>
                    </footer>
                </div>
            </section>
            <div class="slds-backdrop slds-backdrop_open"></div>
        </div>
        
</template>

2. modalConfirmation.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
import { LightningElement,api } from 'lwc';

export default class ModalConfirmation extends LightningElement {
    @api message;
    @api title;
    @api firstButton;
    @api secondButton;
    
    firstButtonAction(event)
    {
        // Prevents the anchor element from navigating to a URL.
        event.preventDefault();

        const selectedEvent = new CustomEvent('firstbuttonclicked', { detail: this.firstButton});

        // Dispatches the event.
        this.dispatchEvent(selectedEvent);
    }

    secondButtonAction(event)
    {
        // Prevents the anchor element from navigating to a URL.
        event.preventDefault();

        const selectedEvent = new CustomEvent('secondbuttonclicked', { detail: this.secondButton});

        // Dispatches the event.
        this.dispatchEvent(selectedEvent);
    }

    closeModal(event)
    {
        // Prevents the anchor element from navigating to a URL.
        event.preventDefault();

        const selectedEvent = new CustomEvent('closeModal');

        // Dispatches the event.
        this.dispatchEvent(selectedEvent);
    }

}

3. modalConfirmation.js-meta.xml

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>47.0</apiVersion>
    <isExposed>false</isExposed>
</LightningComponentBundle>


4. Parentwindow.xml
Attribute openModal is used to control the display of the modal window.

Each event triggered on modalConfirmation component (onfirstbuttonclicked,onsecondbuttonclicked and onclosemodal) is bind to a method in parent window.

When DELETE button on parent window is clicked,  modalConfirmation components API properties are assigned valued (Title,Message,FirstButton and SecondButton), plus openModal set to true.
This results modal window is displayed in front of the parent window.

1
2
3
4
5
6
7
<template>
    <template if:true={openModal}>
        <c-modal-confirmation title={title} message={messsage} first-button={firstButton} second-button={secondButton} onfirstbuttonclicked={firstButtonClicked} onsecondbuttonclicked={secondButtonClicked}  onclosemodal={closeModal}>
        </c-modal-confirmation>
    </template>

   ..................


5. Parentwindow.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
import { refreshApex } from '@salesforce/apex';
import getContentNoteList from '@salesforce/apex/ContentNoteController.getContentNoteList';
import deleteContentNote from '@salesforce/apex/ContentNoteController.deleteContentNote';
import removeContentNote from '@salesforce/apex/ContentNoteController.removeContentNote';
import getContentNote from '@salesforce/apex/ContentNoteController.getContentNote';

export default class ContentNoteList extends LightningElement 
{
    @api recordId;
    @api flexipageRegionWidth;
    @track openModal = false;
    @track messsage;    
    @track title;
    @track firstButton;
    @track secondButton;
    action;


    handleDeleteButton(event) {
        this.action = event.detail.action.name;
        
        this.firstButton = 'Cancel';
        this.secondButton = 'Delete';

        this.messsage = 'Deleting a file also removes it from any records or posts it\'s attached to.';
        this.title = 'Delete File?';
        this.openModal = true;
            
    }

    closeModal()
    {
        this.openModal = false;
    }


    firstButtonClicked()
    {
        this.openModal = false;
    }

    secondButtonClicked()
    {
        this.openModal = false;

 // add your logic here
        if (this.action === 'Delete')
        {
           
        }
        else if (this.action ==='Remove')
        {

        }
        
    }
}


We can also extend modalConfirmation LWC component. In the following example, we implmenet a modal datepick component.

6. modalDatePicker.html
The major part of is the same except a datepicker input component is to replace displaying a message.

 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
<template>
    <div style="height: 640px;">
            <section role="dialog" tabindex="-1" aria-labelledby="heading" aria-modal="true" aria-describedby="content" class="slds-modal slds-fade-in-open">
                <div class="slds-modal__container">
                    <header class="slds-modal__header">
                        <button class="slds-button slds-button_icon slds-modal__close slds-button_icon-inverse" title="Close" onclick={closeModal}>
                            <lightning-icon icon-name="utility:close" size="medium">
                            </lightning-icon>
                            <span class="slds-assistive-text">Close</span>
                        </button>
                        <h2 class="slds-text-heading_medium slds-hyphenate">{title}</h2>
                    </header>
                    <div class="slds-modal__content slds-p-around_medium">
                        <h2><b>
                            <lightning-input class="datepicker" type="date" value={selectedDate} required></lightning-input>
                        </b></h2><br/>
                        
                    </div>
                    <footer class="slds-modal__footer">
                        <lightning-button label={firstButton} variant="neutral" onclick={firstButtonAction}></lightning-button>&nbsp;&nbsp;&nbsp;&nbsp;
                        <lightning-button label={secondButton} variant="brand" onclick={secondButtonAction}></lightning-button>
                    </footer>
                </div>
            </section>
            <div class="slds-backdrop slds-backdrop_open"></div>
        </div>
        
</template>

7. modalDatePicker.js
This component is inherited from modalConfirmation. We only need to add additional attributes (selectedDate in this case) and override the button action ( secondButtonAcion).


 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
import {track } from 'lwc';
import ModalConfirmation from 'c/modalConfirmation';

export default class ModalDatePicker extends ModalConfirmation {
    @track selectedDate;


    secondButtonAction(event)
    {
        if (!this.checkComponentValidity())
            return;

        // Prevents the anchor element from navigating to a URL.
        event.preventDefault();

        let inputComp =this.template.querySelector(".datepicker")

        const selectedEvent = new CustomEvent('secondbuttonclicked', { detail: inputComp.value });

        // Dispatches the event.
        this.dispatchEvent(selectedEvent);
    }

    checkComponentValidity()
    {
        let inputComp =this.template.querySelector(".datepicker"); 
        inputComp.setCustomValidity('');
        if (inputComp.value)
        {
            let inputDate = new Date(inputComp.value);
            if (inputDate < new Date())
                inputComp.setCustomValidity('Invalid Date. Date Must be a Future Date.');
        }
        return inputComp.reportValidity();

        
    }

}

8. DatePickerParentWindow.html

1
2
3
4
5
6
<template>

    <template if:true={openModalDatePicker}>
        <c-modal-date-picker title='Please select a future date' first-button="No" second-button="Yes" onfirstbuttonclicked={firstDatePickerButtonClicked} onsecondbuttonclicked={secondDatePickerButtonClicked}>
        </c-modal-date-picker>
    </template>


9.DatePickerParentWindow.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
import { LightningElement,wire,track } from 'lwc';

export default class DatePickerParentWindow extends LightningElement {


    @track openModalDatePicker = false;
    action;

   
    openDatePicker()
    {
        this.openModalDatePicker  = true;
    }

    firstDatePickerButtonClicked()
    {
        this.openModalDatePicker = false;
    }

    secondDatePickerButtonClicked(event)
    {
        this.openModalDatePicker = false;

 //add your logic here


    }
}


Sunday, January 12, 2020

VSCode Common Issues for Salesforce Development

1.Issue: The Salesforce CLI is not installed

After user installs Salesforce CLI, then tries to 'Create Project with Manifest', VS Code starts creating project but errs out with the following message:
The Salesforce CLI is not installed. Install it from https://developer.salesforce.com/tools/sfdxcli

Cause: Missing or incorrect Windows Environment Variable setting.

Fix: 1.1 we need to get the full path nae of installed programs: Salesfoce CLI and Microsoft VS Code.

1.2 In Windows search box, search "Edit Environment". Control panel | Path | Edit. 


It opens Path variable setting; then New | Add folders you get in last step | OK 



2. Issue: Java runtime could not be located

When starting a Salesforce project in VS Code, there is warning saying "Java runtime could not be located. Set one using the salesforcedx-vscode-apex.java.home VS Code setting. For more information, go to Set Your Java Version."

Cause: The Apex Language Server, shipped as part of the Salesforce Apex Extension for VS Code depends upon the Java Platform, Standard Edition Development Kit (JDK). It requires an installation of either JDK version 11 (Recommended) or JDK version 8. It also requires proper settings in VS Code.

Fix: 2.1 Install JDK v11 or V8. Get the full path name of installed JDK. (i.e. C:\Program Files\Java\jdk-11.0.5)
       2.2 Select File > Preferences > Settings (Windows or Linux) or Code > Preferences > Settings (macOS).
       2.3 Search for apex.
       2.4 Change the salesforcedx-vscode-apex.java.home setting to the full pathname of your Java Runtime. 

  3. Issue: unable to authorize an org behind a company firewall
There are many cases user cannot authorize an org in VS code.
The one I am addressing is this post is about company security policy does not allow using port 1717 on user's computer.

When user tries to authorize a Salesforce org with oAuth, user gets "localhost 1717 Oauth Redirect" error at the redirect step.

Cause: One of the causes is company security policy does not allow using port 1717 on your computer.

Fix:  3.1 Find a public VPN as proxy server.
        3.2 Add HTTP_Proxy and HTTPs_Proxy Windows Environment Variables
        The format is:
        https_proxy=http://username:password@proxy.example.com:port
        https_proxy=http://username:password@proxy.example.com:port