What is the need of Lightning Container ?
- Why host that application onto some other server ? Use iframe to connect with that even if I need to use it within Salesforce Org only ?
- Why develop and use custom Salesforce APIs for data sync when I have other better methods to connect with Salesforce ?
Features of Lightning Container
- Lightning Container allows you to use an application developed in 3rd party framework like:- Angular, React Js etc. in Lightning Component.
- Lightning Component can handle messages as well as errors from your embedded app in it's controller.
- In the same way as above, the embedded javascript application can also handle messages as well as errors, from your lightning component
- You can also call apex from your custom lightning:container application
Lightning Container Implementation
- You must have nodejs installed into your system, download it from here
- npm (node package manager) is automatically installed when you install nodejs.
- Download and install git from here.
Once you have nodejs and npm installed, next step is to star my github repository so that you don't lose access to it in future and clone it using the command below github repository :-
npm install command will install all the dependencies needed by our react boilerplate application and also the dependencies needed by our js app to connect with Salesforce. Salesforce has provided easy to use npm modules to use in your custom js app to establish a connection between both. A brief description about the npm modules is given below:-
- lightning-container:- This npm module is a must have to establish the communication between your javascript application and lightning component. It consists of methods like:- sendMessage, callApex, addMessageHandler, addErrorHandler etc.
- @salesforce-ux/design-system:- This module consists of all the slds classes that we can use in our custom js application to give it a proper look and feel of lightning.
Now, before moving to the code in custom js application, we'll have a look at the Salesforce code first.
LightningContainerBoilerplate.cls
LightningContainerBoilerplateApp.app
LightningContainerBoilerplate.cmp
As you can see above, I have made a simple lightning component that I can embed in lightning pages, I have made a single attribute named myMessage which is of type String and displays the message received from the custom js react application. Then I have made two lightning cards, one has a title Lightning Component and other has a title JavaScript Application.
Lightning Component
- In first part, I am displaying myMessage attribute using {!v.myMessage}. This is the message received from react application.
- In the second part, I am displaying an input field with a label of Input Message and an aura:id as inputMessage in which I can input the text to send to my react js application.
- In the third part, I have made a simple button with a label Send Message which is calling sendMessage controller function on click and is responsible to send the message to the react application.
JavaScript Application
LightningContainerBoilerplate.css
As you can see above, I have defined the containerClass here used in lightning:container tag and specified a height of 400px for my custom js react application.LightningContainerBoilerplateController.js
React Js Application
create-react-app <application-name>
If you create any application using the above command and then move to that app's folder and run npm start, you'll see the below page:-
I have only modified the code in app.js file and the updated code is given below:-
As you can see in the above code,
- First of all, we've imported the lightning-container module and also the css file of slds from @salesforce-ux/design-system module
- In the constructor, I have initialized the state with two keys:- messageReceived and messageToSend. A state is basically a javascript object that we can use for interaction within a component. The messageReceived object mainly consist of a type and a value so that we can use the same object to display different types of messages received (success and error). I have also binded all the functions used in my application. It is a react syntax to bind all the functions to use them.
- In the componentDidMount() which is like an init method for react and is called as the component is mounted, I have called addMessageHandler and addErrorHandler methods from the LCC package and passed the handleMessage and handleError methods in them to specify the methods that'll be responsible to handle the message and error respectively coming from lightning component.
- The handleMessage method is receiving the message in a variable named successMessage, creating a javascript object with a type - success and value with the message received in successMessage variable.I am setting the messageReceived in state with the newly created messageReceived object using this.setState({ messageReceived: messageReceived });
- Similarly in the handleError method, I am setting the messageReceived in state in the same way, the only difference is here the object has a type of error.
- Next we have sendMessage() which is calling LCC.sendMessage() that can pass a javascript object as it's parameter. Here, our javascript object consists of two things, a name and a value where I have hardcoded name as MyMessage and value is this.state.messageToSend i.e. the value of this key in the component state. This value I am going to set as I take input in the input field.
- Now, let's move down to the HTML code returned inside render() It has slds applied and consists of simple title and then a form which consists of an input field for entering the Last Name and two buttons, one to send a message to lightning component and other to save contact. Both buttons use the input provided in the last name field.
- On change of input in the lastname field, I am calling this.handleInputChange. In this method, we're getting the value of input field using event.target.value and storing it in message variable and I am setting the messageToSend in state with that message variable using this.setState({ messageToSend: message });
- The Save Contact button is an input of type submit on click of which, the form is submitted and in the form tag, we're calling this.callApexController method onSubmit of that form. If you see the callApexController method, I am preventing the default behaviour of form submission using event.preventDefault and then I am using LCC.callApex() to call the apex controller directly from this JS application.
- In LCC.callApex I am passing 4 parameters, first is the class and method name LightningContainerBoilerplate.createNewContact, the second is this.state.messageToSend which I have set on change in Last Name input before. The third is this.handleApexReturnValue which is the callback method to be called when we receive a response from apex controller and the fourth is {escape:true} which is to maintain security to escape special characters so that no malicious js code is passed in response from apex.
- In the handleApexReturnValue method, we're getting two parameters as specified by salesforce, first is the result which is the value returned by our apex controller method and second is the event which is used to get the status of apex call. Next, I have created a variable named messageReceived and checking if the event.status is true then the messageReceived is equal to an object with type apexsuccess and value as result received from apex controller method. Otherwise if event.type is exception, then we have an object with type as apexerror and the value as event.message concatenated with event.where else we have an object with type as apexerror and value as event.message only as it is not an exception but any other unknown error. Finally, I am setting the messageReceived state with messageReceived object.
- In the HTML code, below the form, we're checking the this.state.messageReceived.type and displaying different messages according to the state and then the value as this.state.messageReceived.value
If you make any changes, then to create a production build, you again open another terminal and move to the same lightning-container-boilerplate directory and run npm run build command. This will create a production build for application that we can upload as a static resource in our Salesforce Org and use it from there to embed in our lightning component. Once you've created a production build, you'll see a build/ folder into your base lightning-component-boilerplate folder. Now, we have to zip this build folder and upload it as a static resource in Salesforce.
Just make sure that, you append the above code in the manifest.json file generated in the build folder generated after creating a production build. If there is no such file, create one and add this code surrounded by { } which is the standard format of json file. This is required to specify that our custom js app is dealing with the apex controller which is mentioned in the code above and I have specified the apex-controller as LightningControllerBoilerplate which is the one I am using.
I hope you liked this blog and if you want this session to happen again or want a video about the same let me know in comments. Sharing a picture of my session at JDF below:-

Sharing my twitter moment below:-
And I am also embedding my first VLOG made at #JDF18. Do watch it and let me know if you liked it or any suggestions for further VLOGs :P
Happy Trailblazing..!!
Impressive article! Keep it up!
ReplyDeleteGreat to see that you liked it Hitesh :-) Make sure to share it in your network..!!
DeleteGreat article!
ReplyDeleteCould we also send props and listen to events from a web component using this arch? Lets say i am rendering a webcomponent using lightning container rather than a whole app and LCC would be responsible for making api call and getting the data and then passing it down as a prop to my webcomponent(not lightning web component but my own custom web component that i uploaded to static resources)
Hi, It should work with web component as well.
Delete