tyoshikawa1106のブログ

- Force.com Developer Blog -

SFDC:Lightning地図コンポーネントの作成を試してみました

Lightningコンポーネントの開発でGoogleマップを呼び出したいときのためにlightning:mapタグが用意されています。

f:id:tyoshikawa1106:20190127122140p:plain

Lightning 地図コンポーネントと Apex の継承された共有の使用 単元 | Salesforce Trailhead

Towerオブジェクト作成

カスタムオブジェクトで作成。取引先と主従関係で紐付く。緯度経度の情報を登録するカスタム項目を用意。
f:id:tyoshikawa1106:20190127123207p:plain

テストデータを作成

取引先に2つのテストデータを作成。
f:id:tyoshikawa1106:20190127123307p:plain


取引先に紐付く形で、Towerオブジェクトのレコードを作成。
f:id:tyoshikawa1106:20190127123354p:plain

Apexクラス作成

TowerMapUtilClass
public class TowerMapUtilClass {
     public static List<sObject> queryObjects(String theObject, List<String> theFields, String theFilter, String sortField, String sortOrder) {
          String theQuery = 'SELECT ' + string.join(theFields, ',');
          theQuery += ' FROM ' + theObject;
          if(!String.isEmpty(theFilter)) {
               theQuery += ' WHERE ' + theFilter;
          }
          if(!String.isEmpty(sortField)) {
               theQuery += ' ORDER BY ' + sortField;
               if(!String.isEmpty(sortOrder)) {
                    theQuery += ' ' + sortOrder;
               }
          }
          return database.query(theQuery);
     }
}
TowerMapControllerClass
public class TowerMapControllerClass {
     @AuraEnabled
     public static List<Tower__c> getAllTowers() {
          String theObject = 'Tower__c';
          List<String> theFields = new List<String>{'Id', 'Name', 'State__r.Name', 'Tower_Location__Latitude__s', 'Tower_Location__Longitude__s'};
          String theFilter = '';
          String sortField = 'Name';
          String sortOrder = 'ASC';
          List<Tower__c> allTowers = TowerMapUtilClass.queryObjects(theObject, theFields, theFilter, sortField, sortOrder);
          return allTowers;
     }
}

Lightningコンポーネント作成

Towermap
<aura:component implements="flexipage:availableForAllPageTypes" controller="TowerMapControllerClass" access="global" >
     <aura:attribute name="mapMarkers" type="Object" access="PRIVATE" />
     <aura:attribute name="markersTitle" type="String" access="PRIVATE" />
     <aura:handler name="init" value="{!this}" action="{!c.handleInit}"/>
     <aura:if isTrue="{!!empty(v.mapMarkers)}" >
          <!-- Create lightning:map here -->
     </aura:if>
</aura:component>
コントローラ
({
     handleInit: function (component, event, helper) {
          helper.initHelper(component, event, helper);
     }
})
ヘルパー
({
     initHelper : function(component, event, helper) {
          helper.utilSetMarkers(component, event, helper);
     },
     utilSetMarkers : function(component, event, helper) {
          let action = component.get("c.getAllTowers");
          action.setCallback(this, function(response) {
               const data = response.getReturnValue();
               const dataSize = data.length;
               let markers = [];
               for(let i=0; i < dataSize; i += 1) {
                    const Tower = data[i];
                    markers.push({
                        'location': {
                             'Latitude' : Tower.Tower_Location__Latitude__s,
                             'Longitude' : Tower.Tower_Location__Longitude__s
                        },
                        'icon': 'utility:Tower',
                        'title' : Tower.Name,
                        'description' : Tower.Name + ' Tower Location at ' + Tower.State__r.Name
                   });
               }
               component.set('v.markersTitle', 'Out and About Communications Tower Locations');
               component.set('v.mapMarkers', markers);
          });
          $A.enqueueAction(action);
     }
})

lightning:mapタグ埋め込み

<!-- Create lightning:map here -->
<lightning:map mapMarkers="{! v.mapMarkers }" markersTitle="{!v.markersTitle}" zoomLevel="5" />


あとはLighntingアプリケーションビルダーでLightningページを作成してLightningコンポーネントを追加すればGoogleマップでTowerの位置情報を表示できます。
f:id:tyoshikawa1106:20190127125458p:plain

f:id:tyoshikawa1106:20190127130230p:plain

SFDC:apex:iframeタグと$IFrameResourceの使い方

Visualforceページでirameを埋め込むときにはapex:irameタグが用意されていますが、iframe を使用した信頼されないサードパーティコンテンツの参照したいときのために$IFrameResourceという呼び出し方が用意されているみたいです。
f:id:tyoshikawa1106:20190127121754p:plain

https://developer.salesforce.com/docs/atlas.ja-jp.pages.meta/pages/pages_resources_iframe.htm


使い方の例はこちら。

<apex:iframe src="{!$IFrameResource.TestHtml}" id ="theiframe" width="500" height="500"/>

SFDC:Winter'19で新規および変更されたApex処理

メソッドが新規追加された既存の Apex クラス

Schema.DescribeFieldResult クラス

isAiPredictionField()

Einstein 予測データを表示するように項目が有効化されているかどうかを示す Boolean を返します。

Schema.DescribeFieldResult F = Opportunity.StageName.getDescribe();
System.debug(F.isAiPredictionField());

Schema.DescribeSObjectResult クラス

getRecordTypeInfosByDeveloperName()

関連付けられたレコードタイプに API 参照名を照合する対応付けを返します。

Schema.DescribeSObjectResult d = Account.sObjectType.getDescribe();
System.debug(d.getRecordTypeInfosByDeveloperName());

Schema.RecordTypeInfo クラス

getDeveloperName()

このレコードタイプの API 参照名を返します。

RecordType rt = [SELECT Id,Name FROM RecordType WHERE SobjectType='Account' LIMIT 1];
Schema.DescribeSObjectResult d = Schema.SObjectType.Account; 
Map<Id,Schema.RecordTypeInfo> rtMapById = d.getRecordTypeInfosById();

Schema.RecordTypeInfo rtById =  rtMapById.get(rt.id);
System.debug(rtById.getDeveloperName());

System.Crypto クラス

verify(String algorithmName, Blob data, Blob signature, Blob publicKey)

指定アルゴリズムと供給された公開鍵を使用して、暗号化されたデータのデジタル署名を確認する Boolean を返します。

verify(String algorithmName, Blob data, Blob signature, String certDevName)

指定アルゴリズムと certDevName に関連付けられた公開鍵を使用して、データのデジタル署名を確認する Boolean を返します。

verifyMac(String algorithmName, Blob input, Blob privateKey, Blob macToVerify)

指定アルゴリズム、入力データ、非公開鍵、MAC アドレスを使用して、データの HMAC 署名を確認する Boolean を返します。

System.Url クラス

getOrgDomainUrl()

組織の正規 URL を返します。

System.debug(System.Url.getOrgDomainUrl());

メソッドが変更された既存の Apex クラス

System.UserInfo クラス

getSessionId()

非同期 Apex (一括処理、future、Queueable、またはスケジュール済み Apex) で getSessionId() を使用できるようになりました。以前は、このメソッドは非同期実行時に null を返していました。

非同期 Apex では、このメソッドはコードが有効なユーザによって実行されている場合にのみセッション ID を返します。コードが内部ユーザ (自動化プロセスユーザやプロキシユーザなど) によって実行されている場合、このメソッドは null を返します。

変更された Apex Enum

Winter '19 リリースでは、次の列挙が更新されています。

Auth.Auth.VerificationMethodEnum Enum

ユーザがログイン時に自分自身を識別できるさまざまな方法が含まれます。モバイルで使いやすいパスワードなしのログインページの実装や、検証方法のセルフ登録 (および登録解除) のために使用できます。

この Enum に新しい値 Password が追加されました。パスワードで ID を確認できます。

Metadata.PlatformActionListContextEnum Enum

アクションリストの各種コンテキストを説明します。

この Enum に新しい値 ActionDefinition が追加されました。この値は、将来の使用のために予約されています。

新規 Apex 例外

Winter '19 リリースでは、次の例外が導入されています。

Auth 名前空間

LoginDiscoveryException

例外を発生させて、ログイン検出ハンドラを実行しているときにエラーが発生したことを示します。

DiscoveryCustomErrorException

ログイン、検証、およびセルフ登録ページに表示するエラーメッセージをカスタマイズするための例外を発生させます。

新規 Apex インターフェース

Winter '19 リリースでは、次の新規インターフェースが導入されています。

Auth.ConfigurableSelfRegHandler インターフェース

ユーザ名とパスワードの代わりにメールアドレスまたは電話番号でユーザを登録するには、Auth.ConfigurableSelfRegHandler を実装するクラスを作成します。設定可能なセルフ登録ページでコミュニティのセルフ登録を設定すると、Salesforce によってデフォルトの AutocreatedConfigSelfReg Apex クラスが生成されます。このクラスを修正して機能を拡張し、ユーザが作成される方法の変更などを行うことができます。

createUser(accountId, profileId, registrationAttributes, password)

コミュニティのセルフ登録ページで訪問者が提供した情報を使用して、コミュニティメンバーを作成します。

Auth.LoginDiscoveryHandler インターフェース

ユーザ名とパスワード以外の確認方法に基づいてユーザのログインを行うには、Auth.LoginDiscoveryHandler を実装するクラスを作成します。ユーザはメール、電話番号、または統合 ID やデバイス識別子などの他の識別子を使用して、自分自身を識別できます。ログイン検出ページでコミュニティのログインページを設定すると、Salesforce によってデフォルトの AutocreatedDiscLoginHandler が生成されます。このクラスを修正し、シングルサインオン (SSO) のサポートの追加などを行うことができます。

login(identifier, startUrl, requestAttributes)

メールや電話番号などの識別子が指定された外部ユーザのログインを行います。成功した場合、開始 URL で指定されたコミュニティページにユーザをリダイレクトします。

System.Callable インターフェース

開発者が共通インターフェースを使用して、異なるパッケージ内のコードでも Apex クラスまたはトリガ間の疎結合インテグレーションを作成できます。共通インターフェースについて合意することで、異なる会社や異なる部署の開発者が相互のソリューションに基づいてソリューションを作成できます。コミュニティの規模を拡大し、当初の予定とは異なるソリューションが必要になる場合、このインターフェースを実装してコードの機能を拡張します。

call(action, args)

他のクラスまたはパッケージで利用したり作成時の基盤としたりできる機能を提供します。

変更された Apex の SOAP API コール

runTests() コールが変更されました。

RunTestsResult オブジェクトに次の 2 つの新規項目が追加されました。

flowCoverage

フローを実行したテスト実行の結果の配列。

flowCoverageWarnings

フローを実行したテスト実行によって生成された警告の配列。

SFDC:Lightningアプリケーションビルダーとコンポーネントの表示設定

Lightningアプリケーションビルダーとコンポーネントの表示設定についてです。
f:id:tyoshikawa1106:20190126201504p:plain


アクセスするユーザの情報を条件にコンポーネントの表示・非表示をアプリケーションビルダー側の設定で制御できます。
f:id:tyoshikawa1106:20190126201801p:plain


条件指定はこんな感じ。(※アプリケーションページの場合)
f:id:tyoshikawa1106:20190126202026p:plain

f:id:tyoshikawa1106:20190126202046p:plain

f:id:tyoshikawa1106:20190126202100p:plain


クライアントを選択するとデバイスの種別も条件に指定できました。
f:id:tyoshikawa1106:20190126202204p:plain:w300


レコードページの場合は項目の値を条件指定できます。またレコードページの場合にはクライアントによる条件指定はできないみたいです。
f:id:tyoshikawa1106:20190126202521p:plain

f:id:tyoshikawa1106:20190126202547p:plain

SFDC:クイックテキストの設定を試してみました

クイックテキストはテキストエリアの項目などに定型文を差し込むことができる機能です。きまった文章を入力することが多い場合に活用できます。特定の値を差し込むこともできるみたいです。(メールテンプレートみたいなイメージ) この機能は設定で有効 / 無効を切り替えることができます。

f:id:tyoshikawa1106:20190121044029p:plain


設定ページではクイックテキストのボタンを非表示にできるオプションも用意されています。ユーザに表示したくない場合はこちらで切り替えができるみたいです。
f:id:tyoshikawa1106:20190121044213p:plain:w300


有効化は設定ページから行いますが、クイックテキストの定型文作成はアプリケーションランチャーから行います。
f:id:tyoshikawa1106:20190121044309p:plain


レコードを作成するときと同じように登録できました。
f:id:tyoshikawa1106:20190121044343p:plain


オブジェクトと項目を選択して文中に差し込みできます。
f:id:tyoshikawa1106:20190121044520p:plain


作成例としてはこんな感じ。
f:id:tyoshikawa1106:20190121044638p:plain


テキストエリア型の項目などにフォーカスがあったときにアイコンが表示されます。
f:id:tyoshikawa1106:20190121044726p:plain


クリックすると作成済みのクイックテキストが表示されます。
f:id:tyoshikawa1106:20190121044916p:plain


対象を選択すると本文に差し込まれます。差し込み項目の値をすぐにセットされた状態となりました。
f:id:tyoshikawa1106:20190121044858p:plain


以上がクイックテキストの使い方の流れです。問い合わせ対応や日報などの業務で活用できると思います。

おまけ

うっかり削除してしまったときはゴミ箱に残っているのでそこから復元できました。
f:id:tyoshikawa1106:20190121045313p:plain

SFDC:商談フェーズとレコードタイプの関係

商談はフェーズ管理を正しく整理、設定することでユーザが次に何をすればいいかを可視化することができます。

f:id:tyoshikawa1106:20190120191640p:plain


フェーズを設定するとフェーズごとに入力フォームのレイアウトを切り替えたくなるかもしれません。後半のフェーズに関する項目に値を入力するときに下にスクロールする手間が発生するためです。また、現在のフェーズで入力してほしい項目を上に持ってくることでユーザがどこで入力すればいいかで悩むことがなくなります。
f:id:tyoshikawa1106:20190120192125p:plain


上記要望を簡単に実現する方法としてフェーズごとにレコードタイプを用意する方法が思いつくかもしれません。確かに1フェーズ1レコードタイプとすることでそれぞれ専用のページレイアウトを割り当てることができます。ですがこの方法は長期的に運用していく上でトラブルの原因になると思います。


わかりやすい部分でパスの設定があります。パスは「1つのレコードタイプに1つのパス」という構成で成り立ちます。もしフェーズごとにレコードタイプを用意した場合、レコードタイプの数だけパスを用意する必要が出てくることになります。コピーや一括更新は行えないため、成功へのガイダンスのメッセージや表示項目を無駄に多く登録する必要が発生してメンテナンスしずらい組織となります。
f:id:tyoshikawa1106:20190120193534p:plain


このパス設定への影響だけでもフェーズごとにレコードタイプを用意するのはデメリットの方が大きいと思います。また、本来レコードタイプはデータの種別を分別するためのものです。例えば法人向けのビジネスの会社ということで下記のようにフェーズ設定を行ったとします。

法人向け商談
  • フェーズA
  • フェーズB
  • フェーズC
  • フェーズD
  • フェーズE


上記設定で運用を開始したあとに個人向けの商談管理が必要になった場合、通常であれば法人/個人の2つのレコードタイプを用意すれば済みましたが、フェーズごとにレコードタイプを用意しているとこうなります。

個人向け商談
  • フェーズF
  • フェーズG
  • フェーズH
  • フェーズI
  • フェーズJ


はじめ5個あったレコードタイプが、一気にレコードタイプが10個に増えました。レコードタイプが増えれば増えるほどページレイアウト設定やパス設定を行う際に作業する箇所が多くなるので、ちょっと修正でも無駄に作業時間が多く発生してしまいます。


レコードタイプは法人/個人など大きな括りで用意するようにして、フェーズごとに用意するというのは避けた方が良いと思います。

フェーズごとにレイアウトを分けたい時の対応方法

上記のとおりレコードタイプを用意して対応するのはデメリットの方が大きいです。そのため別の方法が必要になりますが、標準機能のみでの対応はおそらくムリだと思います。こういう場合はVisualforceページ(もしくはLightningコンポーネント)を開発して対応することで結果的にメンテナンス性を損なわずに実現できると思います。

SFDC:メールテンプレートでレコードURLを差し込む方法

レコード登録時に社内向けのメールアラートを作成するときにレコードURLを差し込みたい場合があると思います。そんなときのためにメールテンプレートの設定で差込項目を選択する際に「詳細リンク」という項目が用意されています。

f:id:tyoshikawa1106:20190120184625p:plain


詳細リンクをつかうことでレコードURLが組織に合わせて自動で生成されます。(本番環境で実行されたときは本番環境ドメインのレコードURLが生成され、Sandboxで実行された場合はSandboxドメインのレコードURLが生成されます。)
f:id:tyoshikawa1106:20190120184734p:plain


別の方法としては各オブジェクトごとにレコードURLを保持する数式項目があります。ドメイン部分はカスタム設定で保持することで本番、Sandboxでそれぞれ指定することが可能です。


数式項目でも一見問題なさそうですが、下記の問題が発生する可能性があります。

  1. Sandbox組織で設定完了
  2. 本番環境にリリース
  3. 本番環境でカスタム設定を指定し忘れる
  4. ドメイン部分が空白値となることでリンクURLにならない


上記問題の場合、一度設定してしまえばもう問題なさそうにも見えますが、Sandboxリフレッシュ時にカスタム設定の値がクリアされるため同じように問題が発生する可能性があります。


メールテンプレートでレコードURLを差し込みたいときは標準で用意された詳細リンクをつかうのが間違いなさそうです。