I described in this article HERE how to embed your own custom JavaScript app (in my case VueJS app) inside SalesForce page with Aura Component. I mean by “JavaScript” app, the application coded in VueJS, ReactJS, Angular or in pure JavaScript. I will continue here article mentioned earlier.
Embedding you custom front-end gives really huge possibilities of extending your SalesForce instance. You can build custom application in any JavaScript framework, and next, integrate it with SalesForce. For sure here you will need to exchange some data between SalesForce and custom front-end app.
Scroll this page to the final solution – skip introduction and local environment creation
Table of Contents
Send data between SalesForce and JavaScript app in Aura component
Normal scenario would be to take some data from SalesForce, display in custom JavaScript (vue, react, angular) app, modify, and send back to SalesForce result data:
To make it happen, we must install in our front-end project the lightning-container
npm package. In my case, I will extend here the my “External Front-end app for salesforce” external-frontend-app-for-salesforce
VueJS application. You can find it’s description here.
So what does the “Lightning container” npm package? Very good documentation of that you can find on SalesForce developer blog HERE, but I will describe it here in a few words.
Let’s say that this is a messaging broker between your custom JavaScript app and the hosting Aura Component (lightning container). It’s very simple, you can send message with data from your custom JavaScript app to SalesForce Aura component (lightning container) and, in opposite direction, you can receive the data message from SalesForce Aura component. So both instances, SalesForce and you custom embed app, can talk to each other. With LCC you can use 4 methods, but as I’m not a SalesForce develper, I’m interested only in first 3 methods:
LCC.sendMessage(message)
by this method we can send data to hosting Aura (lightning) component.LCC.addMessageHandler(handler)
by this method we can receive data from hosting Aura (lightning) component.LCC.removeMessageHandler(handler)
this method removes existing listener.LCC.callApex(method, params, callback, config)
I will be honest – I’m front-end, not SalesForce, developer, I do not know how to use it 😉
This is a very similar communication interface like native JavaScript window.postMessage()
and window.addEventListener("message", handler )
methods. Read more about native JavaScript postMessage function HERE. And next, before creating a connection to SalesForce, we will use native JavaScript to mock up the development SalesForce parent instance.
I have created example how to use window messages to send data between iframe and parent window by JavaScript – CHECK IT BELOW. You can download example HERE or check it on GITLAB. Check it below:
Check its code on screen shot below:
SalesForce and JavaScritp app data exchange example
Our example will be very easy. SalesForce (and the same mock up local environment) will send JSON data with employees, next custom front-end JavaScript app will display it in table where employee’s salary will be editable. Edited employees data on button click will be send back to SalesForce Aura (lightning) component.
Local environment – mock up
As I mentioned earlier, LCC communication interface looks similar like native JavaScript window.postMessage()
and window.addEventListener("message", handler )
methods. So with that we can build the development environment which would simulate for us the target connection between SalesForce and our VueJS app. You can find our VueJS application HERE on GITLAB
- We are sending “messages” – but what is a message? This should be not only the data, but headers + data. Headers are information which allows to identify the type of sent data, like endpoint in normal REST API. In my example I will use the following syntax of message, where instead of headers I have simply “type”:
var message = { type: "emplyees-list", data: '{"example": "Some JSON DATA"}' };
So message is in our case is the JavaScript object, where with “type” property we can identify later what kind of message we have received in parent or child instance.
- We will reuse here example for iframe child and parent window data exchange, check it HERE or download HERE or check it on our GITLAB.
- But in above example we have as child only static iframe window, but we need to have there our JavaScript, in my case Vue
external-frontend-app-for-salesforce
app. - So first let’s go into our VueJS
external-frontend-app-for-salesforce
app and let’s add here code for listening for messages from parent window (now in pure JavaScript, because we want to have similar local development environement, like we will have with SalesForce with LCC library. - Before next points, where I explain in details how it works with code presentation, check this solution HERE on GITLAB (not master branch):
–sales-force-mockup
folder for the SalesForce mock-up static window implementation
–src/App.vue
file with VueJS component as child implementation - As I code it in JavaScript in Vue, I add in main component
mounted
hook and inside it I create event listener for receiving messages. Further I add a table to list employees, inputs to change its salaries, and at the end, I add submit button to send modified employees list to parent (hosting) window.
Final solution looks like below:
App.vue file<template> <div id="app"> <img alt="Vue logo" src="./assets/logo.png" class="logo"> <h1>VueJS project for SalesForce</h1> <div v-if="employeesData.length"> <h2>Employees list</h2> <button @click="sendData">Send data back</button> <table> <thead> <tr> <th>index</th> <th>age</th> <th>eyeColor</th> <th>name</th> <th>gender</th> <th>company</th> <th>email</th> <th>phone</th> <th>salary</th> </tr> </thead> <tbody> <tr v-for="row in employeesData" :key="row.index"> <td>{{row.index}}</td> <td>{{row.age}}</td> <td>{{row.eyeColor}}</td> <td>{{row.name}}</td> <td>{{row.gender}}</td> <td>{{row.company}}</td> <td>{{row.email}}</td> <td>{{row.phone}}</td> <td><input type="number" v-model="row.salary"></td> </tr> </tbody> </table> </div> </div> </template> <script> export default { name: 'App', data: function () { return { employeesData: [] } }, computed: { employeesDataHeaders () { const firstRow = this.employeesData.length ? this.employeesData[0] : {}; return Object.keys(firstRow); } }, methods: { sendData () { const parentWindow = window.parent; const message = { type: "employees-list", data: this.employeesData }; parentWindow.postMessage(message, "*"); } }, mounted () { window.addEventListener("message", (e) => { let data = e.data ? e.data : {}; let type = data.type; var content = data.data; if(type === "employees-list") { this.employeesData = JSON.parse(content); } }); } } </script> <style> body { background: #fff; } #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 10px; } table { width: 100%; max-width: 1200px; margin: 20px auto; } button { padding: 10px; background: #07ab85; color: #fff; font-weight: bold; border: none; text-transform: uppercase; border-radius: 5px; } .logo { max-width: 100px; } </style>
- Last step is to change the parent static window html page into a SalesForce instance mock-up in
sales-force-mockup
folder –index.html / main.js / main.css
files, its code is as following:sales-force-mockup/index.html
REMEMBER: in iframe tag in src attr you must put URL to your running application. In my case it was: http://localhost:8080<!DOCTYPE html> <html> <head> <title>SalesForce mock-up</title> <meta http-equiv="expires" content="0"> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <script type="text/javascript" src="main.js" defer></script> <link type="text/css" rel="stylesheet" href="main.css"> </head> <body> <h1>SalesForce Mock-up - parent window</h1> <p id="notification">JSON employees data received. Check console window.</p> <button id="btn">Send Emplyees to VueJS app</button> <iframe id="mainframe" src="http://localhost:8080/"></iframe> </body> </html>
sales-force-mockup/main.js
(function () { // DATA var employeesData = '[{"index":0,"age":20,"eyeColor":"blue","name":"Puckett Branch","gender":"male","company":"FIREWAX","email":"puckettbranch@firewax.com","phone":"+1 (968) 479-2314","salary":2356},{"index":1,"age":31,"eyeColor":"green","name":"Shelia Boyd","gender":"female","company":"LOCAZONE","email":"sheliaboyd@locazone.com","phone":"+1 (995) 468-3653","salary":6432}]'; // SEND MESSAGE TO CHILD var btn = document.getElementById("btn"); btn.addEventListener("click", () => { var frame = document.getElementById("mainframe"); frame = frame ? frame.contentWindow : null; var message = { type: "employees-list", data: employeesData }; frame.postMessage(message, "*"); }); // RECEIVE MESSAGE FROM CHILD window.addEventListener("message", (e) => { var data = e.data ? e.data : {}; var type = data.type; var data = data.data; if (type === "employees-list") { document.querySelector("#notification").style.display = "block"; console.log("Emplyees list received by PARENT: ", data); } }); })();
sales-force-mockup/main.css
body { background: #4B9EE4; font-family: sans-serif; color: #fff; } h1{ font-size: 22px; font-weight: 400; text-transform: uppercase; } iframe { width: 100%; height: 400px; border: 1px dashed #ccc; } button { padding: 10px; background: #a203b5; color: #fff; font-weight: bold; border: none; text-transform: uppercase; border-radius: 5px; } #notification { display: none; background: red; color: #fff; padding: 5px; border-radius: 3px; text-align: center; }
- Â If you downloaded my VueJS demo application from our GitLab (not master branch) then just write in terminal window started in this project root directory:
npm run install
and after installation write there
npm run serve
to start application
Then check the port on which app is running, and if needed, change this URL in sales-force-mockup/index.html in iframesrc
attribute - When VueJS app is running, then go into sales-force-mockup folder and double click on
index.html
file. Then VueJSexternal-frontend-app-for-salesforce
app will be visible in iframe tag like on image below: - You can play with that by clicking violet button to send data to child VueJS app, modify salaries there, and send data back to parent window and check it in console. It will look like on screen shot below:
- So what do we have now? We can simply continue development of our VueJS app (or react/angular/vanilla JS/whatever) and simulate the data which is coming back to SalesForce based on our mockup static html page which is hosting the VueJS app. Native JavaScript postMessage communication API works almost the same as works LCC library, so we can simulate this in our local envorinment what makes for us much easier coding, debugging and testing.
- Check functioning SalesForce mock-up example below:
The final solution of data exchange between SalesForce and embed JavaScript app
Here we will present the final solution how to communicate own custom made front-end JavaScript application (here in VueJS) with SalesForce hosting Aura (lightning) component.
If you are interested how we created own local development environment SalesForce instance mockup, then read this article from the beginning.
We base here on SalesForce Org (instance) project, created our another ARTICLE HERE.
Our JavaScript VueJS external-frontend-app-for-salesforce
app connected via LCC library with SalesForce instance, which is descriebed in details below, is available HERE on GITLAB.
Whole SalesForce project with Aura component and JavaScript VueJS external-frontend-app-for-salesforce
app’s distribution package as static resource is available HERE on GITLAB.
OK, let’s go step by step by elements of our VueJS app and SalesForce Aura component.
Custom JavaScript app with lightning-container LCC package
- First, we need to add to our project npm
lightning-container
package bynpm install lightning-container
command, and next import it into our project file by command (in ES2015+ syntax):
import LCC from 'lightning-container'
- Next, we need to add methods for receiving data via LCC:
LCC.addMessageHandler((data) => { let type = data.type; var content = data.data; if(type === "employees-list") { this.assignEmployeesData(content); } });
- And for sending data via LCC:
const message = { type: type, data: data }; LCC.sendMessage(message);
- In our VueJS application we are using also development environment simulating LCC by native jsÂ
window.postMessage()
methods (read article from the beginning to learn that), so our code is a little bigger. The code of our VueJS app component responsible for the communication via LCC is HERE on GITLAB and is below:<template> <div id="app"> <img alt="Vue logo" src="./assets/logo.png" class="logo"> <h1>VueJS project for SalesForce</h1> <div v-if="employeesData.length"> <h2>Employees list</h2> <button @click="sendData">Send data back</button> <table> <thead> <tr> <th>index</th> <th>age</th> <th>eyeColor</th> <th>name</th> <th>gender</th> <th>company</th> <th>email</th> <th>phone</th> <th>salary</th> </tr> </thead> <tbody> <tr v-for="row in employeesData" :key="row.index"> <td>{{row.index}}</td> <td>{{row.age}}</td> <td>{{row.eyeColor}}</td> <td>{{row.name}}</td> <td>{{row.gender}}</td> <td>{{row.company}}</td> <td>{{row.email}}</td> <td>{{row.phone}}</td> <td><input type="number" v-model="row.salary"></td> </tr> </tbody> </table> </div> </div> </template> <script> import LCC from 'lightning-container'; export default { name: 'App', data: function () { return { employeesData: [] } }, computed: { employeesDataHeaders () { const firstRow = this.employeesData.length ? this.employeesData[0] : {}; return Object.keys(firstRow); } }, methods: { sendData () { this.dispatchMessage("employees-list", this.employeesData); }, assignEmployeesData (content) { this.employeesData = JSON.parse(content); }, dispatchMessage (type, data) { const message = { type: type, data: data }; if (process.env.NODE_ENV === 'production') { LCC.sendMessage(message); } else { const parentWindow = window.parent; parentWindow.postMessage(message, "*"); } }, receiveMessage () { if (process.env.NODE_ENV === 'production') { LCC.addMessageHandler((data) => { let type = data.type; var content = data.data; if(type === "employees-list") { this.assignEmployeesData(content); } }); } else { window.addEventListener("message", (e) => { let data = e.data ? e.data : {}; let type = data.type; var content = data.data; if(type === "employees-list") { this.assignEmployeesData(content); } }); } } }, mounted () { this.receiveMessage(); } } </script> <style> body { background: #fff; } #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 10px; } table { width: 100%; max-width: 1200px; margin: 20px auto; } button { padding: 10px; background: #07ab85; color: #fff; font-weight: bold; border: none; text-transform: uppercase; border-radius: 5px; } .logo { max-width: 100px; } </style>
So, if you didn’t read whole article – long story short – this component is responsible for:
– take data from SalesForce with “Employees list”
– display data in table
– make salaries editable
– send modified “Employees list” back to SalesForce
– provide also development environemnet communication via native JSwindow.postMessage()
insteadLCC
. - Â Next, like described in our PREVIOUS ARTICLE HERE, we need to create a distribution package of VueJS app (with relative paths to scripts, so it must work by opening simply index.html file) and paste it into a
staticresources
folder (check on GITLAB):Then do right click on “staticresources” folder and click “Deploy Source to Org”:
- Next, we need to modify our Aura component from our PREVIOUS ARTICLE HERE, and after that, deploy to SalesForce Org (instance) as myFrontendApp.cmp
<aura:component implements="flexipage:availableForAllPageTypes" access="global" > <aura:attribute name="truthy" type="Boolean" default="false"/> <lightning:card title="VUE APP TEST"> <aura:if isTrue="{!v.truthy}"> Message received from child custom VueJS app. Check console window. </aura:if> <br/> <lightning:button label="Send employees to custom VueJS app" onclick="{!c.sendMessage}"/> <lightning:container aura:id="vueApp" src="{!$Resource.external_frontend_app_for_salesforce + '/index.html'}" onmessage="{!c.handleMessage}" /> </lightning:card> </aura:component>
We have here very basic code. Starting from the top:
1. initialization of boolean value to show notification about received from VueJS app data
2. Notification about received data
3. Button to send hardocded JSON data with “Employees list”
4. Lightning container hosting the VueJS app.myFrontendAppController.js
({ sendMessage : function(component, event, helper) { var employeesData = '[{"index":0,"age":20,"eyeColor":"blue","name":"Puckett Branch","gender":"male","company":"FIREWAX","email":"puckettbranch@firewax.com","phone":"+1 (968) 479-2314","salary":2356},{"index":1,"age":31,"eyeColor":"green","name":"Shelia Boyd","gender":"female","company":"LOCAZONE","email":"sheliaboyd@locazone.com","phone":"+1 (995) 468-3653","salary":6432},{"index":2,"age":37,"eyeColor":"green","name":"Tameka Pacheco","gender":"female","company":"JUNIPOOR","email":"tamekapacheco@junipoor.com","phone":"+1 (840) 412-2533","salary":4678}]'; var message = { type: "employees-list", data: employeesData }; component.find("vueApp").message(message); }, handleMessage: function(component, message, helper) { var data = JSON.parse(JSON.stringify(message.getParams().payload)); var type = data.type; var data = data.data; if (type === "employees-list") { console.log("Emplyees list received by PARENT: ", data); component.set("v.truthy", true); } } })
Here we have 2 methods only – to send and receive data from child JavaScript VueJS app. It’s so basic that here is nothing to comment, code is self-explained.
Next, deploy the Aura component:
- Next you must login into your SalesForce Org and add
myFrontendApp
component into a main page (details are in our PREVIOUS ARTICLE HERE) - Then just jump into you main SalesForce instance page and you can see results like on screen shot and gif film below:
Gif film with presentation of data flow between SalesForce and custom JavaScript app inside it (in Aura component):
And here we have reached the end. I hope that I have described in details how the communication of external custom JavaScript app with SalesForce via LCC library looks like.