Lightning Web コンポーネント(LWC)
Lightning Web コンポーネントを使用する理由
Lightning Web コンポーネントは、コア Web コンポーネント標準を使用し、Salesforce でサポート対象のブラウザで適切に動作するために必要な機能のみを提供します。Lightning Web コンポーネントは、ブラウザでネイティブに実行されるコード上に構築されるため、軽量で卓越したパフォーマンスを発揮します。記述するコードの大部分は、標準の JavaScript および HTML です。
シンプルなコンポーネントの作成
JavaScriptファイル:コアビジネスロジックとイベント処理を定義します。
HTMLファイル:コンポーネントの構造を提供します。
CSS ファイル:コンポーネントのデザインとアニメーションを提供します。※CSS必須ではない
作成のIDE
・インストールツール
VSCode
・お勧めツール
webcomponents.dev(デザイン表示、直観的なツール)
** PS:webcomponents.devツールリンク**
https://webcomponents.dev/create/lwc
VSCodeでLightning Web コンポーネントを作成
①Visual Studio Code で、Ctrl+Shift+P (Windows) または Cmd+Shift+P (macOS) を押して、コマンドパレットを開きます。
②「SFDX: プロジェクトを作成」選択
テンプレート:標準
プロジェクト名:bikeCard
③lwc フォルダを右クリックして [SFDX: Create Lightning Web Component (SFDX: Lightning Web コンポーネントを作成)]
フォルダー:lwc
コンポーネント名:bikeCard
④コンポーネントファイルのコンテンツをこのユニットからVisualStudioCodeのファイルにコピーします。
bikeCard.html
BikeCard.js
bikeCard.js-meta.xml
⑤BikeCardコンポーネントファイルを組織にデプロイします
※デプロイ失敗、下記のエラーが出た時、一つの原因はプロジェクト作成する時選択のフォルダが長すぎまた別のプロジェクトの中にフォルダ作成した。
「Unable to build Lightning Component source for markup://c:bikeCard: Invalid suffix: json.」
⑥Lightningアプリページを作成します。
Lightningアプリケーションビルダーに新規「App Page」作成
Label: Bike Card
Developer Name: Bike_Card
⑦bikeCardコンポーネントをページに追加します
⑧すべてのユーザーのページを有効化する
LightningWebコンポーネントでイベントを処理する
tile: 個々の品目を表示します。
list: タイルを配置します。
detail: タイルがクリックされると品目の詳細を表示します (作成した bikeCard に類似)。
selector: すべてのコンポーネントが含まれます。コンテナコンポーネントは必要ありませんが、ここでは、イベント処理をしやすくするためにコンテナを 1 つ使用します。
コンポーネントの関係
一部のコンポーネントは、他のコンポーネント内にネストされています。
複雑なコンポーネント(複数の親コンポーネントと子コンポーネントを含むコンポーネント)では、コンポーネントは上下に通信できます。
スタイル設定
①*.cssの中に、設定したいの項目をCSS設定可
・設定サンプル
②Salesforce Lightning Design System(SLDS)のスタイルの適用。
※設定できるスタイルは下記のリンク参照
https://lightningdesignsystem.com/design-tokens/
動的データ表示
キーワード
変数声明
@track XXX コンポーネント内部私有変数、動的、変更すると画面連動
@api XXX 外部から取得可能、動的、変更すると画面連動
XXX コンポーネント内部私有変数、画面初期化する時一回のみ取得、変更しても、画面変わらない
Apex呼び出し
・@wire 初期化する時呼び出し、データ取得のみお勧め、データ操作関連は勧めではない
※パラメータは必ず初期値設定必要、設定しない場合、データ取得できないかもしれません
import getdata from '@salesforce/apex/MydataController.getdata';
export default class GetData extends LightningElement {
let recordId='';
@wire(getdata, { recordId: '$recordId' })
wiredData({ error, data }) {
if(data){
//データ取得後の処理
}else if(error){
//エラーメッセージ出力
}
}
または
import getdata from '@salesforce/apex/MydataController.getdata';
export default class GetData extends LightningElement {
let recordId='';
@wire(getdata, { recordId: '$recordId' })
.then(data => {
//データ取得後の処理
})
.catch(error =>{
//エラーメッセージ出力
}
}
・Apexクラスリネーム、直接呼び出し
import insertDatafrom '@salesforce/apex/MydataController.insertData';
export default class GetData extends LightningElement {
insertData({data: dataRecord})
.then(result => {
//成功処理
})
.catch(error => {
//エラー処理
});
}
}
ソース
data
export const bikes = [
{"apiName":"Product__c","childRelationships":{},"fields":{"Category__c":{"displayValue":"Mountain","value":"Mountain"},"CreatedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Description__c":{"displayValue":null,"value":"A durable e-bike with great looks."},"Id":{"displayValue":null,"value":"a0256000001F1arAAC"},"LastModifiedDate":{"displayValue":null,"value":"2018-10-12T02:57:48.000Z"},"Level__c":{"displayValue":"Racer","value":"Racer"},"MSRP__c":{"displayValue":"$7,800","value":7800},"Material__c":{"displayValue":"Carbon","value":"Carbon"},"Name":{"displayValue":null,"value":"DYNAMO X1"},"Picture_URL__c":{"displayValue":null,"value":"https://s3-us-west-1.amazonaws.com/sfdc-demo/ebikes/dynamox1.jpg"},"SystemModstamp":{"displayValue":null,"value":"2018-10-12T02:57:48.000Z"}},"id":"a0256000001F1arAAC","lastModifiedById":null,"lastModifiedDate":"2018-10-12T02:57:48.000Z","recordTypeInfo":null,"systemModstamp":"2018-10-12T02:57:48.000Z"},
{"apiName":"Product__c","childRelationships":{},"fields":{"Category__c":{"displayValue":"Mountain","value":"Mountain"},"CreatedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Description__c":{"displayValue":null,"value":"A durable e-bike with great looks."},"Id":{"displayValue":null,"value":"a0256000001F1atAAC"},"LastModifiedDate":{"displayValue":null,"value":"2018-10-10T17:26:47.000Z"},"Level__c":{"displayValue":"Racer","value":"Racer"},"MSRP__c":{"displayValue":"$6,802","value":6802},"Material__c":{"displayValue":"Aluminum","value":"Aluminum"},"Name":{"displayValue":null,"value":"DYNAMO X2"},"Picture_URL__c":{"displayValue":null,"value":"https://s3-us-west-1.amazonaws.com/sfdc-demo/ebikes/dynamox2.jpg"},"SystemModstamp":{"displayValue":null,"value":"2018-10-10T17:26:47.000Z"}},"id":"a0256000001F1atAAC","lastModifiedById":null,"lastModifiedDate":"2018-10-10T17:26:47.000Z","recordTypeInfo":null,"systemModstamp":"2018-10-10T17:26:47.000Z"},
{"apiName":"Product__c","childRelationships":{},"fields":{"Category__c":{"displayValue":"Mountain","value":"Mountain"},"CreatedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Description__c":{"displayValue":null,"value":"A durable e-bike with great looks."},"Id":{"displayValue":null,"value":"a0256000001F1auAAC"},"LastModifiedDate":{"displayValue":null,"value":"2018-10-09T04:37:56.000Z"},"Level__c":{"displayValue":"Enthusiast","value":"Enthusiast"},"MSRP__c":{"displayValue":"$5,601","value":5601},"Material__c":{"displayValue":"Aluminum","value":"Aluminum"},"Name":{"displayValue":null,"value":"DYNAMO X3"},"Picture_URL__c":{"displayValue":null,"value":"https://s3-us-west-1.amazonaws.com/sfdc-demo/ebikes/dynamox3.jpg"},"SystemModstamp":{"displayValue":null,"value":"2018-10-09T04:37:56.000Z"}},"id":"a0256000001F1auAAC","lastModifiedById":null,"lastModifiedDate":"2018-10-09T04:37:56.000Z","recordTypeInfo":null,"systemModstamp":"2018-10-09T04:37:56.000Z"},
{"apiName":"Product__c","childRelationships":{},"fields":{"Category__c":{"displayValue":"Mountain","value":"Mountain"},"CreatedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Description__c":{"displayValue":null,"value":"A durable e-bike with great looks."},"Id":{"displayValue":null,"value":"a0256000001F1avAAC"},"LastModifiedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Level__c":{"displayValue":"Enthusiast","value":"Enthusiast"},"MSRP__c":{"displayValue":"$5,500","value":5500},"Material__c":{"displayValue":"Aluminum","value":"Aluminum"},"Name":{"displayValue":null,"value":"DYNAMO X4"},"Picture_URL__c":{"displayValue":null,"value":"https://s3-us-west-1.amazonaws.com/sfdc-demo/ebikes/dynamox4.jpg"},"SystemModstamp":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"}},"id":"a0256000001F1avAAC","lastModifiedById":null,"lastModifiedDate":"2018-10-09T03:29:52.000Z","recordTypeInfo":null,"systemModstamp":"2018-10-09T03:29:52.000Z"},
{"apiName":"Product__c","childRelationships":{},"fields":{"Category__c":{"displayValue":"Mountain","value":"Mountain"},"CreatedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Description__c":{"displayValue":null,"value":"A durable e-bike with great looks."},"Id":{"displayValue":null,"value":"a0256000001F1azAAC"},"LastModifiedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Level__c":{"displayValue":"Enthusiast","value":"Enthusiast"},"MSRP__c":{"displayValue":"$4,600","value":4600},"Material__c":{"displayValue":"Aluminum","value":"Aluminum"},"Name":{"displayValue":null,"value":"FUSE X1"},"Picture_URL__c":{"displayValue":null,"value":"https://s3-us-west-1.amazonaws.com/sfdc-demo/ebikes/fusex1.jpg"},"SystemModstamp":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"}},"id":"a0256000001F1azAAC","lastModifiedById":null,"lastModifiedDate":"2018-10-09T03:29:52.000Z","recordTypeInfo":null,"systemModstamp":"2018-10-09T03:29:52.000Z"},
{"apiName":"Product__c","childRelationships":{},"fields":{"Category__c":{"displayValue":"Commuter","value":"Commuter"},"CreatedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Description__c":{"displayValue":null,"value":"A durable e-bike with great looks."},"Id":{"displayValue":null,"value":"a0256000001F1b2AAC"},"LastModifiedDate":{"displayValue":null,"value":"2018-10-09T04:41:56.000Z"},"Level__c":{"displayValue":"Beginner","value":"Beginner"},"MSRP__c":{"displayValue":"$3,200","value":3200},"Material__c":{"displayValue":"Aluminum","value":"Aluminum"},"Name":{"displayValue":null,"value":"ELECTRA X1"},"Picture_URL__c":{"displayValue":null,"value":"https://s3-us-west-1.amazonaws.com/sfdc-demo/ebikes/electrax1.jpg"},"SystemModstamp":{"displayValue":null,"value":"2018-10-09T04:41:56.000Z"}},"id":"a0256000001F1b2AAC","lastModifiedById":null,"lastModifiedDate":"2018-10-09T04:41:56.000Z","recordTypeInfo":null,"systemModstamp":"2018-10-09T04:41:56.000Z"},
{"apiName":"Product__c","childRelationships":{},"fields":{"Category__c":{"displayValue":"Commuter","value":"Commuter"},"CreatedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Description__c":{"displayValue":null,"value":"A durable e-bike with great looks."},"Id":{"displayValue":null,"value":"a0256000001F1b3AAC"},"LastModifiedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Level__c":{"displayValue":"Beginner","value":"Beginner"},"MSRP__c":{"displayValue":"$3,200","value":3200},"Material__c":{"displayValue":"Aluminum","value":"Aluminum"},"Name":{"displayValue":null,"value":"ELECTRA X2"},"Picture_URL__c":{"displayValue":null,"value":"https://s3-us-west-1.amazonaws.com/sfdc-demo/ebikes/electrax2.jpg"},"SystemModstamp":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"}},"id":"a0256000001F1b3AAC","lastModifiedById":null,"lastModifiedDate":"2018-10-09T03:29:52.000Z","recordTypeInfo":null,"systemModstamp":"2018-10-09T03:29:52.000Z"},
{"apiName":"Product__c","childRelationships":{},"fields":{"Category__c":{"displayValue":"Commuter","value":"Commuter"},"CreatedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Description__c":{"displayValue":null,"value":"A durable e-bike with great looks."},"Id":{"displayValue":null,"value":"a0256000001F1b6AAC"},"LastModifiedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Level__c":{"displayValue":"Beginner","value":"Beginner"},"MSRP__c":{"displayValue":"$2,700","value":2700},"Material__c":{"displayValue":"Aluminum","value":"Aluminum"},"Name":{"displayValue":null,"value":"ELECTRA X3"},"Picture_URL__c":{"displayValue":null,"value":"https://s3-us-west-1.amazonaws.com/sfdc-demo/ebikes/electrax3.jpg"},"SystemModstamp":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"}},"id":"a0256000001F1b6AAC","lastModifiedById":null,"lastModifiedDate":"2018-10-09T03:29:52.000Z","recordTypeInfo":null,"systemModstamp":"2018-10-09T03:29:52.000Z"},
{"apiName":"Product__c","childRelationships":{},"fields":{"Category__c":{"displayValue":"Commuter","value":"Commuter"},"CreatedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Description__c":{"displayValue":null,"value":"A durable e-bike with great looks."},"Id":{"displayValue":null,"value":"a0256000001F1b7AAC"},"LastModifiedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Level__c":{"displayValue":"Beginner","value":"Beginner"},"MSRP__c":{"displayValue":"$2,700","value":2700},"Material__c":{"displayValue":"Aluminum","value":"Aluminum"},"Name":{"displayValue":null,"value":"ELECTRA X4"},"Picture_URL__c":{"displayValue":null,"value":"https://s3-us-west-1.amazonaws.com/sfdc-demo/ebikes/electrax4.jpg"},"SystemModstamp":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"}},"id":"a0256000001F1b7AAC","lastModifiedById":null,"lastModifiedDate":"2018-10-09T03:29:52.000Z","recordTypeInfo":null,"systemModstamp":"2018-10-09T03:29:52.000Z"}
];
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>48.0</apiVersion>
<isExposed>false</isExposed>
</LightningComponentBundle>
detail
<template>
<template if:true={product}>
<div class="container">
<div class="slds-text-heading_small">{product.fields.Name.value}</div>
<div class="price">{product.fields.MSRP__c.displayValue}</div>
<div class="description">{product.fields.Description__c.value}</div>
<img class="product-img" src={product.fields.Picture_URL__c.value}></img>
<p>
<lightning-badge label={product.fields.Material__c.value}></lightning-badge>
<lightning-badge label={product.fields.Level__c.value}></lightning-badge>
</p>
<p>
<lightning-badge label={product.fields.Category__c.value}></lightning-badge>
</p>
</div>
</template>
<template if:false={product}>
<div class="slds-text-heading_medium">Select a bike</div>
</template>
</template>
body{
margin: 0;
}
.price {
color: green;
font-weight: bold;
}
import { LightningElement, api } from 'lwc';
import { bikes } from 'c/data';
export default class Detail extends LightningElement {
// Ensure changes are reactive when product is updated
product;
// Private var to track @api productId
_productId = undefined;
// Use set and get to process the value every time it's
// requested while switching between products
set productId(value) {
this._productId = value;
this.product = bikes.find(bike => bike.fields.Id.value === value);
}
// getter for productId
@api get productId(){
return this._productId;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>48.0</apiVersion>
<isExposed>false</isExposed>
</LightningComponentBundle>
tile
<template>
<div class="container">
<a onclick={tileClick}>
<div class="title">{product.fields.Name.value}</div>
<img class="product-img" src={product.fields.Picture_URL__c.value}></img>
</a>
</div>
</template>
.container {
border: 1px rgb(168, 166, 166) solid;
border-radius: 5px;
background-color: white;
margin:5px;
padding: 2px;
max-width: 110px;
display: flex;
}
.title {
font-weight: strong;
}
.product-img {
max-width: 100px;
}
a {
text-decoration: none;
}
a:link {
color: rgb(159, 159, 159);
}
a:visited {
color: green;
}
a:hover {
color: hotpink;
}
a:active {
color: blue;
}
import { LightningElement, api } from 'lwc';
export default class Tile extends LightningElement {
@api product;
tileClick() {
const event = new CustomEvent('tileclick', {
// detail contains only primitives
detail: this.product.fields.Id.value
});
// Fire the event from c-tile
this.dispatchEvent(event);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>48.0</apiVersion>
<isExposed>false</isExposed>
</LightningComponentBundle>
list
<template>
<div class="container">
<template for:each={bikes} for:item="bike">
<c-tile
key={bike.fields.Id.value}
product={bike}
ontileclick={handleTileClick}>
</c-tile>
</template>
</div>
</template>
.container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
import { LightningElement } from 'lwc';
import { bikes } from 'c/data';
export default class List extends LightningElement {
bikes = bikes;
handleTileClick(evt) {
// This component wants to emit a productselected event to its parent
const event = new CustomEvent('productselected', {
detail: evt.detail
});
// Fire the event from c-list
this.dispatchEvent(event);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>48.0</apiVersion>
<isExposed>false</isExposed>
</LightningComponentBundle>
selector
<template>
<div class="wrapper">
<header class="header">Available Bikes for {name}</header>
<section class="content">
<div class="columns">
<main class="main" >
<c-list onproductselected={handleProductSelected}></c-list>
</main>
<aside class="sidebar-second">
<c-detail product-id={selectedProductId}></c-detail>
</aside>
</div>
</section>
</div>
</template>
body {
margin: 0;
}
.wrapper{
min-height: 100vh;
background: #ccc;
display: flex;
flex-direction: column;
}
.header, .footer{
height: 50px;
background: rgb(255, 255, 255);
color: rgb(46, 46, 46);
font-size: x-large;
padding: 10px;
}
.content {
display: flex;
flex: 1;
background: #999;
color: #000;
}
.columns{
display: flex;
flex:1;
}
.main{
flex: 1;
order: 2;
background: #eee;
}
.sidebar-first{
width: 20%;
background: #ccc;
order: 1;
}
.sidebar-second{
width: 30%;
order: 3;
background: #ddd;
}
//wire 導入
import { LightningElement, wire } from 'lwc';
//import { 「wire アダプタの ID」 } from '「wire アダプタ関数が含まれるモジュールの ID」'; レコード取得API導入
import { getRecord, getFieldValue } from 'lightning/uiRecordApi';
//取得対象はID設定(項目をインポートして、ワイヤアダプタの設定オブジェクトで使用します。)
import Id from '@salesforce/user/Id';
//同上 取得対象「ユーザ.名前」はNAME_FIELDに設定(項目をインポートして、ワイヤアダプタの設定オブジェクトで使用します。)
import NAME_FIELD from '@salesforce/schema/User.Name';
//定数定義
const fields = [NAME_FIELD];
export default class Selector extends LightningElement {
selectedProductId;
handleProductSelected(evt) {
this.selectedProductId = evt.detail;
}
//取得したIdをuserId変数に設定
userId = Id;
//変数userIdを条件で、「ユーザ.名前」を取得する
@wire(getRecord, { recordId: '$userId', fields })
//取得データ格納
user;
get name() {
//取得データから画面に返却するため、name設定
return getFieldValue(this.user.data, NAME_FIELD);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>48.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__AppPage</target>
<target>lightning__RecordPage</target>
<target>lightning__HomePage</target>
</targets>
</LightningComponentBundle>
参考リンク
wireの説明
webcomponents.dev
SLDS