Microsoft Dynamics 365でWijmoのFlexGridを使用する

Wijmo(ウィジモ)」は業務アプリケーションの様々な要件に対応できるUI部品を多数備えたJavaScriptライブラリです。サーバー環境に依存せず、クライアントサイドで動作するので、様々なクラウドサービスとも連携が可能です。

今回の記事ではMicrosoft社が提供する、CRMとERPが統合されたクラウド型のビジネスアプリケーション「Microsoft Dynamics 365」の中から営業支援に特化した「Dynamics 365 Sales」とWijmoの高機能なデータグリッドコントロール「FlexGrid(フレックスグリッド)」とデータ管理クラス「CollectionView(コレクションビュー)」を連携させ、リッチなUIでCRUDなどの各種処理を行う方法をご紹介します。

今回使用する環境と作成するサンプルの概要

今回はDynamics 365 Salesの試用版の環境を使用してカスタマイズを試してみたいと思います。

Dynamics Sales試用版

今回作成するサンプルでは、クライアントサイドWebアプリケーションにWijmoを組みこみ、DynamicsのAPIを介してCRUD処理を行います。そして作成したアプリケーションはDynamicsの「Webリソース」としてホストします。また、Dynamicsの既存のグリッドニューのコマンドバーにカスタムのコマンドを追加し、シームレスにアクセスできるようにしてみます。

サンプル概要

クライアントサイドWebアプリケーションの作成

まずはDynamicsのAPIとデータ連携を行うクライアントサイドWebアプリケーションを作成します。今回は「flexgrid.html」という名前で以下のようなHTMLを用意し、Dynamicsのリードのデータを編集するようなアプリを作成します。

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <title>Wijmoデモ</title>

    <!-- Wijmoのスタイル(必須) -->
    <link href="https://cdn.grapecity.com/wijmo/5.20222.877/styles/wijmo.min.css" rel="stylesheet" />

    <!-- Wijmoのコアモジュール(必須) -->
    <script src="https://cdn.grapecity.com/wijmo/5.20222.877/controls/wijmo.min.js"></script>
    <!-- Wijmoコントロール(オプション。必要なコントロールを設定する) -->
    <script src="https://cdn.grapecity.com/wijmo/5.20222.877/controls/wijmo.grid.min.js"></script>
    <script src="https://cdn.grapecity.com/wijmo/5.20222.877/controls/wijmo.grid.filter.min.js"></script>
    <script src="https://cdn.grapecity.com/wijmo/5.20222.877/controls/wijmo.grid.grouppanel.min.js"></script>
    <script src="https://cdn.grapecity.com/wijmo/5.20222.877/controls/wijmo.input.min.js"></script>
    <!-- Wijmoカスタムカルチャー(オプション。任意のカルチャーを設定する) -->
    <script src="https://cdn.grapecity.com/wijmo/5.20222.877/controls/cultures/wijmo.culture.ja.min.js"></script>
</head>

<body>
    <button id="update">更新</button>
    <div id="Pager"></div>
    <div id="GroupPanel"></div>
    <div id="Wijmo_FlexGrid" style="height: 600px;"></div>
</body>

<script>
    wijmo.setLicenseKey('Wijmoの配布ライセンスのキーを設定');
    let url = 'https://**********.dynamics.com/api/data/v9.2/leads';
    let cv = new wijmo.collections.CollectionView();
    cv.trackChanges = true;

    // データマップの作成
    let leadsources = [
        { id: 1, name: '広告' },
        { id: 2, name: '社員紹介' },
        { id: 3, name: '外部紹介' },
        { id: 4, name: 'パートナー' },
        { id: 5, name: '広報活動' },
        { id: 6, name: 'セミナー' },
        { id: 7, name: '見本市' },
        { id: 8, name: 'Web' },
        { id: 9, name: '口コミ' },
        { id: 10, name: 'その他' },
    ];
    let status = [
        { id: 1, name: '新規' },
        { id: 2, name: '連絡済み' }
    ];
    let state = [
        { id: 0, name: 'オープン' },
        { id: 1, name: '見込みあり' },
        { id: 2, name: '見込みなし' }
    ];
    let leadquality = [
        { id: 1, name: '高' },
        { id: 2, name: '中' },
        { id: 3, name: '低' }
    ];

    var LeadSourceMap = new wijmo.grid.DataMap(leadsources, 'id', 'name');
    var StatusMap = new wijmo.grid.DataMap(status, 'id', 'name');
    var StateMap = new wijmo.grid.DataMap(state, 'id', 'name');
    var LeadQualityMap = new wijmo.grid.DataMap(leadquality, 'id', 'name');

    wijmo.httpRequest(url, {
        data: {
            '$select': 'companyname,jobtitle,lastname,firstname,address1_stateorprovince,address1_city,address1_line1,emailaddress1,telephone1,mobilephone,subject,statecode,statuscode,leadsourcecode,leadqualitycode,createdon,modifiedon'
        },
        success: (xhr) => {
            cv.sourceCollection = JSON.parse(xhr.responseText).value;

            // ページのナビゲート処理
            cv.pageSize = 10;
            new wijmo.input.CollectionViewNavigator('#Pager', {
                byPage: true,
                headerFormat: '{currentPage:n0} / {pageCount:n0} ページ',
                cv: cv
            });       

            let flexGrid = new wijmo.grid.FlexGrid('#Wijmo_FlexGrid', {
                itemsSource: cv,
                columns: [
                    { binding: 'companyname', header: '会社', width: 400 },
                    { binding: 'jobtitle', header: '役職', width: 180 },
                    { binding: 'lastname', header: '名前(姓)', width: 130 },
                    { binding: 'firstname', header: '名前(名)', width: 130 },
                    { binding: 'address1_stateorprovince', header: '都道府県', width: 130 },
                    { binding: 'address1_city', header: '市区町村', width: 200 },
                    { binding: 'address1_line1', header: '町大字通称名', width: 200 },
                    { binding: 'emailaddress1', header: '電子メール', width: 200 },
                    { binding: 'telephone1', header: '電話番号', width: 150 },
                    { binding: 'mobilephone', header: '携帯電話', width: 150 },
                    { binding: 'subject', header: 'トピック', width: 500 },
                    { binding: 'statecode', header: '状態', width: 120, dataMap: StateMap },
                    { binding: 'statuscode', header: 'ステータス', width: 120, dataMap: StatusMap },
                    { binding: 'leadsourcecode', header: 'リードソース', width: 120, dataMap: LeadSourceMap },
                    { binding: 'leadqualitycode', header: '評価', width: 80, dataMap: LeadQualityMap },
                    { binding: 'createdon', header: '作成日', width: 200 },
                    { binding: 'modifiedon', header: '更新日', width: 200 }
                ],

                allowAddNew: true,
                allowDelete: true,
            });

            // フィルタを設定
            let filter = new wijmo.grid.filter.FlexGridFilter(flexGrid);

            // グループパネルを設定
            let theGroupPanel = new wijmo.grid.grouppanel.GroupPanel('#GroupPanel', {
                placeholder: 'ここに列をドラッグするとグループを作成します',
                grid: flexGrid
            });
            
        }
    });

    document.getElementById('update').addEventListener('click', function () {
        //POST
        for (var i = 0; i < cv.itemsAdded.length; i++) {
            wijmo.httpRequest(url, {
                method: 'POST',
                data: cv.itemsAdded[i]
            });
            if (i == cv.itemsAdded.length - 1) {
                alert(cv.itemsAdded.length + "件のデータを登録しました。")
            }
        }

        //PATCH  
        for (var i = 0; i < cv.itemsEdited.length; i++) {
            wijmo.httpRequest(url + '(' + cv.itemsEdited[i].leadid + ')', {
                method: 'PATCH',
                data: cv.itemsEdited[i]
            });
            if (i == cv.itemsEdited.length - 1) {
                alert(cv.itemsEdited.length + "件のデータを更新しました。")
            }
        }

        //DELETE
        for (var i = 0; i < cv.itemsRemoved.length; i++) {
            wijmo.httpRequest(url + '(' + cv.itemsRemoved[i].leadid + ')', {
                method: 'DELETE'
            });
            if (i == cv.itemsRemoved.length - 1) {
                alert(cv.itemsRemoved.length + "件のデータを削除しました。")
            }
        }
    });
</script>

</html>

以下でコードの詳細を解説していきます。

Wijmoの参照設定

まずは使用するWijmoのライブラリやスタイルシートの参照設定を行います。全てCDNを参照することでDynamicsにホストするWebリソースの数を削減します。

今回は単純なグリッドでの表示のほか、ページングやフィルタ、グループパネルも使用するため、対応したライブラリの参照を追加しています。

・・・(中略)・・・
<head>
    <meta charset="utf-8" />
    <title>Wijmoデモ</title>

    <!-- Wijmoのスタイル(必須) -->
    <link href="https://cdn.grapecity.com/wijmo/5.20222.877/styles/wijmo.min.css" rel="stylesheet" />

    <!-- Wijmoのコアモジュール(必須) -->
    <script src="https://cdn.grapecity.com/wijmo/5.20222.877/controls/wijmo.min.js"></script>
    <!-- Wijmoコントロール(オプション。必要なコントロールを設定する) -->
    <script src="https://cdn.grapecity.com/wijmo/5.20222.877/controls/wijmo.grid.min.js"></script>
    <script src="https://cdn.grapecity.com/wijmo/5.20222.877/controls/wijmo.grid.filter.min.js"></script>
    <script src="https://cdn.grapecity.com/wijmo/5.20222.877/controls/wijmo.grid.grouppanel.min.js"></script>
    <script src="https://cdn.grapecity.com/wijmo/5.20222.877/controls/wijmo.input.min.js"></script>
    <!-- Wijmoカスタムカルチャー(オプション。任意のカルチャーを設定する) -->
    <script src="https://cdn.grapecity.com/wijmo/5.20222.877/controls/cultures/wijmo.culture.ja.min.js"></script>
</head>
・・・(中略)・・・

body要素の定義

body要素でWijmoのFlexgridをホストするdiv要素を定義します。また、ページングを行うためのページャー、グループ化をインタラクティブに行うためのグループパネル、登録、更新、削除処理を行うための[更新]ボタンの要素もそれぞれ定義します。

・・・(中略)・・・
<body>
    <button id="update">更新</button>
    <div id="Pager"></div>
    <div id="GroupPanel"></div>
    <div id="Wijmo_FlexGrid" style="height: 600px;"></div>
</body>
・・・(中略)・・・

ライセンスキーの設定

今回はWijmoを組み込んだアプリをDynamicsの環境に配布しますので、配布ライセンスキーを設定します。
※ 設定しない場合はトライアル版のバナーが表示されます。

・・・(中略)・・・
<script>
    wijmo.setLicenseKey('Wijmoの配布ライセンスのキーを設定');
・・・(中略)・・・

データマップの定義

Dynamicsではステータスのようなデータを、「0、1、2」のようなコード値で管理していますが、実際に画面上に表示するときは「オープン、見込みあり、見込みなし」のような文字列を表示します。Wijmoではデータマップという機能を使い、コード値と文字列のマッピングを行うことでこのような要件も対応可能です。

・・・(中略)・・・
    // データマップの作成
    let leadsources = [
        { id: 1, name: '広告' },
        { id: 2, name: '社員紹介' },
        { id: 3, name: '外部紹介' },
        { id: 4, name: 'パートナー' },
        { id: 5, name: '広報活動' },
        { id: 6, name: 'セミナー' },
        { id: 7, name: '見本市' },
        { id: 8, name: 'Web' },
        { id: 9, name: '口コミ' },
        { id: 10, name: 'その他' },
    ];
    let status = [
        { id: 1, name: '新規' },
        { id: 2, name: '連絡済み' }
    ];
    let state = [
        { id: 0, name: 'オープン' },
        { id: 1, name: '見込みあり' },
        { id: 2, name: '見込みなし' }
    ];
    let leadquality = [
        { id: 1, name: '高' },
        { id: 2, name: '中' },
        { id: 3, name: '低' }
    ];
・・・(中略)・・・

FlexGridの初期化処理

次にWijmoのhttpRequestメソッドを使用してDynamicsのAPIをコールしてリードのデータを取得し、FlexGridの初期化処理とあわせてデータバインドを行います。その際、先ほど定義したデータマップも一緒に設定します。

また、このタイミングでページングやフィルタ、グループパネルの設定も行っています。

さらにWijmoのデータ管理クラスであるCollectionViewという機能を使用して、1ページに表示するデータ数や、この後設定する登録、更新、削除に必要なデータの変更箇所の追跡を行います。データの追跡については以下の記事もご参考ください。

・・・(中略)・・・
    let url = 'https://**********.dynamics.com/api/data/v9.2/leads';
    let cv = new wijmo.collections.CollectionView();
    cv.trackChanges = true;
・・・(中略)・・・
    wijmo.httpRequest(url, {
        data: {
            '$select': 'companyname,jobtitle,lastname,firstname,address1_stateorprovince,address1_city,address1_line1,emailaddress1,telephone1,mobilephone,subject,statecode,statuscode,leadsourcecode,leadqualitycode,createdon,modifiedon'
        },
        success: (xhr) => {
            cv.sourceCollection = JSON.parse(xhr.responseText).value;

            // ページのナビゲート処理
            cv.pageSize = 10;
            new wijmo.input.CollectionViewNavigator('#Pager', {
                byPage: true,
                headerFormat: '{currentPage:n0} / {pageCount:n0} ページ',
                cv: cv
            });       

            let flexGrid = new wijmo.grid.FlexGrid('#Wijmo_FlexGrid', {
                itemsSource: cv,
                columns: [
                    { binding: 'companyname', header: '会社', width: 400 },
                    { binding: 'jobtitle', header: '役職', width: 180 },
                    { binding: 'lastname', header: '名前(姓)', width: 130 },
                    { binding: 'firstname', header: '名前(名)', width: 130 },
                    { binding: 'address1_stateorprovince', header: '都道府県', width: 130 },
                    { binding: 'address1_city', header: '市区町村', width: 200 },
                    { binding: 'address1_line1', header: '町大字通称名', width: 200 },
                    { binding: 'emailaddress1', header: '電子メール', width: 200 },
                    { binding: 'telephone1', header: '電話番号', width: 150 },
                    { binding: 'mobilephone', header: '携帯電話', width: 150 },
                    { binding: 'subject', header: 'トピック', width: 500 },
                    { binding: 'statecode', header: '状態', width: 120, dataMap: StateMap },
                    { binding: 'statuscode', header: 'ステータス', width: 120, dataMap: StatusMap },
                    { binding: 'leadsourcecode', header: 'リードソース', width: 120, dataMap: LeadSourceMap },
                    { binding: 'leadqualitycode', header: '評価', width: 80, dataMap: LeadQualityMap },
                    { binding: 'createdon', header: '作成日', width: 200 },
                    { binding: 'modifiedon', header: '更新日', width: 200 }
                ],

                allowAddNew: true,
                allowDelete: true,
            });

            // フィルタを設定
            let filter = new wijmo.grid.filter.FlexGridFilter(flexGrid);

            // グループパネルを設定
            let theGroupPanel = new wijmo.grid.grouppanel.GroupPanel('#GroupPanel', {
                placeholder: 'ここに列をドラッグするとグループを作成します',
                grid: flexGrid
            });
            
        }
    });
・・・(中略)・・・

登録、更新、削除処理の設定

最後に画面上の[更新]ボタンをクリックしたときに、CollectionViewで追跡していたグリッド上の変更箇所のデータをもとに、DynamicsのAPIに登録、更新、削除のリクエストを送信し、グリッド上の変更をDynamicsのデータに反映します。

・・・(中略)・・・
    document.getElementById('update').addEventListener('click', function () {
        //POST
        for (var i = 0; i < cv.itemsAdded.length; i++) {
            wijmo.httpRequest(url, {
                method: 'POST',
                data: cv.itemsAdded[i]
            });
            if (i == cv.itemsAdded.length - 1) {
                alert(cv.itemsAdded.length + "件のデータを登録しました。")
            }
        }

        //PATCH  
        for (var i = 0; i < cv.itemsEdited.length; i++) {
            wijmo.httpRequest(url + '(' + cv.itemsEdited[i].leadid + ')', {
                method: 'PATCH',
                data: cv.itemsEdited[i]
            });
            if (i == cv.itemsEdited.length - 1) {
                alert(cv.itemsEdited.length + "件のデータを更新しました。")
            }
        }

        //DELETE
        for (var i = 0; i < cv.itemsRemoved.length; i++) {
            wijmo.httpRequest(url + '(' + cv.itemsRemoved[i].leadid + ')', {
                method: 'DELETE'
            });
            if (i == cv.itemsRemoved.length - 1) {
                alert(cv.itemsRemoved.length + "件のデータを削除しました。")
            }
        }
    });
</script>

Webリソースの登録

作成したWebアプリケーションをDynamicsのWebリソースとして登録します。

Dynamics Salesの画面右上のメニューから「詳細設定」をクリックします。

詳細設定

「カスタマイズ」のメニューをクリックします。

カスタマイズ

カスタマイズメニューの一覧から「システムのカスタマイズ」をクリックします。

システムのカスタマイズ

Dynamicsの既定のソリューションのオブジェクトの一覧が表示されます。この画面からも登録は可能ですが、新しいデザイナが使えるので、画面上部の[新しいエクスペリエンスを試す]のボタンをクリックします。

新しいエクスペリエンス

Power Appsのアプリデザイナーでオブジェクトの一覧が表示されます。

アプリデザイナー

「新規」⇒「その他」⇒「Webリソース」を選択します。

Webリソースの追加

ダイアログから[ファイルを選択]ボタンをクリックし、先ほど作成した「flexgrid.html」をアップロードします。

ファイルを選択

「表示名」、「名前」にそれぞれ適当な名前を付けます。今回はそれぞれ「wijmo」と設定します。設定後[保存]ボタンをクリックし、Webリソースの登録を完了します。

表示名と名前の設定

登録したWebリソースが一覧に表示されるので、表示名の部分をクリックします。
※ 以下はフィルタを使用して表示しています。

登録したWebリソース

編集のダイアログが表示されるので、「高度なオプション」を開くと「URL」の項目で登録したWebリソースのURLを取得できます。

URLの確認

取得したURLにブラウザからアクセスすると、以下のようにDynamicsのリードのデータをWijmoのFlexGrid上に表示できました。

DynamicsのデータをFlexGridに表示

動作確認

以上でWebアプリケーションをDynamicsのWebリソースにホストして実行することができましたので、FlexGridの機能をそれぞれ確認してみます。

ページング

今回設定したFlexGridのページングはクライアントサイドで実行されるので、非常に高速にページ移動が出来ます。

フィルタ

列ヘッダ右側のアイコンをクリックすると、Excelライクなフィルタが使用可能です。

グループ化

コードからあらかじめ項目を指定してグループ化することもできますが、以下のようにグループパネルを使ってユーザーがインタラクティブにグループ化を行うこともできます。

データマップ

Dynamicsから取得した各種ステータスの値などは「0、1、2」のようなコード値ですが、データマップの機能を使うことで画面に表示するときは対応した文字列を表示できます。

データマップ

登録、更新、削除

グリッド上でデータを編集し、[更新]ボタンをクリックすることで変更をDynamicsのデータに反映することが出来ます。以下はデータの更新する場合の例です。

Dynamicsのリードの一覧画面を開いてみると、きちんと更新が反映されていることがわかります。

リードのメイングリッドで更新を確認

コマンドバーにリンクの追加

最後に仕上げとしてリードのグリッドビューにあるコマンドバー(リボン)をカスタマイズして、先ほど登録したWebリソースの画面にリンクするカスタムのコマンドを追加してみたいと思います。

コマンドバーの編集方法は以下の記事をご参考ください。

上記の記事の手順に従いリードのメイングリッドのコマンドバーのカスタマイズ画面を開き、左上のメニューから新規コマンドを追加します。

コマンドバーのカスタマイズ

新しいコマンドが追加されました。

コマンドバーのカスタマイズ2

右側のメニューからコマンドの設定を行います。「ラベル」の項目からコマンドの表示ラベルを「Edit with Wijmo」に変更します。

また、「アクション」の項目にLaunch関数を使った計算式を設定し、クリックされたら先ほどのWebリソースのページが開くようにリンクを設定します。

コマンドバーのカスタマイズ3

設定完了後、「保存して公開する」をクリックし、カスタマイズした内容を公開します。

コマンドバーのカスタマイズ4

公開完了後、ブラウザを再読み込みするとリードのビューのコマンドバーにカスタムのコマンドが表示されます。

コマンドバーのカスタマイズ5

カスタムのコマンドをクリックすると、リンクに設定したWebリソースのページが開き、FlexGridでリードのデータの表示と編集ができます。

さいごに

今回の記事では、「Microsoft Dynamics 365 Sales」とJavaScript開発ライブラリ「Wijmo」の高機能なデータグリッドコントロール「FlexGrid」とデータ管理クラス「CollectionView」を連携させて使用する方法をご紹介しました。

製品サイトでは、今回ご紹介したWijmoの機能を手軽に体験できるデモアプリケーションやトライアル版も公開しておりますので、こちらもご確認ください。

また、ご導入前の製品に関するご相談、ご導入後の各種サービスに関するご質問など、お気軽にお問合せください。

\  この記事をシェアする  /