プログラマ38の日記

主にプログラムメモです。

Salesforce: Lightning Component上で取得できるユーザ情報

Apexコントローラをコールしてしまえば必要な情報は取得できますが、Apexコントローラをコールしないでも次の情報が取得できます。

 

var userinfo = $A.get("$SObjectType.CurrentUser");

var userId = userinfo.Id;
var userEmail = userinfo.Email;
var isChatterEnabled = userinfo.isChatterEnabled;

 

現時点(2020\5/1)では、上記のユーザID、メール、チャッター有効の3項目だけですが、プロファイル名、ロール名、氏名など取得できる項目が今後増えていくといいなと思います。

 

Salesforce: URLパラメータによるLEX新規作成画面の初期値セット

URLパラメータで新規作成画面の初期値がセットできるようになりました。

 

https://ドメイン/lightning/o/Account/new?recordTypeId=[レコードタイプID]&defaultFieldValues=Name=初期値1,Sic=初期値2,BillingState=初期値3

 

上記は取引先(Account)の例です。別のオブジェクトの場合、AccountではなくてそのオブジェクトのAPI名を指定します。

レコードタイプを指定する場合はrecordTypeIdに指定したいレコードタイプIDを指定します。指定しない場合デフォルト指定しているレコードタイプになります。

項目の初期値をセットするには、項目のAPIに初期値をセットします。
日付項目の形式は、2020-01-01 で、日付/時間項目の形式は、2020-01-01T00:00:00.000Z のデータローダおなじみの形式でした。そして、連動関係の連動項目には初期値は入りませんでした。

 

使い方や動きは「e.force:createRecord」イベントと同じです。(連動関係の連動項目に初期値が入らないところを含め)

 

Trailheadでは数式で使うということでしたが、コピー機能のような感じで使うのだと理解しています。

Salesforce: Lightningページ上に配置したコンポーネントでよく使う aura:hander

Lightningページで、Lightning Componentを組み込んだ際に、初期表示を行ったり、変更やレコードの登録のタイミングでLightning Componentの内容を更新するといった制御が必要です。

 

そんな時には aura:hander を使いますが、個人的によく使うのは次の3つです。

 

[Lightningページ上のLightning Componentでよく使うハンドラー]

  1. <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
  2. <aura:handler event="force:refreshView" action="{!c.doRefreshView}" />
  3. <aura:handler event="force:showToast" action="{!c.doShowToast}" />

 

1つ目の name="init" は、画面の初期表示用ハンドラーで、ヘルプ上 valueInit で記載されています。
2つ目の event="force:refreshView" は、例えば詳細画面で値を変更した時に、Lightning Componentを更新するといった場合にこのハンドラーを使います。画面のすべてのデータ登録/更新でforce:refreshViewのイベントを受信できるわけではないので注意が必要です。(どのタイミングでこのイベントを受信できるかは下記にまとめました)
3つ目の event="force:showToast" は、例えば、「○○が登録されました。」といったトースト表示のタイミングでLightning Componentを更新するといった場合にこのハンドラーを使います。こちらはトースト表示されると動くのでわかりやすいです。(こちらも、どのタイミングでこのイベントを受信できるかは下記にまとめました)

         イベントを受信できるタイミング

操作 name="init" event="force:refreshView" event="force:showToast" 備考
初期表示      
chatterの投稿       投稿のイベントは受信できなかった
関連レコード     標準の関連レコードコンポーネント
活動   標準の活動コンポーネント
所有者の変更    
レコードタイプの変更 name="init"を最後に受信する
編集画面から保存      
関連リストから登録      
関連リストから更新    ○  
関連リストから削除      

 

関連リストの登録、削除はevent="force:showToast"でしか受信できないので注意です。

Salesforce: 動的に作成したコンポーネントとの値のやりとり(cmp.getReference)について

「lightning:overlayLibrary」をはじめとして、動的に作成したコンポーネントと、それを表示する元のコンポーネントとで値のやり取りをしたい場合があります。

 

例えば、javascriptのconfirmでは、

var result = window.confirm('ボタンをクリックしてください');

のように、確認ダイアログのクリックしたボタンの結果でtrue/falseが返ってきます。

 

これを「lightning:overlayLibrary」で行う場合次のようになります。

  1. 元のコンポーネントの変数の参照を確認ダイアログに渡す
  2. 確認ダイアログで渡された変数の参照にどのボタンをクリックしたのかの結果をセットする
  3. 元のコンポーネント側で変数の値を取得する

ここで変数の参照を渡すというのが出てきますが次の書き方になります。
{ パラメータ : component.getReference("v.変数名") }

これはattributeの参照なので v.変数の参照 ですが、Contoroller のfunction の参照であれば c.functionの関数名 となります。

 

このあたりの制御は、次の記事の記載内容で使っています。

 

crmprogrammer38.hatenablog.com

 

 

 

Salesforce: lightning:picklistPathを使った2段階のステータス表示の実装

f:id:crmprogrammer38:20200426163432p:plain

前に上記のような2段階でのステータス表示について、lightning:picklistPathを使ったアイデアを書きました。
 

crmprogrammer38.hatenablog.com

 

そのアイデアの実装内容になります。

実装内容

  •  パスによる1段階目のステータスと、lightning:picklistPathによる2段階目のステータスを表示
  • 2段階目のステータスは1段階目のステータスの連動項目のみを表示
  • 2段階目のステータスのリスト値をクリックすると確認ダイアログを表示後、値を変更する。

オブジェクト項目

オブジェクト:サブのパス(SubPath__c)
選択リスト(制御):ステータスLv1(StatusLv1__c)
選択リスト(連動):ステータスLv2(StatusLv2__c)

選択リストの連動関係

ステータスLv1 ステータス1 ステータス2 ステータス3 ステータス4 ステータス5
ステータスLv2 ステータスA        
ステータスB        
ステータスC ステータスC      
ステータスD ステータスD      
ステータスE ステータスE ステータスE    
  ステータスF ステータスF ステータスF  
  ステータスG ステータスG ステータスG  
    ステータスH ステータスH  
    ステータスI ステータスI ステータスI
      ステータスJ ステータスJ
      ステータスK ステータスK

 

ステータスLv2の連動設定に対応する選択リスト項目

ステータスLv2_1(StatusLv2_1__c):ステータス1の連動項目
ステータスLv2_2(StatusLv2_2__c):ステータス2の連動項目
ステータスLv2_3(StatusLv2_3__c):ステータス3の連動項目
ステータスLv2_4(StatusLv2_4__c):ステータス4の連動項目
ステータスLv2_5(StatusLv2_5__c):ステータス5の連動項目

作成したトリガ

今回の実装では、ステータスLv2の連動設定に対応する選択リスト項目を用意したので、その項目は常にステータスLv2と同期するようにトリガを作成した。

trigger SubPathTrigger on SubPath__c (before insert , before update) {

    for(SubPath__c newrow : Trigger.New ){
        //常に同期する
        newrow.StatusLv2_1__c = newrow.StatusLv2__c;
        newrow.StatusLv2_2__c = newrow.StatusLv2__c;
        newrow.StatusLv2_3__c = newrow.StatusLv2__c;
        newrow.StatusLv2_4__c = newrow.StatusLv2__c;
        newrow.StatusLv2_5__c = newrow.StatusLv2__c;
    }
}

作成したクラス

lightning:picklistPathの表示で、どの選択リスト項目を表示するかの制御と lightning:picklistPath上で選択したリスト値を更新する処理を作成した。

public without sharing class SubPathPickList {
    
    //ステータスLv1の値に応じて表示する選択リストのフィールドを返す
    @AuraEnabled
    public static String getLv2PickFieldName(String salesforceId){    
        
        List rows = [select Id, StatusLv1__c From SubPath__c where Id =:salesforceId ];
        
        Map<String, String> fieldMapForLv1 = new Map<String,String>();
        fieldMapForLv1.put('ステータス1','StatusLv2_1__c');
        fieldMapForLv1.put('ステータス2','StatusLv2_2__c');
        fieldMapForLv1.put('ステータス3','StatusLv2_3__c');
        fieldMapForLv1.put('ステータス4','StatusLv2_4__c');
        fieldMapForLv1.put('ステータス5','StatusLv2_5__c');
        
        String fieldName = 'StatusLv2_1__c';
        
        if( rows.isEmpty() == false){
            if( fieldMapForLv1.get(rows[0].StatusLv1__c) != null ){
                fieldName = fieldMapForLv1.get(rows[0].StatusLv1__c);
            }
        }
        
        return fieldName;
    }
    
    //ステータスLv2の値を更新
    @AuraEnabled
    public static Boolean setLv2PickFieldValue(String salesforceId, String pickValue){    
        
        List rows = [select Id From SubPath__c where Id =:salesforceId ];
        
        if( rows.isEmpty() == true){
            return false;
        }
        
        rows[0].StatusLv2__c =pickValue ;
        
        try{
            update rows;
            return true;
        } catch( Exception ex){
            return false;
        }
    }
}

作成したLightning Component

モーダル確認ダイアログ用にbodyとfooterを用意

[ConfirmDialogBody] Component
<aura:component>
  <aura:attribute name="messageText" type="String"></aura:attribute>
<div class="slds-align_absolute-center">{!v.messageText}</div>

</aura:component>

[ConfirmDialogFooter] Component
<aura:component>
     <aura:attribute name="modalResult" type="String"></aura:attribute>
    <lightning:overlaylibrary aura:id="overlayLib"></lightning:overlaylibrary>
  <lightning:button variant="neutral" label="キャンセル" onclick="{! c.handleCancel }"></lightning:button>
    <lightning:button variant="brand" label="OK" onclick="{! c.handleOK }"></lightning:button>
</aura:component>

[ConfirmDialogFooter] Component
({
  handleOK: function(cmp, event, helper) {
    cmp.set("v.modalResult", "OK");
    cmp.find("overlayLib").notifyClose();
  },
  handleCancel: function(cmp, event, helper) {
    cmp.set("v.modalResult", "Cancel");
    cmp.find("overlayLib").notifyClose();
  }
});    

Lightningページ上に表示するLightning Component


[SubPath] Component
<aura:component
  controller="SubPathPickList"
  implements="flexipage:availableForAllPageTypes,force:hasRecordId"
  access="global"
>
  <lightning:overlayLibrary aura:id="overlayLib" />
  <aura:attribute name="recordId" type="String" />
  <aura:attribute name="modalResult" type="String" />

  <aura:attribute name="pickField" type="String" default="StatusLv2_1__c" />

  <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
  <aura:handler event="force:refreshView" action="{!c.doInit}" />

  <lightning:picklistPath
    aura:id="picklistPath"
    recordId="{!v.recordId}"
    variant="non-linear"
    picklistFieldApiName="{!v.pickField}"
    onselect="{!c.handleSelect}"
  ></lightning:picklistPath>
</aura:component>

[SubPath] Controller
({
  doInit: function(component, event, helper) {
    var recordId = component.get("v.recordId");
    var fetchEventAction = component.get("c.getLv2PickFieldName");
    fetchEventAction.setParams({ salesforceId: recordId });
    fetchEventAction.setCallback(this, function(response) {
      var fieldName = response.getReturnValue();
      component.set("v.pickField", fieldName);
    });

    $A.enqueueAction(fetchEventAction);
  },

  handleSelect: function(component, event, helper) {
    var recordId = component.get("v.recordId");
    var stepName = event.getParam("detail").value;

    $A.createComponents(
      [
        [
          "c:ConfirmDialogBody",
          { messageText: "ステータスLv2を" + stepName + "に更新しますか?" }
        ],
        [
          "c:ConfirmDialogFooter",
          { modalResult: component.getReference("v.modalResult") }
        ]
      ],
      function(components, status) {
        if (status == "SUCCESS") {
          component.find("overlayLib").showCustomModal({
            header: "ステータスLv2を更新",
            body: components[0],
            footer: components[1],
            showCloseButton: true,
            closeCallback: function() {
              if (component.get("v.modalResult") == "OK") {
                var updateAction = component.get("c.setLv2PickFieldValue");
                updateAction.setParams({
                  salesforceId: recordId,
                  pickValue: stepName
                });
                updateAction.setCallback(this, function(response) {
                  if (
                    response.getState() == "SUCCESS" &&
                    response.getReturnValue()
                  ) {
                    $A.get("e.force:refreshView").fire();

                    //refreshViewと同じタイミングでtoastの再描画を防ぐ
                    window.setTimeout(
                      $A.getCallback(function() {
                        var toastEvent = $A.get("e.force:showToast");
                        toastEvent.setParams({
                          message: stepName + "に更新しました。",
                          type: "success"
                        });
                        toastEvent.fire();
                      }),
                      1000
                    );
                  } else {
                    var toastEvent = $A.get("e.force:showToast");
                    toastEvent.setParams({
                      message: stepName + "の更新に失敗しました。",
                      type: "error"
                    });
                    toastEvent.fire();
                  }
                });

                $A.enqueueAction(updateAction);
              }
            }
          });
        }
      }
    );
  }
});

最後に

lightning:picklistPathの「onselect」アクションでは、選択された選択リスト値のAPI参照名が取得できます。表示ラベルは取得できないので表示ラベルを使いたい場合は、Apexで取得してくるなどの処理が必要になります。

 

Salesforce: lightning:picklistPath について調べたこと

lightning:picklistPath について調べて考えたことのメモです。

 

f:id:crmprogrammer38:20200424092312p:plain

上記のように、lightning:picklistPathは、いわゆるパスをLightning Componentタグで書ける機能で、次の特徴があります。

  1. パスのように表示する選択リストの項目を変数で指定できる
  2. パスの更新ボタンはない
  3. 連動選択リストの連動関係の連動項目(制御される側)でも表示される選択リスト値は連動関係の設定は関係ない
  4. レコードタイプ毎で使える選択リスト値を設定した場合は、そのレコードタイプで表示可能な選択リスト値が表示される

 

最初調べた時は、連動関係を使って2段階のステータスで制御している時にパスを2つ表示できたらいいなという期待をもって調べたのですが、3の連動関係に関係なく選択リストが表示されるのでうーんとなってました。

やりたかったのはこんな感じで、下のパスは連動項目が表示されるイメージです。

f:id:crmprogrammer38:20200424092612p:plain

 

タグをかけばこれが実現できたら便利なのにと思ってましたが、次を実装することで対処できるのでアイデアを残しておこうと思います。(実現するために色々な設定をしていてメンテナンス性は度外視しています)

  1. 連動関係の連動項目を、制御される制御項目毎に別項目として定義する、この項目はレイアウトに表示しない
  2. lightning:picklistPathを使い、制御項目の値に応じて該当する選択リスト項目を表示する
  3. lightning:picklistPathでは更新ボタンがないので、lightning:picklistPath上で選択リストが選択されたらそのタイミングで更新する処理を実装する。(onselectで、確認メッセージ表示と、選択リスト値の更新を行う。選択リスト値を更新する際に、別項目として定義した選択リスト値と、元になる連動関係の連動項目の両方を更新する)
  4. パス上ではなく、連動項目側を変更されたら、連動項目に該当する選択リスト項目も同期するようにトリガを実装する

上記で、2段階のパスが実装できます。

実装の詳細は次で記載します。

 

crmprogrammer38.hatenablog.com

 

Salesforce: 開発中の「セッションの設定」メモ

開発組織でのセッションの設定メモです。

 設定>セキュリティ>セッションの設定から次を設定します。

 

1.「ユーザとしてログインしてから再ログインを強制する」のチェックを外す

f:id:crmprogrammer38:20200422090937p:plain

チェックを外すことで、代理ログイン(内部ユーザではユーザのログインボタン、外部ユーザでは取引先責任者からのログイン)してログアウト後に、代理ログイン前のユーザのログイン状態に戻ります。
[内部ユーザ]

f:id:crmprogrammer38:20200422091405p:plain
[外部ユーザ]
f:id:crmprogrammer38:20200422091241p:plain

 

 チェックをつけると、代理ログインしてログアウトすると、代理ログイン前のユーザからもログアウトされてしまいます。

代理ログインを使ってテストを実施する時など、代理ログインするたびにログインをするのを避けられるためチェックを外します。



2.キャッシュをオフにする。

f:id:crmprogrammer38:20200422092309p:plain

上の、「パフォーマンスを向上させるためにブラウザの安全で永続的なキャッシュを有効にする」と「Lightning コンポーネントフレームワークコンテンツ配信ネットワーク (CDN) を有効化」のチェックを外します。

チェックをつけたままだと、LightningComponentの画面(COMPONENT)の修正や、画面レイアウト、Lightningページなどの修正の反映に多少時間がかかってしまいます。

設定を変更後にすぐ確認を行うためチェックを外します。