LWC で使える Salesforce の API を使ってコンポーネントを作ってみた。
こんにちは、co-meetingの町田です。
Trailhead をやったり、リファレンスを読んでいた際に、LWC 開発に使えそうな Salesforce の API がいくつかありました。しかし、実際の LWC 開発では使う機会がなく、使い心地を確かめる機会がありませんでした。
ということで、コンポーネントを作って使い心地を確かめてみました。
LWC 開発に使えそうな Salesforce の API
使ったことなくて今後使えそうだなと思ったものは以下の2つです。
Platform Event
Utility Bar Api
これらを使ってコンポーネントを作ってみました。
作ったコンポーネントの概要
「誰がどのページを開いてるか」コンポーネントを作りました。
このコンポーネントは、アプリケーションのユーティリティバーに設置することで、自分の見ているページを別ユーザーに共有できます。
続いて、このアプリの機能と使っている要素を解説します。
機能1. 開いているページの共有
この機能は、以下の赤枠に表示されているように、開いているページを共有する機能です。
こちらの機能は Platform Event を使って実現しています。
機能2. アラートの送信
この機能は、以下の赤枠のアイコンをクリックすることで、他ユーザーにアラートを送信する機能です。
アラートが送信されると、他ユーザーのユーティリティバーが開きます。
こちらの機能は、Platform Event と Utility Bar Api を使っています。
実際に動いているコード
使い方は、最初にあげた公式のドキュメント通りです。
コード解説はコメントにあるため省略させていただきます🙇♂️
LWC
javascript
import { LightningElement, track, wire } from "lwc";
import { subscribe } from "lightning/empApi";
import { CurrentPageReference, NavigationMixin } from "lightning/navigation";
import Id from "@salesforce/user/Id";
import publish from "@salesforce/apex/PlatformEventService.publishViewing";
import { open, EnclosingUtilityId } from "lightning/platformUtilityBarApi";
/**
* @typedef {{UserId__c: string, Action__c: string, PageReferenceJson__c: string, PageTitle__c: string, CreatedById: string, CreatedDate: string}} ViewingMemberEvent
*/
const channel = "/event/ViewingMemberEvent__e";
const convertViewingUserEvent = (/** @type {ViewingMemberEvent} */ event) => ({
isNotMe: event.UserId__c !== Id,
userId: event.UserId__c,
pageReferenceJson: event.PageReferenceJson__c,
pageTitle: event.PageTitle__c,
createdDate: new Date(event.CreatedDate),
isAlerted: false
});
const handlePublish = (promise) => {
promise
.then((res) => {
console.log("publish result", JSON.stringify(res));
})
.catch((err) => {
console.error("publish error", JSON.stringify(err));
});
};
export default class ViewingUserList extends NavigationMixin(LightningElement) {
// 設置されているユーティリティバーのId
@wire(EnclosingUtilityId) utilityId;
/**
* このコンポーネントを開いているユーザーのリスト
*
* @type {{userId: string, pageReferenceJson: string, pageTitle: string, createdDate: Date }[]}
*/
@track
viewingUserList = [];
publishTimeout = null;
currentPageReference;
@wire(CurrentPageReference)
setCurrentPageReference(pageReference) {
console.log(pageReference);
this.currentPageReference = pageReference;
// ページを移動したら他ユーザーへプラットフォームイベントを送信
this.publishViewing();
}
connectedCallback() {
handlePublish(
publish({
userId: Id,
action: "Entering",
pageReferenceJson: JSON.stringify(this.currentPageReference),
pageTitle: document.title
})
);
subscribe(
channel,
-1,
(/** @type {{data: {payload: ViewingMemberEvent}}} */ event) => {
const index = this.viewingUserList.findIndex(
(e) => e.userId === event.data.payload.UserId__c
);
console.log("action", event.data.payload.Action__c);
if (event.data.payload.Action__c === "Entering") {
// だれかが新しくページを開いた場合、自分が開いているページを送る。
this.publishViewing();
} else if (event.data.payload.Action__c === "Viewing") {
// だれかがページを開いたら、ユーザー情報を更新
if (index >= 0) {
this.viewingUserList.splice(index, 1);
}
this.viewingUserList.unshift(
convertViewingUserEvent(event.data.payload)
);
} else if (event.data.payload.Action__c === "Alert") {
// アラートが来たらユーティリティバーを開く
open(this.utilityId, {
autoFocus: true
});
if (index !== -1) {
this.viewingUserList[index].isAlerted = true;
}
}
}
)
.then((res) => {
console.log("subscribe result", JSON.stringify(res));
})
.catch((err) => {
console.error("subscribe error", JSON.stringify(err));
});
}
publishViewing() {
// 連続したページ遷移の場合、最後のページ遷移のみを送信するためにタイムアウトを設定している。
if (this.publishTimeout) {
clearTimeout(this.publishTimeout);
}
this.publishTimeout = setTimeout(() => {
handlePublish(
publish({
userId: Id,
action: "Viewing",
pageReferenceJson: JSON.stringify(this.currentPageReference),
pageTitle: document.title
})
);
}, 1000);
}
handlePageTitleClick(event) {
const user = this.viewingUserList.find(
(u) => u.userId === event.target.value
);
console.log(
user,
user.Id,
user.pageReferenceJson,
JSON.parse(user.pageReferenceJson)
);
if (user) {
this[NavigationMixin.Navigate](JSON.parse(user.pageReferenceJson));
}
}
handleSayHiClick() {
handlePublish(
publish({
userId: Id,
action: "Alert",
pageReferenceJson: JSON.stringify(this.currentPageReference),
pageTitle: document.title
})
);
}
}
HTML
<template>
<template for:each={viewingUserList} for:item="user">
<div class="user" key={user.userId}>
<c-user-info user-id={user.userId} is-alerted={user.isAlerted}>
<template if:true={user.isNotMe}>
<lightning-icon
lwc:if={user.isAlerted}
icon-name="utility:anywhere_alert"
alternative-text="Alerted"
size="x-small"
variant="warning"
></lightning-icon>
<lightning-button-icon
lwc:else
class="slds-m-left_x-small"
icon-name="utility:anywhere_alert"
alternative-text="Say Hi"
size="medium"
title="Say Hi"
type="button"
value={user.userId}
onclick={handleSayHiClick}
variant="bare"
></lightning-button-icon>
</template>
</c-user-info>
<div class="viewing-page">
<lightning-button
variant="base"
label={user.pageTitle}
title={user.pageTitle}
onclick={handlePageTitleClick}
value={user.userId}
></lightning-button>
</div>
</div>
</template>
</template>
Apex
public with sharing class PlatformEventService {
@AuraEnabled
public static void publishViewing(
String userId,
String action,
String pageReferenceJson,
String pageTitle
) {
ViewingMemberEvent__e event = new ViewingMemberEvent__e(
UserId__c = userId,
Action__c = action,
PageReferenceJson__c = pageReferenceJson,
PageTitle__c = pageTitle
);
EventBus.publish(event);
}
}
作ってみた感想
コンポーネント同士つなげる感覚が新しく、Salesforceのプラットフォーム上でできることが色々とわかってきた感じがします。
今後のLWC開発において、Salesforce内のLWCが相互に作用する機能を作るときに、今回の経験が助けになるかな、と思いました。
この記事が気に入ったらサポートをしてみませんか?