今回は Lightning コンポーネントによって iframe で読み込まれた Visualforce ページと、呼び出し元の Lightning コンポーネントでメッセージのやり取りをする方法です。
Web Messaging API を使う
iframe を使って読み込まれた Visualforce ページとの通信には JavaScript の Web Messaging API
が使えます。
(Web Messaging API の詳細については省きます)
基本的な方針は、まず Lightning コンポーネントで MessageChannel を作成し window オブジェクトの postMessage() を介して Visualforce ページに片方の MessagePort を渡します。それ以降は、それぞれの MessagePort の postMessage() を利用し通信する事で 1 対 1 のコミュニケーションを確立します。
window.postMessage() の問題点
Lightning コンポーネントと Visualforce ページ間のコミュニケーションは window.postMessage() だけでも実現できます。
しかし、これは window オブジェクトに対してメッセージを送信してしまうので、同じ window オブジェクトを共有している全員がメッセージを受け取れてしまうという点で注意が必要です。
Visualforce ページは、それぞれのフレーム内で異なる window オブジェクトを持ちますが、 Lightning コンポーネントは同じ window オブジェクトを共有しています。
つまり、 Visualforce ページから送信されたメッセージが予期せぬ Lightning コンポーネントによって読み取られる可能性があります。
サンプルコード
上記の基本方針に沿ったサンプルコードです。
<aura:component access="global" implements="flexipage:availableForAllPageTypes" >
<!-- User Interface -->
<iframe aura:id="iframe" src="/apex/VFPage" onload="{!c.onLoad}" />
</aura:component>
({
onLoad: function(c, e, h) {
// MessageChannel を作成する
const channel = new MessageChannel();
// Lighting コンポーネントは port1 を利用する事として、メッセージハンドラーを登録する
// このハンドラーでは、Visualforce ページから送信されたメッセージをコンソールに出力した後、
// 同じメッセージを Visualforce ページに送信する
const port1 = channel.port1;
port1.onmessage = function(event) {
console.log('Lighting Component received: ', event.data);
port1.postMessage(event.data);
}
// port2 は Visualforce ページに渡す
const contentWindow = c.find('iframe').getElement().contentWindow;
contentWindow.postMessage('', '*', [channel.port2]);
},
})
<apex:page>
<script>
// Lightning コンポーネントから window.postMessage() によって MessagePort が渡されるのを待つ
window.addEventListener('message', function(event) {
// MessagePort を受け取ったら、メッセージハンドラを登録し、
// Lightning コンポーネントにメッセージを送信する
// Lightning コンポーネントからメッセージを受け取ったら、コンソールに出力する
const port2 = event.ports[0];
port2.onmessage = function (e) {
console.log('Visualforce Page received : ', e.data);
};
port2.postMessage('Hello!');
}, false);
</script>
</apex:page>