Japan Dynamics CRM Team Blog

Microsoft Dynamics CRM technical information for partners and customers

Dynamics CRM 2011 サンプル紹介 REST Endpoint jQuery Contact Editor

Dynamics CRM 2011 サンプル紹介 REST Endpoint jQuery Contact Editor

  • Comments 0

みなさん、こんにちは。

今回は REST エンドポイントと JScript のサンプルの締めくくりとして、よりリアルなサンプルである
REST Endpoint jQuery Contact Editor を紹介します。このサンプルはでは、先日紹介した
REST エンドポイントを利用する jQuery サンプルのスクリプトを含んでいますので、まだそちらを
ご覧になっていない方は、まずこちらの記事をご一読ください。

また今回は、サンプルの問題の指摘とトラブルシューティングをして見ます。

このサンプルは、取引先担当者の操作をカスタム画面で実現します。

image

主な機能は
- 新規取引先担当者の作成
- 既存取引先担当者の検索
- 検索結果から、レコードの更新
- 検索結果から、レコードの削除
- Dyanmics CRM 2011 でのレコード表示

サンプルは、sdk\samplecode\js\restendpoint\restjquerycontacteditor\
にあるものを利用します。動作を試すために、事前にこのフォルダにある
restjquerycontacteditor_1_0_0_0_managed.zip ファイルを、ソリューションとして
インポートしておいてください。

このサンプルは以下のファイルで構成されています。
restjquerycontacteditor.htm - 上記スクリーンショットの画面を提供
restjquerycontacteditor.js - REST エンドポイントを利用した操作を提供
json2.js および jquery1.4.1.min.js - JSON および jQuery  ライブラリ

では早速中身を確認していきましょう。

JScript サンプル

ファイル名が違うものの、restjquerycontacteditor.js は 前回紹介したサンプルの
jqueryrestdataoperationfunctions.js と内容は同等です。よって今回は説明を
割愛します。

HTML ファイル

今回の restjquerycontacteditor.htm ファイルは、ただスクリプトをホストする
だけでなく、ユーザーに画面も提供します。要素の指定や取得、イベントの
アタッチ等、随所に jQuery を利用して読みやすいソースになっています。
$ で始まるものが主にそうですので、興味があれば調べてみてください。

早速中身を見ていきましょう。

スクリプトの読み込み
他のサンプルと同じく、まずスクリプトの読み込み定義があります。
<script src="../ClientGlobalContext.js.aspx"></script>
<script src="Scripts/json2.js" type="text/javascript"></script>
<script src="Scripts/jquery1.4.1.min.js" type="text/javascript"></script> 
<script src="Scripts/RESTJQueryContactEditor.js" type="text/javascript"></script>

独自スクリプト
次にページ内に直接埋め込まれた関数が記述されています。以下にコメントを
日本語にしつつ、説明を補足します。

変数宣言
// 結果テーブルの行データを一時的に保管するオブジェクト
var TMP_ROW = null;
        
イベントハンドラー関数
ボタンクリックに対応した関数を定義

// Search ボタンクリック時のハンドラー
function SearchButton_onclick() {

    // 検索テキストに文字がある場合
    if (SearchText.value) {
       // フィルタ条件として追加し、retrieveMultiple 関数を実行
       retrieveMultiple("ContactSet", "?$filter=substringof('" + 
       SearchText.value + "',FullName)", SearchCompleted, null);
    }
    // 検索テキストがない場合には、全ての取引先担当者を取得
    else {
       // フィルタ条件に null を渡して retrieveMultiple 関数を実行
       retrieveMultiple("ContactSet", null, SearchCompleted, null);
    }
}
// New ボタンクリック時のハンドラー
function NewButton_onclick() {

    // 取引先担当者用オブジェクトを作成
    var contact = CreateNewContact();

    // 作成したオブジェクトを渡して、新しい行を作成   
    CreateNewRow(contact);
}

コールバック関数 
操作完了時のコールバック関数を定義

// 検索完了コールバック
function SearchCompleted(data, textStatus, XmlHttpRequest) {

    // 画面上のテーブルをクリア
    EmptyTable(); 
    // 検索結果がある場合、テーブルを再作成    
    if (data && data.length > 0) {
      // 検索結果がありませんメッセージを非表示
      HideEmptyRow();
      // 検索結果の行数だけ処理をループ
      for (var i=0; i<data.length; i++) {
    
       // テーブル用の行を作成し、データをバインド
       CreateNewRow(data[i]);
       }
    }
    // 検索結果がない場合
    else { 
      // 検索結果がありませんのメッセージ表示
      ShowEmptyRow();
    }
}

// 削除成功コールバック 
function DeleteContactCompleted(data, textStatus, XmlHttpRequest) {
    alert("Contact deleted!");
}

// 削除失敗コールバック
function DeleteContactErrored(data, textStatus, errorThrown) {
    alert("Failed to delete Contact.");
}

// 更新成功コールバック
function ContactUpdateCompleted(data, textStatus, XmlHttpRequest) {

    TMP_ROW = null;

    // 全てのボタンを有効
    $(":button").attr('disabled', '');

    alert("Update complete!");
}

// 作成成功コールバック
function ContactCreateCompleted(data, textStatus, XmlHttpRequest) {

    // ID 列を更新
    if (TMP_ROW && data && data.ContactId) {

    // 取引先担当者 ID をセット
    $(TMP_ROW).find("#ContactId").attr("value", data.ContactId);
 
    // リンクの作成    
    $(TMP_ROW).find("#RecordLink").html("<a target=\"_blank\" href=\"" +
    serverUrl + "/main.aspx?etc=2&id=%7b" + data.ContactId +
    "%7d&pagetype=entityrecord\">View</a>");
    }

    // 全てのボタンを有効
    $(":button").attr('disabled', '');

    TMP_ROW = null;

    alert("Create complete!");
}

// 作成失敗コールバック
function ContactCreateFailed(data, textStatus, errorThrown) {

    // 全てのボタンを有効
    $(":button").attr('disabled', '');

    TMP_ROW = null;

    alert("Failed to create Contact...");
}

ヘルバー関数

// 新しい行を作成 
// 引数に取引先担当者を受け取り、各列に値をセット
// また各種ボタンにハンドラーをアタッチ
function CreateNewRow(contact) { 
// 取引先担当者がある場合
if (contact) {

// 1 行目用の場所を作成
var newRow = $("#ContactTable").find("tbody").children("TR:first");

// 検索結果がありませんメッセージ非表示
$("TR.EmptyRow").hide();

// テンプレート行をコピー、属性を変更
var copiedRow = $("#ContactTable").find("TR.Template").clone(true)
    .removeClass("Template").addClass("CopiedRow")
    .css("display", "inline");

// テンプレートをコピーした行を、1行目に挿入
copiedRow.insertBefore(newRow);

// 各列に値をセット
// 取引先担当者 ID より、リンク行の作成
if (contact.ContactId) {
    copiedRow.find("#ContactId").attr("value", contact.ContactId);
    // RecordLink を探してリンク行を設定
    copiedRow.find("#RecordLink").html("<a target=\"_blank\" href=\"" +
serverUrl + "/main.aspx?etc=2&id=%7b" + contact.ContactId +
"%7d&pagetype=entityrecord\">View</a>");
}
// 取引先担当者の名
if (contact.FirstName)
    copiedRow.find("#FirstName").attr("value", contact.FirstName);
// 取引先担当者の姓
if (contact.LastName)
    copiedRow.find("#LastName").attr("value", contact.LastName);
// 取引先担当者の電話番号
if (contact.Telephone1)
    copiedRow.find("#Telephone1").attr("value", contact.Telephone1);
// 取引先担当者の電子メールアドレス
if (contact.EMailAddress1)
    copiedRow.find("#EMailAddress1").attr("value", contact.EMailAddress1);
// 取引先担当者の住所 1 番地
if (contact.Address1_Line1)
    copiedRow.find("#Address1_Line1").attr("value", contact.Address1_Line1);
// 取引先担当者の住所 1 市区町名 
if (contact.Address1_City)
    copiedRow.find("#Address1_City").attr("value", contact.Address1_City);
// 取引先担当者の住所 1 都道府県
if (contact.Address1_StateOrProvince)
    copiedRow.find("#Address1_StateOrProvince").attr("value", contact.Address1_StateOrProvince);
// 取引先担当者の住所 1 郵便番号
if (contact.Address1_PostalCode)
    copiedRow.find("#Address1_PostalCode").attr("value", contact.Address1_PostalCode);

  
// Save ボタンのクリックイベントにハンドラーをアタッチ
copiedRow.find("#SaveButton").click(function () {

    //クリックされた Save ボタンの行を取得 
    var contactRow = $(this).parent().parent();

    // 行のデータを一時変数に代入 
    TMP_ROW = contactRow;

    // 全てのボタンを無効化  
    $(":button").attr('disabled', 'disabled');

    // 取得した行データより、取引先担当者のオブジェクトを作成
    var contactObject = ExtractContactFromRow(contactRow);

    // 取引先担当者のデータが ID を持っているか確認
    // 持っている場合は、レコードは存在するので、更新処理
    // 持っていない場合は、レコードが存在しないので、新規作成 
    if (contactObject.ContactId && contactObject.ContactId != null) {
 
// 取引先担当者の更新
updateRecord(contactObject.ContactId, contactObject, "ContactSet", ContactUpdateCompleted, null);
    }
    else {
 
// 取引先担当者の作成
createRecord(contactObject, "ContactSet", ContactCreateCompleted, ContactCreateFailed);
    }
});

//Save ボタンのクリックイベントにハンドラーをアタッチ
copiedRow.find("#DeleteButton").click(function () {

    var contactRow = $(this).parent().parent();

    // 行のデータを一時保管変数に代入 
    TMP_ROW = contactRow;

    //取得した行データより、取引先担当者のオブジェクトを作成
    var contactObject = ExtractContactFromRow(contactRow);

    // 取引先担当者 ID を確認して、削除実行 
    if (contactObject.ContactId && contactObject.ContactId != null) {
 
      // 取引先担当者の削除
     deleteRecord(contactObject.ContactId, "ContactSet", DeleteContactCompleted, null);

     // テーブルより行を削除
     contactRow.remove();
    }
    // 取引先担当者 ID がない場合
    else {

      // テーブルより行を削除のみ
      contactRow.remove();
    }
});   
    }
}

// 行を引数で受け取り、取引先担当者オブジェクトを返す 
function ExtractContactFromRow(row)

    // 取引先担当者用のオブジェクトを作成
    var contact = new Object();

    // 取引先担当者の ID の存在を確認 
    if (row.find("#ContactId").val()) {
       // 取引先担当者の ID をセット
       contact.ContactId = row.find("#ContactId").val();
    } 
    // 他の列の値もそれぞれセット
    contact.FirstName = row.find("#FirstName").val();
    contact.LastName = row.find("#LastName").val();
    contact.Telephone1 = row.find("#Telephone1").val();
    contact.EMailAddress1 = row.find("#EMailAddress1").val();
    contact.Address1_Line1 = row.find("#Address1_Line1").val();
    contact.Address1_City = row.find("#Address1_City").val();
    contact.Address1_StateOrProvince = row.find("#Address1_StateOrProvince").val();
    contact.Address1_PostalCode = row.find("#Address1_PostalCode").val();

    // 取引先担当者オブジェクトを返す
    return contact;
}

// 空の取引先担当者を作成 
function CreateNewContact() {

    // 取引先担当者のオブジェクトを作成 
    var contact = new Object();

    // 姓・名にダミーデータを入力して、あとは null 値
    contact.FirstName = "Firstname";
    contact.LastName = "Lastname";
    contact.Telephone1 = null;
    contact.EMailAddress1 = null;
    contact.Address1_Line1 = null;
    contact.Address1_City = null;
    contact.Address1_StateOrProvince = null;
    contact.Address1_PostalCode = null;
    contact.ContactId = null;

    // 取引先担当者オブジェクトを返す   
    return contact;
}

// レコード結果がありませんを非表示
function HideEmptyRow() {
    $("TR.EmptyRow").hide();
}

// レコード結果がありませんを表示 
function ShowEmptyRow() {
    $("TR.EmptyRow").show();
}

// テーブルのレコードをクリア
function EmptyTable() {
   
    // CopiedRow を指定して、クリア
    $("TR.CopiedRow").remove();
}

その他
スクリプト以外は、CSS およびテーブル等の定義となります。

動作の確認

では動作の確認をしたいと思います。サンプルのソリューションをインポートした後、
設定 | カスタマイズ | システムのカスタマイズを開きます。 Web リソースをクリックして、
sample_/RESTJQueryContactEditor.htm を開き、下部リンクをクリックします。

取引先担当者を新規で作成したり、検索後データを更新したりして、動作を確認します。

問題点とその対応

しばらく操作をするとあることに気がつくと思います。それは日本語で検索した場合、このブログの
はじめのスクリーンショットと異なり、検索結果が出ないということです。
image

トラブルシューティング
ではトラブルシューティングをして見ましょう。まずは Internet Explorer 8 のデバッグ機能を
利用して、 Search ボタンをクリックした場合の動作を検証します。

1. 上記画面が開いている状態で、F12 キーを押下します。

2. 開発者ツールが起動するので、スクリーンショット左下の矢印のアイコンをクリックします。
image

3. 元の画面に戻り、Search ボタンをクリックします。
image

4. ボタンが青色に縁取られるので、開発者ツールに戻ります。
この時点でソース上のボタンの箇所が選択されています。
ここの onclick を確認することで、呼出される関数を確認します。
image

5. 開発者ツールのスクリプトタブを開き、デバッグの開始をクリックします。

6. ファイルが RESTJQueryContactEditor.htm であることを確認して、関数を探し、
一番はじめのライン (53 行目) の画面左端をクリックします。するとブレークポイントが設定されます。
image

7. 再度画面に戻り、検索文字列 「山本」 を入力して、Search ボタンをクリックします。すると自動的に
ブレークポイントで実行が停止します。
image

8. SearchText.vaue を選択後右クリックして、ウォッチ式の追加をクリックします。
image

9. 開発者ツール右画面に情報が出ます。この要領で見たいオブジェクトを表示
していくことが可能です。
image

10. F11 を押下することで次のラインに処理が進みます。また F5 を押下することで
次のブレークポイントへ移動します。ブレークポイントがない場合は、処理が完了します。

この作業をすることで、その後 retrieveMultiple 関数に検索文字列がそのまま渡され、
結果以下のような REST 用 URI が作成されることが分かります。

…省略…/OrganizationData.svc/ContactSet()?$filter=substringof('山本',FullName)" 

この URI を実際にブラウザに渡しても結果は返って来ません。

対処

対処としては、日本語の文字列を URI エンコードすることが考えられます。以下の
手順でファイルを更新してみます。

1. システムのカスタマイズより Web リソースを開き、sample_/RESTJQueryContactEditor.htm
を開きます。

2. テキストエディターボタンをクリックします。
image

3. ソースタブをクリックして、SearchButton_onclick 関数を探します。

4. ソースを以下のように変更して、URI エンコードを処理します。
if (SearchText.value) {
    var encodeString =encodeURI(SearchText.value); // 検索文字列を URI エンコードする
    retrieveMultiple("ContactSet", "?$filter=substringof('" +
        encodeString + "',FullName)", SearchCompleted, null); // encodeString を渡す
}

5. 画面を閉じて上書き保存します。

6. 画面下部のリンクをクリックして、再度日本語で検索を実施します。

まとめ

今回で REST エンドポイントと JScript のサンプル紹介は終了とします。サンプルは汎用的に
作られている一方で、言語や状況によって修正が必要な場合もあります。是非お手元の環境で
動かしてみていただき、理解を深めてください。

次回からは REST エンドポイントを利用した Silverlight のサンプルを紹介していきます。

- Dynamics CRM サポート 中村 憲一郎

  • Loading...
Leave a Comment
  • Please add 2 and 8 and type the answer here:
  • Post