初めに
Salesforce開発に関して、実際に実装が必要になった際に調べて役に立ったTipsを延々と紹介します(随時更新)
Apex関連
文字列配列の初期化&文字追加
- Java
String[] str = {“a”, “b”, “c”};
List<String> strList = new List<String>(Arrays.asList("Apple", "Orange", "Melon"));
- Apex
List<String> strList = new List<String>{'a', 'b', 'c'};
null、空文字チェック
オブジェクト項目の数値型をIntegerに変換
- そのまま取得した数値型を
obj__c.ItemNumber__c == 1
とか比較したりはできない
System.assertEquals(Integer.valueOf(Account.Number_of_Contacts__c) ,1);
オブジェクトから取得したデータを元にループ
for (Account account : [select id from account]) {
contacts.add(new Contact(firstname='first', lastname='last', accountId=account.id));
}
List型変数内の同じ値を数える
- Mapを使う
- String型にしているが、型を変えれば汎用的に使用OK(標準関数ない?)
public static Map<String, Integer> findDuplicateCount(List<String> targetStrList) {
Map<String, Integer> cntMap = new Map<String, Integer>();
for(String tmpKey : targetStrList) {
if(!cntMap.containsKey(tmpKey)) {
cntMap.put(tmpKey, 0);
} else {
cntMap.put(tmpKey, cntMap.get(tmpKey)+1);
}
}
return cntMap;
}
Map型の指定したKey値からValue値を取得する
-
KeySet()
を使用するとSet型のリストが返ってくる - Setをループすればいいだけだが、1Linerでやりたいとき(混乱するので丁寧な解説を)
-
KeySet()
で取得するSetをList型に変換 - Key値のList型変数になる
- Index値を指定してKey値を取り出す
- 指定のMapに対して
get()
でKeyを指定して値を取り出す
-
- 例はindex0の値を取り出す例
System.debug('Current Key: ' + new List<String>(tmpMap.keySet()).get(0));
System.debug('Current Value By Key: ' + tmpMap.get(new List<String>(tmpMap.keySet()).get(0)));
Cloneメソッド
- いきなり出てきて焦った。しかも
DeepClone
もあるらしい。-
Clone
は単純に現在のデータをCloneして、新しいIdで作り直す。 -
DeepClone
は関連リストも含めてCloneするらしい。 - 参照: Clone and DeepClone
-
clone(preserveId, isDeepClone, preserveReadonlyTimestamps, preserveAutonumber))
レコードの一括作成
sObject
型でデータを作ってListにaddしてinsertするだけ.
List<Contact> lstContact = new list<Contact>();
for(Account acc: lstAccounts)
{
Contact objContact = new Contact(LastName = 'test', AccountId = acc.Id);
lstContact.add(objContact );
}
if(!lstContact.isEmpty())
{
insert lstContact ;
}
作成したレコードのId
の取得
- フローとかでできるやつ、Apexでどうやる?ってなったが、Insertした後のレコード変数の
Id
にアクセスするだけだった。
Account acc = new Account(Name='Test');
insert acc;
system.debug("Inserted Account Id = " + acc.Id);
レコードの更新
- リストの中のフィールドを更新するとき
List<Account> acc =[SELECT Id, Name FROM Account];
List<Account> newAcc = new List<Account>();
for(Account a : acc){
a.Name ='Test';
newAcc.add(a);
}
update newAcc; //upsertでも可
複数レコードの更新をする際に、成功したものはコミットさせたい場合
Database.SaveResult[]
を使用してallOrNone
パラメータをfalse
にしておく
普通にinsert
を使用してしまうと一つでもエラーになった場合全てロールバックされるため
// Add Account
Account acct = new Account(
Name='Test Account',
NumberOfEmployees=10,
BillingCity='Tokyo');
List<Database.SaveResult> results = Database.insert(acct, false);
ちなみに、upsertの場合は下記となります
List<Database.UpsertResult> results = Database.upsert(ops, false);
メソッドと返り値はこちらが詳しい:
https://qiita.com/t_yano/items/19f8462f41b2a2bafded
レコードの復元
-
RECORD_ID
を復元したい再生レコードIdに変更すること。 - 再生レコードIdは変更データキャプチャしてれば取得できるが、それ以外の方法はまだ調べていない
Employee__c record = [SELECT Id,Name FROM Employee__c WHERE Id='RECORD_ID' ALL ROWS];
undelete record;
乱数を作りたい場合
- 下記のメソッドで作成。
- 引数として
UPPER_LIMIT
を渡しても良いし、Constにしても良いかと。 - 帰ってきた値で配列のindexを指定すれば文字列やレコードなんかもいけます。
static final Integer UPPER_LIMIT = 3;
Integer getRandomNumber() {
Integer rand = Math.round(Math.random()*1000);
return Math.mod(rand, UPPER_LIMIT);
}
こちらのGitHubのソースを参考に:
GitHub: Get Random Number
あるいはこちら:
https://qiita.com/Supply-net/items/c3e63edd2a3a260f06ba
正規表現で日本語を見つける
.find()
はBooleanが返却される。
String regex = '[\\p{IsHan}\\p{IsHiragana}\\p{IsKatakana}]';
Pattern regexPattern = Pattern.compile(regex);
Matcher regexMatcher = regexPattern.matcher(tmpStr);
if(regexMatcher.find()) {
// ...
}
ユーザのタイムゾーンに合わせた時間を取得
TimeZone tz = UserInfo.getTimeZone();
DateTime dt = Datetime.now();
// offset分の秒をGMTタイムゾーンに足す
DateTime nowJST = dt.addSeconds(tz.getOffset(dt)/1000);
Datetime値からDate値を取得して計算
- 例は1年足している
obj2.Date__c = obj1.CreatedDate.date().addYears(1);
Datetimeの差分を計算
- 秒(/1000だけ)
- 分(/1000/60)
- 時間(/1000/60/60)
- 日(/1000/60/60/24)などは計算で取得
Decimal days = decimal.valueOf(nowJST.getTime() - tmpSA.SchedStartTime.getTime())/1000/60/60/24;
ある時点のデータベース値を取得して、エラーが発生した場合はRollBackしたい場合
- 下記の通りに実装。
Savepoint sp = Database.setSavePoint();
try {
// logic
} catch(Exception ex) {
Database.rollback(sp);
return ex.getMessage();
}
あるオブジェクトの全ての項目API名を取得して、動的にSOQLを発行したい場合
-
SELECT * ...
ができないので項目名をいちいち記載したくないとき -
FieldDefinition
オブジェクトからAPI名を取得 -
SObject
型なのでString
に変換 - SOQLクエリを
String
で作成しておき、Database.query()
を実行 -
SELECT
のカラム名の','挿入はString.join()
を使用する
public static List<Boat__c> getData(String dataId) {
List<FieldDefinition> fieldApiNames = [SELECT QualifiedApiName FROM FieldDefinition WHERE EntityDefinition.QualifiedApiName = 'Data__c' ];
List<String> fieldApiStrNames = new List<String>();
for(FieldDefinition fieldApiName : fieldApiNames) {
fieldApiStrNames.add(String.valueOf(fieldApiName.QualifiedApiName));
}
String soql = 'SELECT ' + String.join(fieldApiStrNames, ',') + ' FROM Data__c';
if(String.isNotBlank(dataId)) {
soql += ' WHERE dataId__c = :dataId';
}
return Database.query(soql);
}
GEOLOCATION型のレコードの取得
- JSON型で返却したい、LatitudeとLongitudeがメソッドに渡される
- SELECT時も
Geolocation__latitude__s
って感じにしないといけない - WHEREやORDER BYで
DISTANCE()
、GEOLOCATION()
を使用しないといけないらしい
public static string getDataByLocation(Decimal latitude, Decimal longitude, Id dataId){
String query =
'SELECT Id, Name, Geolocation__latitude__s, Geolocation__longitude__s' +
'FROM Data__c' +
'WHERE DataId__c = :dataId' +
'ORDER BY DISTANCE(Geolocation__c, GEOLOCATION(latitude, longitude), \'mi\')' +
'LIMIT 10';
return JSON.serialize(Database.query(query));
}
組織の社内ユーザのみを取得したい場合
- ちなみに、他
UserType
はこちらを参考 - GuestやCommunity Userなども取得可能
SELECT Id, Name, UserType FROM User WHERE UserType = 'Standard'
取得したSObject型リストをId型リストに変換する
-
Map
クラスのコンストラクターに便利なものが存在していた...
List<Account> acc = [SELECT Id, Name FROM Account];
Set<Id> accIds = (new Map<Id, Account>(acc)).KeySet();
SOQLで取得した結果をMapに変換する
-
Id
とSObject
型のMapに変換する
Map<Id, Product2> prds = new Map<Id, Product2>([SELECT Id, Name FROM Product2 WHERE Id IN :ids]);
組織のドメイン取得
- 下記でString型で使用可能
- 取得できるのは
https://xxxx.salesforce.com
まで
System.URL.getSalesforceBaseURL().toExternalForm();
// 例えばケースレコードのLinkなら
String caseLink = URL.getSalesforceBaseUrl().toExternalForm() + '/lightning/r/Case/' + targetCase.Id + '/view/';
メール送信時にファイルを添付する
- あるレコードに添付されているファイルを添付してメールを送信したい場合
Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
// ここはメール送信の宛先を指定する
email.setToAddresses(new String[] { asset.ContactsEmail__c });
email.setSubject('テスト件名');
email.setHtmlBody('<html><body>' + 'テスト' + '</body></html>');
// Contact, Lead, UserのIdを指定. ここをセットするとメール追跡が可能
email.setTargetObjectId(asset.ContactId);
// 例:カスタムオブジェクトに添付されたファイルのContentDocumentLinkを取得
List<ContentDocumentLink> contentDocLinks = [
SELECT ContentDocumentId
FROM ContentDocumentLink
WHERE LinkedEntityId = :req.customObjectId
];
if (!contentDocLinks.isEmpty()) {
// ContentDocumentIdを取得
Set<Id> contentDocumentIds = new Set<Id>();
for (ContentDocumentLink cdl : contentDocLinks) {
contentDocumentIds.add(cdl.ContentDocumentId);
}
// 最新のContentVersionを取得
List<ContentVersion> contentVersions = [
SELECT VersionData, Title, FileExtension
FROM ContentVersion
WHERE ContentDocumentId IN :contentDocumentIds AND IsLatest = true
];
// ContentVersionの情報をEmailFileAttachmentにadd
List<Messaging.EmailFileAttachment> attachments = new List<Messaging.EmailFileAttachment>();
for (ContentVersion cv : contentVersions) {
Messaging.EmailFileAttachment attachment = new Messaging.EmailFileAttachment();
attachment.setFileName(cv.Title + '.' + cv.FileExtension);
attachment.setBody(cv.VersionData);
attachments.add(attachment);
}
email.setFileAttachments(attachments);
}
// メール送信
Messaging.SendEmailResult[] sendResults = Messaging.sendEmail(new Messaging.SingleEmailMessage[] { email });
if (sendResults[0].isSuccess()) {
res.isSuccess = true;
} else {
res.isSuccess = false;
res.errorMessage = sendResults[0].getErrors()[0].getMessage();
break; // エラーが発生した場合、ループを中断
}
- 上記コード例は1アドレスずつ送信しているが、下記のように
SingleEmailMessage
のリストを作ってまとめて送信することも可能
List<Messaging.SingleEmailMessage> emails = new List<Messaging.SingleEmailMessage>();
// 連絡先のリストを取得
List<Contact> contacts = [SELECT Id, Email FROM Contact WHERE Id IN :contactIds];
for (Contact c : contacts) {
Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
email.setToAddresses(new String[] { c.Email });
email.setSubject('テストメール - 追跡付き');
email.setHtmlBody('<html><body>これは追跡付きのテストメールです。</body></html>');
email.setTargetObjectId(c.Id);
emails.add(email);
}
// メールを送信
if (!emails.isEmpty()) {
try {
Messaging.sendEmail(emails);
System.debug('メールが正常に送信されました');
} catch (Exception e) {
System.debug('メール送信中にエラーが発生しました: ' + e.getMessage());
}
}
Apex Test
HTTP Call Outのテストがしたい場合
- Test Classに
HttpCalloutMock
を継承する - NGの場合も別途作成する
@isTest
global class HttpCallOutMockTestMethod implements HttpCalloutMock{
global HttpResponse respond(HTTPRequest request) {
HttpResponse response = new HttpResponse();
response.setHeader('Content-Type', 'application/json');
response.setStatus('OK');
response.setStatusCode(201);
return response;
}
}
- 下記の通り、TestクラスのMethodを使用して
Assert
をかける - 大概のTestロジックの書き方も下記に習うが、HttpCallOutの場合は
Test.startTest()
などが必要
@isTest
private class TestClassSample {
@isTestSetup
static void MakeData() {
// テストデータの作成
Opportunity opp = new Opportunity(Name='TestOK' ...);
// Custom Settingなど、Calloutで認証情報などがある場合はここで取得
// some logic here...
}
@isTest
static void HttpCallOutSucessTest() {
Opportunity opp = [SELECT Id, Name FROM Opportunity WHERE Name = 'TestOK'];
List<Id> OppIds = new List<Id>();
OppIds.add(opp.Id);
// HttpCallOutでの結果を返すMockをセット
Test.setMock(HttpCalloutMock.class, new ProjectCalloutServiceMock());
Test.startTest();
// 下記にテストしたいクラスのメソッドを記述
testTargetClass.TestTargetMethod(oppIds);
Test.stopTest();
opp = [SELECT StageName FROM Opportunity WHERE Id =: opp.Id];
// 期待する結果
System.assertEquals('XXXXX', opp.StageName);
}
}
SOQL
親から子オブジェクトの項目を取得するSOQL
- 絶対忘れる。 子オブジェクトのquery分は
()
で囲んでおく。
List<Account> lstAccount = [SELECT Id, Number_of_Contacts__c, (SELECT Id FROM contacts) FROM account WHERE Id in :setId ];
子から親オブジェクトの項目を取得するSOQL
- これもいつも忘れる。
SELECT FirstName, LastName, Account.Name FROM Contact
キューIDの取得
- これも忘れるんだよなぁ, Groupオブジェクトに対して
Type
を`Queueに指定。
Group queue = [SELECT Id FROM Group WHERE Name='Regional Dispatch' AND Type='Queue'];
キューに含まれるユーザIDの取得
- 上記で取得したキューIDを使用
-
Map<Id, GroupMember>
を使用して取得できるId
は実際のユーザIDにならないので注意.
List<GroupMember> userIdsInQueue = [SELECT UserOrGroupId FROM GroupMember WHERE GroupId = :queueId];
集計関数
- 集計関数を使った際は返却されるデータ型が
AggregateResults
のリスト型になる(なにそれ) - apexで結果を取得する際の例は下記
List<AggregateResult> results = [SELECT Industry, count(Id) total
FROM Account GROUP BY Industry];
for (AggregateResult ar : results) {
System.debug('Industry: ' + ar.get('Industry'));
System.debug('Total Accounts: ' + ar.get('total'));
}
ユーザに対してレコードアクセスがあるか確認
SELECT RecordId, HasEditAccess FROM UserRecordAccess WHERE UserId = [single ID] AND RecordId = [single ID]
Salesforce組織のオブジェクト名、フィールド名のAPI名を取得するSOQLクエリ
あるオブジェクトのアクセス権をプロファイルを指定して取得する
SELECT Id, Field, SObjectType, PermissionsRead, PermissionsEdit
FROM FieldPermissions
WHERE parentId IN ( SELECT id
FROM permissionset
WHERE PermissionSet.Profile.Name = 'システム管理者') AND SObjectType = 'Case'
Flow
Apex Classを呼び出す際
-
@InvocableMethod
をメソッド前に記載する - 引数は1つしか渡せません (Innerクラスを作らない限りは)
public with sharing class GetRecordsByMultipleIds {
@InvocableMethod
public static List<List<Product2>> GetRecordsByMultipleIds(List<List<ID>> inputCollectionIds) {
//...
}
入力をCollection変数、出力も複数レコードになる場合
- 非常に面倒な仕様です。
-
List<List<XXX>> var
を引数とします。 - Flowから渡したリストは
var.get(0)
で取得します。 - Returnも
List<List<XXX>> retVal
という風にしないといけないです。
-
public with sharing class GetRecordsByMultipleIds {
@InvocableMethod
public static List<List<Product2>> GetRecordsByMultipleIds(List<List<ID>> inputCollectionIds) {
List<ID> queryIds = inputCollectionIds.get(0);
List<Product2> products = [SELECT Id, Name, Family FROM Product2 WHERE Id IN:queryIds ORDER BY Name ASC];
List<List<Product2>> result = new List<List<Product2>>();
result.add(products);
return result;
}
}
Genericな引数と戻り値にしたい場合
FlowからApexクラスを呼び出す際、結構文字列操作したい場合ってないですか?
いちいちObjectの型を指定せずに、様々なオブジェクトで使いたい場合はよくやりそう。
例えばName
のフィールド値とか。
- ちなみにこの場合、Flow側から入力/出力のオブジェクト型を指定することができます!
public with sharing class TestClass {
@InvocableMethod
public static List<List<SObject>> TestMethod(List<List<SObject>> records) {
List<SObject> tmp = records.get(0);
for(Integer i=0; i<tmp.size(); i++) {
// SObjectのNameフィールドの値を取得する
String name = String.valueOf(tmp.get(i).get('Name'));
if(expression) {
//...
}
}
return result; //これはList<List<SObject>>のリスト
}
{
フローからApexに複数の引数を渡したい場合
- 引数、返却値を内部クラスに指定することで可能だった...!
- Class内に複数変数を定義できるので、かなり自由度が上がる
- 引数はClassのリスト型にする
public with sharing class IncrementQuantiyForDuplicatedSerialNo {
@InvocableMethod
public static List<FlowOutputs> updateQuantityForDuplicatedSerialNo(List<FlowInputs> inputs) {
FlowInputs input = inputs.get(0);
List<ProductItem> pi = new List<ProductItem>(input.recordList);
String scannedSerialNo = input.serialNo;
List<FlowOutputs> results = new List<FlowOutputs>();
FlowOutputs rst = new FlowOutputs();
for(ProductItem tmpPi : pi) {
if(tmpPi.SerialNumber == scannedSerialNo) {
tmpPi.QuantityOnHand += 1;
rst.productName = tmpPi.ProductName;
rst.serialNo = tmpPi.SerialNumber;
rst.quantity = tmpPi.QuantityOnHand;
}
}
rst.updatedList = new List<ProductItem>(pi);
results.add(rst);
System.debug('Return Value: Result ' + results);
return results;
}
public class FlowInputs {
@InvocableVariable
public List<ProductItem> recordList;
@InvocableVariable
public String serialNo;
}
public class FlowOutputs {
@InvocableVariable
public List<ProductItem> updatedList;
@InvocableVariable
public String productName;
@InvocableVariable
public String serialNo;
@InvocableVariable
public Decimal quantity;
}
}
画面フロー上にCSSを読み込みたい場合
- 定数: テキストとして、CSSを入力する
background-color: #0076D2; border-radius: 30px; color: #fff; padding: 15px 60px; font-size: 18px;
- テキストテンプレートを作成する
- この際に、プレーンテキストを選択
<a href="" style="{!sampleCss}">応募する!</a>
下記みたいなのは作れる. (border-radius
が効かない...)
Trigger
文法
-
Trigger.New
でループさせる。 - Fireの条件が複数ある場合、Triggerのタイミングをキーに処理分岐も可能
// オブジェクト名指定、いつFireするかのタイミングを引数に指定
trigger TestTrigger on Opportunity (after insert, after update) {
for(Opportunity opp : Trigger.New) {
if(trigger.isInsert) {
// ...
}
// ...
}
}
トリガされた時点での変更前の値の取得
-
after update
の場合に使用するケースが多い
trigger TestTrigger on Case (after update) {
for(Case record : Trigger.New) {
Id priorId = Trigger.oldMap.get(record.Id).OwnerId;
// logic
if(priorId != record.OwnerId) {
// ...
}
}
}
LWC
Lightning Buttonのwidthを100%にしたい際
slds-button_stretch
をclassにapplyしても適用されない。
代わりにstyle
でwidthを100%にする。
<lightning-button
label="検索"
onclick={handleSearchKeyword}
variant="brand"
style="display: grid; width: 100%;">
</lightning-button>
LWCから画面フローを呼び出したい場合
-
Winter'23からついにLWC版の画面フロー呼び出しが可能に。
-
ハマってしまったのだが、html側からフローを呼び出す際、例えば入力変数がセットされていない場合はフラグなどでFlowを描画するかどうかのフラグを持ち、(
<template if:true={renderFlow}>
などで)Trueになって初めてフローを描画する形にした方が良い。- フローを描画するタイミングで確実に入力変数が渡されないと、変数のデータ型タイプエラーが発生することがある。
- 例:
onclick
でボタンを押した際にデータ精査を行い、flowInputVariables
に値をセット、その後でフローを描画するなど
-
htmlに下記を記述(変数名は固定)
<template if:true={renderFlow}>
<lightning-flow
flow-api-name={flowApiName}
flow-input-variables={flowInputVariables}
onstatuschange={handleFlowStatusChange}>
</lightning-flow>
</template>
//...
flowApiName = 'flow_name';
flowInputVariables = [];
renderFlow = false;
someFunc() {
// ...
this.flowInputVariables = [
{
name: 'recordId',
type: 'String',
value: this.selectedResourceId
},
{
name: 'InvitationURLPrefix',
type: 'String',
value: this.InvitationURLPrefix
},
];
// 何かしらのロジックで描画を可にするフラグを用意
this.renderFlow = true;
}
handleFlowStatusChange(event) {
console.log("flow status : ", event.detail.status);
if (event.detail.status === "FINISHED") {
this.dispatchEvent(
new ShowToastEvent({
title: "成功",
message: "サンプルメッセージ",
variant: "success",
})
);
}
}
画面フローからLWCを呼び出す際に、Collection変数を渡したい場合
-
js-meta.xml
ファイルに対して、下記の通り記述する -
type="String[]"
を指定するとエラーがVSCode上では表示されるのだが、なぜかデプロイは成功する
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>56.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__FlowScreen</target>
</targets>
<targetConfigs>
<targetConfig targets="lightning__FlowScreen">
<property name="sampleNumbers" type="String[]" label="Sample Numbers"/>
<property name="sampleTexts" type="String[]" label="Sample Texts"/>
</targetConfig>
</targetConfigs>
</LightningComponentBundle>
- JavaScript側は xmlで指定した変数を
@api
化しておくだけ
import { LightningElement, api } from 'lwc';
export default class DataTableOnFlow extends LightningElement {
@api sampleNumbers;
@api sampleTexts;
}
- 画面フローに作成したLWCを配置する
- LWCから、コレクション変数が選択できるようになっている
LWCから画面フローに対して値をアウトプットさせたい場合
- 例えば、画面フロー上のLWCが読み込まれた場合に
@wire
apex あるいは 明示的Apexにてデータを取得してアウトプットとして画面フローに戻したい場合 - 一例として、SObjectのリストを返す場合を想定
-
type
に"@salesforce/schema/SObjectName[]"
を指定する - ちなみに、Genericな型にInputであればできる? 参考
Outputは試したらエラーになりました。
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>56.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__FlowScreen</target>
</targets>
<targetConfigs>
<targetConfig targets="lightning__FlowScreen">
<property name="returnData" type="@salesforce/schema/SObjectName[]" label="Return Data" role="outputOnly" />
</targetConfig>
</targetConfigs>
</LightningComponentBundle>
-
Apex側は、フローに返すのであれば、
List<List<SObject>>
型にしないとイケさそうに思えるが、そもそも@InvocableMethod
ではないので、xmlと同じで、返却の型は<List<SObject>
とする。 -
JavaScript側は、Outputする変数は
@api
にしておく -
@wire
のapexメソッドで返却される値に対して、@api
変数を指定して、問題なく動作しました。
import { LightningElement, api, wire } from 'lwc';
import testMethod from '@salesforce/apex/TestClass.testMethod';
export default class DataTableOnFlow extends LightningElement {
@api returnData;
@wire(testMethod, {strings: '$sampleTexts', nums: '$sampleNumbers'})
wiredMethod({error, data}) {
if(data) {
this.returnData = data;
this.error = undefined;
} else if(error) {
console.log('error')
console.log(error)
}
}
}
LWC上で音を出すアクションを作成
- 例えばボタンが押されたら、音を出すなど
- まずは、音声ファイルを静的リソースに登録する
- Javacriptで下記の通り実装するだけ
// sound from static resource
import staticSuccessSound from '@salesforce/resourceUrl/SuccessSound';
export default class Sample extends LightningElement {
successSound = new Audio(staticSuccessSound);
connectedCallback() {
this.successSound.play();
}
}
データテーブルに検索機能を付ける
- フローではできるのに、実はLWCでは標準オプションが無いので、作らないといけない
<template if:true={showProductTable}>
<div class="slds-m-top_large">
<h3 class="slds-text-heading_small slds-m-bottom_small">検索結果</h3>
<!-- この部分が検索カラム部分 -->
<div class="slds-form-element slds-m-bottom_medium">
<div class="slds-form-element__control slds-grid">
<div class="slds-col slds-size_4-of-5">
<lightning-input
id="search-input"
type="search"
variant="label-hidden"
placeholder="検索語を入力"
value={searchTerm}
onchange={handleSearchTermChange}
></lightning-input>
</div>
<div class="slds-col slds-size_1-of-5 slds-p-left_xx-small">
<lightning-button
label="検索"
variant="brand"
onclick={handleSearch}
></lightning-button>
</div>
</div>
</div>
<!-- ここまでが検索カラム部分 -->
<lightning-datatable
key-field="Id"
data={data}
columns={columns}
onsave={handleSave}
draft-values={draftValues}
hide-checkbox-column
></lightning-datatable>
</div>
</template>
<template if:true={noProductsFound}>
<div class="slds-text-color_error slds-p-vertical_medium">
一致する商品がありません
</div>
</template>
export default class TestDatatable extends LightningElement {
// ...
// 省略
// ...
@track searchResult = [];
searchTerm = '';
// ...
// 省略
// ...
handleSearchTermChange(event) {
this.searchTerm = event.target.value;
}
handleSearch() {
if (this.searchTerm) {
// apexなどで取得したレコードリストを、`this.searchTerm`を使用して検索
} else {
// apexなどで取得したレコードリストを返却
}
this.showProductTable = this.searchResult.length > 0;
this.noProductsFound = this.searchResult.length === 0;
}
}