Friday, December 13, 2019

JavaScript Essentials for Lightning Aura/Web Component Continued..























In our Previous blog we saw the general javaScript which is MUST to know if you are going to start with any kind of Web Development not just Lightning.

In this blog we are going to see JavaScript specific to Lightning Aura Component Development.

So, without any further ado, Let’s get Started.
WHERE?
Where do we use JavaScript in Lightning Component Framework?
Component Bundle
Let’s look at a “sampleCMP” component bundle…























Type
Resource Name
Component
sampleCMP.cmp
Controller
sampleCMPController.js
Helper
sampleCMPHelper.js
Style
sampleCMP.css
Documentation
sampleCMP.auradoc
Renderer
sampleCMPRenderer.js
Design
sampleCMP.design
SVG
sampleCMP.svg

So, what are the JavaScript in the above component bundle. Let’s take a closer look at the above example.

Controller, Helper and Renderer are JS of the component.

Component Bundle JavaScripts...













The basic work of Controller is to handle events raised by the components.

A Salesforce Controller Definition would be –

A client-side controller handles events within a component. It’s a JavaScript resource that defines the functions for all of the component’s actions.
A client-side controller is a JavaScript object in object-literal notation containing a map of name-value pairs. Each name corresponds to a client-side action. Its value is the function code associated with the action. Client-side controllers are surrounded by parentheses and curly braces. Separate action handlers with commas (as you would with any JavaScript map).
That seems a bit complex to understand if you are really new to this Lightning world –
Let us simplify this –
Basic things that we do in controller are –
  1.  Accessing Component attributes
  2. Handling Component events
  3. Handling Framework events

Let’s see what all three are with an example with our sampleCMP component.
sampleCMP.cmp
<aura:component>
   
    //
Component Attribute
   
<aura:attribute name="sampleName" type="String" default="I am a component"/>
   
    //
Framework Event
   
<lightning:button label="Framework Button" onclick="{!c.handleClick}"/>
   
    //
Just a UI to show value
   
<p><lightning:formattedText value="{!v.sampleName}" /></p>
   
</aura:component>

//Please Ignore commenting format as it was not visible in blog so I used a workaround

sampleCMPController.js
({
   
//Handling Component Action
    handleClick : function(component,
event, helper) {
                             
       
//Accessing component Attribute
        
let attributeValue = component.get("v.sampleName");
        console.log(
"component sampleName current value" + attributeValue);
       
       
//Let us see how do we handle framework events
        
let newTargetVal = event.getSource(); //Here we are handling framework                                                    Event using "event"
        component.
set("v.sampleName", newTargetVal.get("v.label"));
    }
})

1. Accessing Component attributes

 let attributeValue = component.get("v.sampleName"); 

Here we are accessing component attribute in a JavaScript variable  “attributeValue”.

2. Handling Component events

handleClick : function(component, event, helper) {
}

“handleClick” is an event raised from the component button “onClick”. we are handling the event raised from component in Controller.

3. Handling Framework Event

let newTargetVal = event.getSource(); //Here we are handling framework Event using "event"

Here we are using Controller to handle to Framework event and the values using the same.

There is another one - Invoking Another Action in the Controller for which we use Controller, that we will cover in next topic.

NOTE: You cannot call a controller method from the same controller or other controller. For example if your controller have two methods, Method A and Method B. If you want to call Method A from Method B, You can't do.
Helper
Helper is kind of utility class that we create in apex and we put all the reusable methods in that.

A Salesforce Helper definition would be –

Put functions that you want to reuse in the component’s helper. Helper functions also enable specialization of tasks, such as processing data and firing server-side actions.
A helper function can be called from any JavaScript code in a component’s bundle, such as from a client-side controller or renderer.
Helper functions are similar to client-side controller functions in shape, surrounded by parentheses and curly braces to denote a JavaScript object in object-literal notation containing a map of name-value pairs. A helper function can pass in any arguments required by the function, such as the component it belongs to, a callback, or any other objects.

Again, this seems too bookish, let us see it by example with our sampleCMP

sampleCMPController.js
({
     
//Handling Component Action
      handleClick :
function(component, event, helper) {
            helper.handleClickHelper(component, event)
      }
})

sampleCMPHelper.js
({
      handleClickHelper :
function(component, event)            
       
//Accessing component Attribute
       
let attributeValue = component.get("v.sampleName");
       
console.log("component sampleName current value" + attributeValue);
       
       
//Lets see how do we handle framework events
       
let newTargetVal = event.getSource(); //Here we are handling framework Event using "event"
                 component.set(
"v.sampleName",newTargetVal.get("v.label"));
      }
})

Here I moved the functionality we wrote in Controller to Helper, so that this can be called from any JavaScript code within Component.

Renderer
Most of the time you don’t have to work anything on this part of Component JS. It is only touched when someone needs a custom rendering.
Salesforce Lightning Framework itself manage (Creating and Managing) of the Data Object Model (DOM) elements of a component.

A Salesforce Renderer definition would be –

The framework’s rendering service takes in-memory component state and creates and manages the DOM elements owned by the component. If you want to modify DOM elements created by the framework for a component, you can modify the DOM elements in the component’s renderer. Otherwise, the framework will override your changes when the component is rerendered.
The DOM is the language-independent model for representing and interacting with objects in HTML and XML documents. The framework automatically renders your components so you don’t have to know anything more about rendering unless you need to customize the default rendering behavior for a component.
To modify default rendering behavior of the component or to access the DOM elements, We can extend the existing functions from the rendering lifecycle of a component.
The base component in the framework is aura:component. Every component extends this base component.
The renderer for aura:component is in componentRenderer.js. This renderer has base implementations for the four phases of the rendering and rerendering cycles:
  •  render()
  • rerender()
  • afterRender()
  • unrender()

The framework calls these functions as part of the rendering and rerendering lifecycles and we will learn more about them soon. You can override the base rendering functions in a custom renderer.
sampleRenderer.js 
({
    render :
function(component, helper) {
       
var ret = this.superRender();
       
// do custom rendering here
       
return ret;
    },
    rerender :
function(component, helper){
       
this.superRerender();
       
// do custom rerendering here
    },
    afterRender:
function (component, helper) {
       
this.superAfterRender();
       
// interact with the DOM here
    },
    unrender:
function () {
       
this.superUnrender();
       
// do custom unrendering here
    }
})

HOW?
The above was all about WHERE do we use JavaScript in Lightning Component Framework. Now we will explore HOW do we use JavaScript in Lightning Component Framework.
Working with Lightning Component Attributes
HOW do we GET and SET the attribute of a Lightning Component?

getSetFunc : function (component) {
       
   
// get component attribute
   
var label = component.get("v.label");
       
   
// set component attribute
    component.set(
"v.label","This is a label");
       
   
// check if attribute is undefined, null or empty
   
var isEmpty = $A.util.isEmpty(component.get("v.label"));
       
   
// get/set attribute of an inner component with "aura:id"
   
var sumthin = component.find("cmpAuraId").get("v.value");
    component.find(
"cmpAuraId").set("v.value", "some value");
}

GET: Use Lightning Component framework Get method to get the attribute value of a component in Component’s JavaScript.

// get component attribute
 
var label = component.get("v.label");

SET: Similarly, you can use SET is set the value of an attribute in JavaScript.

// set component attribute
 component.set(
"v.label","This is a label");

You can also use “$A” utility functions APIs

// check if attribute is undefined, null or empty
 
var isEmpty = $A.util.isEmpty(component.get("v.label"));

Some of the “$A” utility functions are –
  • getBooleanValue
  • hasClass
  • isArray
  • isEmpty
  • isObject
  • isUndefined
  • isUndefinedOrNull
  • removeClass
  • toggleClass
Do Not Use Standard(Vanilla) JavaScript Functions:


Var x = document.getElementById("htmlID")
x.someAttribute =
"Some Value";
x.setAttribute(
'aria-label', 'Test');
x.getAttribute(
'aria-label');



Handling Events
One of the most important part of Lightning Component Development, Handling Events by LIGHTNING WAY

Browser Events like "onClick, onChange etc", and the Lightning Framework Component events can be wired to Component Controller (In our example - sampleCMPController.js) actions.
<aura:component>
   
  //Framwork Event
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
   
    //Component Attribute
    <aura:attribute name="sampleName" type="String" default="I am a component"/>
   
    //Framework Component
    <lightning:button label="Framework Button" onclick="{!c.handleClick}"/>
   
    //Just a UI to show value
    <p><lightning:formattedText value="{!v.sampleName}" /></p>
   
</aura:component>
//Please Ignore commenting format as it was not visible in blog so I used a workaround

In the above code we have Framework Event named INIT which is wired to Controller method doInit.
We also have a Frame component lightning:button whose Event is wired to controller method.

Do not include Browser events directly in the Markup -
<aura:component>
    //don't do this
    <lightning:button label="Click Me" onclick="alert('this will not work')"/>
</aura:component>
//Please Ignore commenting format as it was not visible in blog so I used a workaround

Conditional Display
Need to SHOW or NOT to SHOW a Part of Component...

Basically there are multiple ways to do that, but here I am going to talk about two most commonly used ways to show and hide a part of component.
  1. Use $A.Util method to add/remove a class to toggle the CSS Styling dynamically to show or hide a particular part of component.

    toggleUsingUtil.cmp

    <aura:component>
       
    <lightning:button label="Toggle" onclick="{!c.toggle}"/>
       
    <p aura:id="text">Now you see me</p>
    </aura:component>

    toggleUsingUtilController.js

    ({
        toggle :
    function(component, event, helper) {
           
    var toggleText = component.find("text");
            $A.util.toggleClass(toggleText,
    "toggle");
        }
    })

    toggleUsingUtil.css

    .THIS.toggle {
       
    display: none;
    }
     
  2. Use aura:if, as per Salesforce this more standard practice will take less resource.

    <aura:component>
       
    <aura:attribute name="edit" type="Boolean"
                        default=
    "true"/>
       
    <aura:if isTrue="{!v.edit}">
           
    <ui:button label="Edit"/>
           
    <aura:set attribute="else">
                You cannot edit this.
           
    </aura:set>
       
    </aura:if>
    </aura:component>

    Both of the above can be used, it will also depend on the use cases sometime but most of the time either of these can be used to conditionally render a UI on component.

Dynamic Components
Creating components at run-time or based on Events or you can say Conditionally.

Again, $A is going to help you here.
Method: $A.createComponent()
Syntax: 
$A.createComponent(String type, Object attributes, function callback)
  1. type - The type of component to create; for example, "lightning:button".
  2. attributes - A map of attributes for the component, including the local Id (aura:id).
  3. callback(cmp, status, errorMessage) - The callback to invoke after the component is created.
The callback has three parameters.
  1. cmp - The component that was created. This enables you to do something with the new component, such as add it to the body of the component that creates it. If there’s an error, cmp is null.
  2. status - The status of the call. The possible values are SUCCESSINCOMPLETE, or ERROR. Always check that the status is SUCCESS before you try to use the component.
  3. errorMessage - The error message if the status is ERROR.

Okay that is too much of stuff, For more information on this you can check the Salesforce Developer guide.

Now let us see this through an example.

createComponentDynamic.cmp
<aura:component>
   
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>

   
<p>Dynamically created button</p>
    {!v.body}
</aura:component>

createComponentDynamicController.js
({
    doInit :
function(cmp) {
        $A.createComponent(
           
"lightning:button",
            {
               
"aura:id": "findableAuraId",
               
"label": "Press Me",
               
"onclick": cmp.getReference("c.handlePress")
            },
           
function(newButton, status, errorMessage){
               
//Add the new button to the body array
               
if (status === "SUCCESS") {
                   
var body = cmp.get("v.body");
                    body.push(newButton);
                    cmp.set(
"v.body", body);
                }
               
else if (status === "INCOMPLETE") {
                   
console.log("No response from server or client is offline.")
                   
// Show offline error
                }
               
else if (status === "ERROR") {
                   
console.log("Error: " + errorMessage);
                   
// Show error message
                }
            }
        );
    },

    handlePress :
function(cmp) {
       
// Find the button by the aura:id value
       
console.log("button: " + cmp.find("findableAuraId"));
       
console.log("button pressed");
    }
})

The client-side controller calls $A.createComponent() to create a lightning:button with a local ID (aura:id) and a handler for the onclick attribute. The function(newButton, ...) callback appends the button to the body of c:createComponent. The newButton that’s dynamically created by $A.createComponent() is passed as the first argument to the callback.

NOTE: c:createComponent contains a {!v.body} expression. When you use cmp.set("v.body", ...) to set the component body, you must explicitly include {!v.body} in your component markup.

Renderer and the DOM
Modifying, Accessing and rendering DOM Elements

The lightning component Framework's Renderer manages DOM elements automatically owned by a component.

You go into the Renderer javaScript only if there is a need to custom rendering or need to access DOM. In that case use the Component's renderer.

sampleCMPRenderer.js
({
    render :
function(component, helper){
       
var ret = this.superRender();
       
// do custom rendering here
        alert(
"I'm rendering");
       
return ret;
    },
})

DON'Ts very Important
  • Don’t set attributes in a component’s renderer to avoid new rendering cycles. Use the component’s controller instead.
  • Only modify DOM elements that are part of the component to ensure component encapsulation
  • Don’t fire events in a component’s renderer to avoid infinite rendering loops.


Locker Service
Salesforce ensure that you write a Secure Code, Rules for Writing Secure Code

RULE 1 - A component can only traverse the DOM and access elements created by that
component

RULE 2 - JavaScript ES5 strict mode is implicitly enabled

You can access published, supported JavaScript API framework methods only. Published

Lightning CLI available for linting and Locker Service compatibility


These all were specific to LIGHTNING AURA COMPONENT. WHERE and HOW to use JAVASCRIPT.

I will be writing one more specific for LIGHTNING WEB COMPONENT. So stay tuned.

I hope you guys find this helpful.

Thank you !! Keep Coding.


1 comment: