非営利団体のギフトエントリグリッドテンプレートの列コンポーネントの設定
ギフトエントリグリッドテンプレートの表示および編集動作にカスタム Lightning Web コンポーネントを使用するようにコンポーネント列を設定します。
必要なエディション
| 必要なエディション |
|---|
| 使用可能: Lightning Experience |
使用可能なエディション: Education Cloud を含む Enterprise Edition、Performance Edition、Unlimited Edition、Developer Edition 使用可能なエディション: Enterprise Edition、Unlimited Edition、および Developer Edition (Nonprofit Cloud 付属) |
| 必要なユーザー権限 | |
|---|---|
| 列コンポーネントを設定する | FundraisingAccess 権限セット または 「Education Cloud フルアクセス」権限セット |
- [表示列] で、設定する列の [列の編集] をクリックします。
- 列の型として [コンポーネント] を選択します。
- 列の表示ラベルを入力します。
- [セルの表示コンポーネント] から、表示コンポーネントを表す Lightning Web コンポーネントを選択します。
- [セルの編集コンポーネント] から、編集コンポーネントを表す Lightning Web コンポーネントを選択します。
- [Field API Name] で、Lightning Web コンポーネントに対応付けるフィールドを選択します。
- 変更内容を保存します。
例: カスタムコンポーネントを使用したデータの照合と入力
デフォルトテンプレートの [支援者] 列をバーコードオプションで上書きするカスタムコンポーネントを作成します。ユーザーが編集コンポーネントに値を入力すると、カスタムバーコードオブジェクトで一致する値が検索されます。一致が見つかった場合、そのレコードの値がギフトエントリグリッドの他の列に入力されます。
| 必要なエディション |
|---|
| 使用可能: Lightning Experience |
使用可能なエディション: Education Cloud を含む Enterprise Edition、Performance Edition、Unlimited Edition、Developer Edition 使用可能なエディション: Enterprise Edition、Unlimited Edition、および Developer Edition (Nonprofit Cloud 付属) |
セルの表示コンポーネント
デフォルトでは、[仕様] 列コンポーネントには空白の値が表示されます。1 つの指定が [ギフトエントリ] 行に関連付けられている場合、列コンポーネントはその指定に金額を連結して、グリッド (100 ドルなど) に表示します。 2 つ以上の指定がある場合、グリッドには集計値 (2 つの指定など) が表示されます。
セル表示コンポーネントには次の要件があります。
- JavaScriptjs ファイルの tagName 値。
- グリッドから渡される受信プロパティとして機能する @api params。
このサンプルでは、表示コンポーネントにバーコード列が表示されています。データが入力される前は、待機中として表示されます。一致する値が検出されると、列が更新されて支援者の名前が表示されます。
giftEntryGridBarCodeDisplayColumn.html
<template>
<p>{columnDisplayValue}</p>
</template>
giftEntryGridBarCodeDisplayColumn.js
import { api, LightningElement } from 'lwc';
export default class GiftEntryGridBarCodeDisplayColumn extends LightningElement {
/**
* params is the only inbound property passed to this component from Gift Entry Grid
*/
@api params;
/**
* Example Method:
* This getter function displays various fields in a display component
* that is specified in the yaml file configuration.
* @returns {*|string}
*/
get columnDisplayValue() {
let response = this.params?.data?.properties?.barCodeValue;
if (this.params?.data?.LastName) {
response = this.params?.data?.FirstName + " " + this.params?.data?.LastName;
if (this.params?.data?.Salutation) {
response = this.params?.data?.Salutation + " " + response;
}
}
return response || '(pending)';
}
}
// Tagname is required for the component to function
GiftEntryGridBarCodeDisplayColumn.tagName = 'c-gift-entry-grid-bar-code-display-column';
セルの編集コンポーネント
デフォルトテンプレートの「指定」列の編集コンポーネントには、参照項目と同様に機能する Lightning 入力項目があります。ユーザーが値を入力すると、カスタムバーコードオブジェクトで一致する値が検索されます。一致が見つかった場合、そのレコードの値がギフトエントリグリッドの他の列に入力されます。
セルの編集コンポーネントには次の要件があります。
- JavaScript ファイルの
tagNameパラメータ。 - true に設定された JavaScript ファイルの
isPopupパラメータ。 @salesforce/messageChannel/lightning__giftEntryGridComponentActionからの Gift Entry Grid Lightning Message Service チャネルの使用。- 次のサンプルでコールアウトされているその他のメソッドと変数。
giftEntryGridBarCodeColumnComponent.html
<template>
<lightning-input
label="Bar Code"
name="barCode"
value={barCode}
onblur={handleBarCodeChanged}
onerror={handleError}
placeholder="Scan bar code here"
variant="label-hidden">
</lightning-input>
</template>
giftEntryGridBarCodeColumnComponent.js
import { LightningElement, api } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
/**
* Apex controller used by this example to handle data queries, etc.
*/
import getBarCodeScanData from '@salesforce/apex/GiftEntryBarCodeScanLookupController.getBarCodeScanData';
/**
* Lightning Message Service to enable this component to communicate back to Gift Entry Grid
*/
import { publish, createMessageContext } from 'lightning/messageService';
import GiftEntryGridComponentAction from "@salesforce/messageChannel/lightning__giftEntryGridComponentAction";
const messageContext = createMessageContext();
const DEFAULT_GIFT_AMOUNT = 100.00;
const DEFAULT_PAYMENT_METHOD = "Credit Card";
export default class GiftEntryGridBarCodeColumnComponent extends LightningElement {
/**
* Required for the underlying grid component to properly render this as a gift entry grid cell
*/
static delegatesFocus = true;
/**
* The data entry property used in this example
*/
@api barCode = "";
/**
* The column definition as defined within the underlying grid component
* Set from data in params in the connectedCallback.
*/
_columnDefinition;
/**
* Contains properties that represent each field on the GiftEntry record/row within the grid.
* Set from data in params in the connectedCallback.
*/
_rowData = {};
/**
* _params is the only inbound property passed to this component from Gift Entry Grid
* The properties retrieved in the example below are the primary ones required for grid functionality
*/
_params;
@api
get params() {
return this._params;
}
set params(value) {
this._params = value;
this._rowData = this._params?.data;
this._columnDefinition = this._params?.colDef;
const _headerLabel = this._columnDefinition?.headerName; // informational
// Useful mechanism to pull in the previously entered barcode as the default value
// Values in the `properties` object of the row are transient for the current session only. They persist until the page is closed/refreshed
if (this._rowData && this._rowData?.properties?.barCode) {
this.barCode = this._rowData?.properties?.barCode;
}
}
/**
* Recommended Method: Ensures the column width is set properly on load
*/
connectedCallback() {
this.template.host.style.setProperty('--min-width', this._params?.column?.actualWidth + 'px');
}
/**
* Example Method:
* Handle a change to the entered bar code in the field
* @param {*} event
*/
handleBarCodeChanged(event) {
event.stopPropagation();
try {
if (event.target?.value && event.target?.value !== "") {
this.sendDataBackToGrid(event.target.value);
} else if (event.target?.value === "") {
this.clearCurrentRowOfAllData();
}
} catch (e) {
this.dispatchEvent(
new ShowToastEvent({
title: 'Error',
message: 'An error occurred while querying the entered barcode: ' + (e.body?.message || e.message),
variant: 'error'
})
);
}
this.applyFocus();
}
/**
* Example Method:
* Call the apex controller with the entered bar code
* Parse the response and pass that data back to GiftEntryGrid using the Lightning Message Service channel
* @param {*} barCode
*/
async sendDataBackToGrid(barCode) {
// Call the Apex controller to query and retrieve data
const result = await getBarCodeScanData({ barcodeValue: barCode });
if (result && result !== null) {
// Convert the query result from the apex controller into an object of GiftEntry fields to apply to the row in the grid
const giftEntryFields = this.buildResponse(result);
// Build the "message" obhect to send back to Gift Entry Grid following the documented contract
const message = {
action: "ColumnEdit", // Required property
componentName: this.tagName, // Required property (use as is)
colId: this._columnDefinition.colId, // Required property (use as is)
details: { // Optional property - include if there is data to write to the row in Gift Entry
rowId: this.params?.data?.id, // Required (use as is)
rowIndex: this.params?.data?.rowIndex, // Required (use as is)
giftEntryFields, // Required - all GiftEntry fields to write to rowData
rowProperties: { // Optional - use this to persist values in memory in the grid for use elsewhere, such as the Display Component
barCode, // Example: Used to persist the value of the barcode on the row temporarily to retrieve elsewhere
barCodeValue: result.donorName // Example: This is used by GiftEntryGridColumnDisplay
},
matchingGiftTransactionId: result?.matchingGiftTransactionId, // Optional, to set a matching gift on the row
matchingGiftTransactionName: result?.matchingGiftTransactionName // Optional, to set a matching gift on the row
}
}
publish(messageContext, GiftEntryGridComponentAction, message);
}
}
/**
* Example Method:
* Clear out the entire row if the barCode is set to null
*/
clearCurrentRowOfAllData() {
const giftEntryFields = {
DonorId: null,
Donor: null,
GiftType: "Individual"
};
if (this.params && this.params.data) {
const keepKeys = ['id', 'Id', 'rowId', 'rowIndex', 'properties', 'GiftType'];
Object.keys(this.params.data).forEach(key => {
if (!keepKeys.includes(key)) {
giftEntryFields[key] = null;
}
});
}
const message = {
action: "ColumnEdit", // Required property
componentName: this.tagName, // Required property (use as is)
colId: this._columnDefinition.colId, // Required property (use as is)
details: { // Optional property - include if there is data to write to the row in Gift Entry
rowId: this.params?.data?.id, // Required (use as is)
rowIndex: this.params?.data?.rowIndex, // Required (use as is)
giftEntryFields,
rowProperties: {
barCode : null,
barCodeValue: null
}
}
};
publish(messageContext, GiftEntryGridComponentAction, message);
}
/**
* Example Method:
*
* Notes
* - Lookup fields such as Campaign and OutreachSourceCode should be represented as an object with "id" and "name" to allow them
* render properly in the grid. This does not apply to DonorId
* - Pick List fields, such as PaymentMethod, can a string representing the pick list API Name or an object as {apiName: x, value: y}
* - To take advantage of Gift Entry Grid's built in Donor logic that retrieves all donor fields, including custom mapped fields, the
* response object should include a property for DonorId and GiftType ("Individual" or "Organizational" only)
* - Any field on GiftEntry can be included in this resonse and it will populate fields on the grid
* @param {*} result from call to Apex with query results
* @returns an object that represents fields on the GiftEntry object to be updated on the GiftEntryGrid row
*/
buildResponse(result) {
const response = {
GiftType: result.giftType, // Required to use the built-in donor logic
DonorId: result.donorid, // Required to use the built-in donor logic
// Examples(s) Populate other fields on the row
GiftReceivedDate: new Date().toISOString().split('T')[0],
PaymentMethod: DEFAULT_PAYMENT_METHOD,
GiftAmount: DEFAULT_GIFT_AMOUNT,
CampaignId: result.campaign,
Campaign: {
id: result.campaign,
Name: result.campaignName
},
OutreachSourceCodeId: result.outreachSourceCode,
OutreachSourceCode: {
id: result.outreachSourceCode,
Name: result.outreachSourceCodeName
}
};
return response;
}
/**
* For keyboard accessibility, allows the lookup component to be focused on keyboard tabbing
*/
applyFocus() {
const _gridElement = this.template.querySelector('lightning-input');
return new Promise((resolve) => {
Promise.resolve().then(() => {
_gridElement.focus();
resolve();
});
});
}
/**
* Example Method
*/
handleError(event) {
event.stopPropagation();
}
}
// Required Properties for Column Components:
GiftEntryGridBarCodeColumnComponent.tagName = 'c-gift-entry-grid-bar-code-column-component';
GiftEntryGridBarCodeColumnComponent.isPopup = true;
データの照会と戻し
この Apex コードを使用して、データを照会し、ギフトエントリグリッドカスタムコンポーネントに返します。
GiftEntryGridBarCodeScannerLookupController.apex
/**
* Salesforce Fundraising - Gift Entry Grid Custom Component Example
*
* Controller class for handling barcode scan data lookup from a column component
*/
public with sharing class GiftEntryBarCodeScanLookupController {
/**
* Wrapper class to return the selected donor and any other info that would be applied to the grid row
*/
public class ReponseObject {
@AuraEnabled public String barCode { get; set; }
@AuraEnabled public String giftType { get; set; }
@AuraEnabled public String donorid { get; set; }
@AuraEnabled public String donorName { get; set; }
@AuraEnabled public String campaign { get; set; }
@AuraEnabled public String campaignName { get; set; }
@AuraEnabled public String giftDesignation { get; set; }
@AuraEnabled public String giftDesignationName { get; set; }
@AuraEnabled public String outreachSourceCode { get; set; }
@AuraEnabled public String outreachSourceCodeName { get; set; }
@AuraEnabled public String matchingGiftTransactionId { get; set; }
@AuraEnabled public String matchingGiftTransactionName { get; set; }
@AuraEnabled public Double amount { get; set; }
}
/**
* Retrieves barcode scan data based on the provided barcode value
* @param barcodeValue The barcode value to search for
* @return ReponseObject object with all fields or null if not found
*/
@AuraEnabled(cacheable=false)
public static ReponseObject getBarCodeScanData(String barcodeValue) {
try {
if (String.isBlank(barcodeValue)) {
return null;
}
// Query the BarCodeScanData__c object for the matching barcode
// Include related fields from Donor (Account), Campaign, and Outreach Source Code
List<BarCodeScanData__c> results = [
SELECT Id,
BarCode__c,
Donor__c,
Donor__r.Name,
Donor__r.IsPersonAccount,
Campaign__c,
Campaign__r.Name,
OutreachSourceCode__c,
OutreachSourceCode__r.Name
FROM BarCodeScanData__c
WHERE BarCode__c = :barcodeValue
LIMIT 1
];
if (results.isEmpty()) {
return null;
}
BarCodeScanData__c barCodeRecord = results[0];
ReponseObject response = new ReponseObject();
// a value for DonorId and GiftType required for the donor logic to work
response.donorid = barCodeRecord.Donor__c;
response.giftType = (barCodeRecord.Donor__r.IsPersonAccount ? 'Individual' : 'Organizational');
// Extra fields used within this example component
response.barCode = barCodeRecord.BarCode__c;
response.donorName = barCodeRecord.Donor__r?.Name;
response.campaign = barCodeRecord.Campaign__c;
response.campaignName = barCodeRecord.Campaign__r?.Name;
response.outreachSourceCode = barCodeRecord.OutreachSourceCode__c;
response.outreachSourceCodeName = barCodeRecord.OutreachSourceCode__r?.Name;
// Is there an unpaid gift transaction installment for a commitment for this donor?
// If so, populate the GiftTransactionId field to simulate selecting a matching gift
List<GiftTransaction> matchingGifts = [
SELECT Id, Name,
OriginalAmount, TransactionDueDate,
CampaignId, Campaign.Name,
OutreachSourceCodeId, OutreachSourceCode.Name,
GiftCommitmentId
FROM GiftTransaction
WHERE DonorId = :response.donorid
AND Status = 'Unpaid'
ORDER BY TransactionDueDate ASC
LIMIT 1
];
if (matchingGifts.size() > 0) {
response.matchingGiftTransactionId = matchingGifts[0].Id;
response.matchingGiftTransactionName = matchingGifts[0].Name;
response.amount = matchingGifts[0].OriginalAmount;
response.campaign = matchingGifts[0].CampaignId;
response.campaignName = matchingGifts[0].Campaign.Name;
response.outreachSourceCode = matchingGifts[0].OutreachSourceCodeId;
response.outreachSourceCodeName = matchingGifts[0].OutreachSourceCode.Name;
}
return response;
} catch (Exception e) {
// Log the error and throw a user-friendly exception
System.debug(LoggingLevel.ERROR, 'Error in getBarCodeScanData: ' + e.getMessage());
throw new AuraHandledException('Error retrieving barcode scan data: ' + e.getMessage());
}
}
}

