locked
CRM 2011: SDK CRUD Operation samples RRS feed

  • Question

  • Hi all,

    I was trying to test the CRUD operations samples available on the SDK in order to retrieve account data, so I have tried the below code:

     

    // =====================================================================
    // This file is part of the Microsoft Dynamics CRM SDK code samples.
    //
    // Copyright (C) Microsoft Corporation. All rights reserved.
    //
    // This source code is intended only as a supplement to Microsoft
    // Development Tools and/or on-line documentation. See these other
    // materials for detailed information regarding Microsoft code samples.
    //
    // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
    // PARTICULAR PURPOSE.
    // =====================================================================
    
    // <snippetJScriptRESTDataOperations1>
    var ODataPath;
    var startTime; //So that the duration time can be captured.
    var accountName = "Sample Account";
    var serverUrl;
    
    function init()
    {
      // Set Global Variables
      //var context = GetGlobalContext(); //GetGlobalContext function exists in ClientGlobalContext.js.aspx
      //serverUrl = context.getServerUrl();
      serverUrl="http://<IP address>/<org name>"
      ODataPath = serverUrl + "/XRMServices/2011/OrganizationData.svc";
      startTime = new Date();
      var guid = Xrm.Page.data.entity.getId();
      retrieveAccountRecord(guid);
    }
    
    function retrieveAccountRecord(Id)
    {
      showMessage("retrieveAccountRecord function START");
    
      var retrieveAccountReq = new XMLHttpRequest();
      retrieveAccountReq.open("GET", ODataPath + "/AccountSet(guid'" + Id + "')", true);
      retrieveAccountReq.setRequestHeader("Accept", "application/json");
      retrieveAccountReq.setRequestHeader("Content-Type", "application/json; charset=utf-8");
      retrieveAccountReq.onreadystatechange = function ()
      {
        retrieveAccountReqCallBack(this);
      };
      retrieveAccountReq.send();
      showMessage("retrieveAccountRecord function END.");
    }
    
    function retrieveAccountReqCallBack(retrieveAccountReq)
    {
      if (retrieveAccountReq.readyState == 4 /* complete */)
      {
        if (retrieveAccountReq.status == 200)
        {
          //Success
          var retrievedAccount = JSON.parse(retrieveAccountReq.responseText).d;
          showMessage("ACTION: Retrieved account Name = \"" + retrievedAccount.Name + "\", AccountId = {" + retrievedAccount.AccountId + "}");
    
          //NEXT STEP: Update the account
          updateAccountRecord(retrievedAccount.AccountId);
          showMessage("retrieveAccountReqCallBack function success END");
        }
        else
        {
          //Failure
          errorHandler(retrieveAccountReq);
          showMessage("retrieveAccountReqCallBack function failure END");
        }
      }
    }
    
    //Function to display information on the HTML page.
    function showMessage(message)
    {
      var duration = new Date(new Date() - startTime);
      var time = duration.getSeconds() + ":" + duration.getMilliseconds();
      var dvMessage = document.createElement("div");
      dvMessage.innerHTML = time + " : " + message;
      document.getElementById("status").appendChild(dvMessage);
    }
    
    
    //Function to handle any http errors
    function errorHandler(XmlHttpRequest)
    {
      showMessage("Error : " +
        XmlHttpRequest.status + ": " +
        XmlHttpRequest.statusText + ": " +
        JSON.parse(XmlHttpRequest.responseText).error.message.value);
    }
    // </snippetJScriptRESTDataOperations1>
    

    I have attached the above code sample to the form in addition to the json2.js file and I've called the init() function in the onLoad event, but I was facing a set of errors:

    1 - when I try to open the form a message poped up mentioning an error related to the  GetGlobalContext() function, so I have replaced it with a static server IP, but Now I'm facing another error related to the appendChild() function.

     

    Could anybody please help me solve this errors ??

     

    thanks and best regards..

     

    Tuesday, August 16, 2011 12:13 PM

Answers

  • Mostafa -
    I'm afraid I have to admit I think the sample is not as good as it should be. It was one of the first written and it needs to be re-written.
    Here is a new one that provides better example:
    First create a JScript Web Resource named new_/Scripts/SDK.JScriptRESTDataOperations.js
    Then create an HTML Web Resource named new_/SDK.JScriptRESTDataOperations.htm
    new_/Scripts/SDK.JScriptRESTDataOperations.js
    if (typeof (SDK) == "undefined")
    { SDK = { __namespace: true }; }
    SDK.JScriptRESTDataOperations = {
    	_context: function ()
    	{
    		if (typeof GetGlobalContext != "undefined")
    		{ return GetGlobalContext(); }
    		else
    		{
    			if (typeof Xrm != "undefined")
    			{
    				return Xrm.Page.context;
    			}
    			else
    			{ return new Error("Context is not available."); }
    		}
    	},
    	_getServerUrl: function ()
    	{
    		var serverUrl = this._context().getServerUrl()
    		if (serverUrl.match(/\/$/))
    		{
    			serverUrl = serverUrl.substring(0, serverUrl.length - 1);
    		}
    		return serverUrl;
    	},
    	_ODataPath: function ()
    	{
    		return this._getServerUrl() + "/XRMServices/2011/OrganizationData.svc/";
    	},
    	_errorHandler: function (req)
    	{
    		return new Error("Error : " +
      req.status + ": " +
      req.statusText + ": " +
      JSON.parse(req.responseText).error.message.value);
    	},
    	_dateReviver: function (key, value)
    	{
    		var a;
    		if (typeof value === 'string')
    		{
    			a = /Date\(([-+]?\d+)\)/.exec(value);
    			if (a)
    			{
    				return new Date(parseInt(value.replace("/Date(", "").replace(")/", ""), 10));
    			}
    		}
    		return value;
    	},
    	Create: function (object, type, successCallback, errorCallback)
    	{
    		var req = new XMLHttpRequest();
    		req.open("POST", this._ODataPath() + type + "Set", true);
    		req.setRequestHeader("Accept", "application/json");
    		req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    		req.onreadystatechange = function ()
    		{
    			if (this.readyState == 4 /* complete */)
    			{
    				if (this.status == 201)
    				{
    					successCallback(JSON.parse(this.responseText, SDK.JScriptRESTDataOperations._dateReviver).d);
    				}
    				else
    				{
    					errorCallback(SDK.JScriptRESTDataOperations._errorHandler(this));
    				}
    			}
    		};
    		req.send(JSON.stringify(object));
    	},
    	Retrieve: function (id, type, successCallback, errorCallback)
    	{
    		var req = new XMLHttpRequest();
    		req.open("GET", this._ODataPath() + type + "Set(guid'" + id + "')", true);
    		req.setRequestHeader("Accept", "application/json");
    		req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    		req.onreadystatechange = function ()
    		{
    			if (this.readyState == 4 /* complete */)
    			{
    				if (this.status == 200)
    				{
    					successCallback(JSON.parse(this.responseText, SDK.JScriptRESTDataOperations._dateReviver).d);
    				}
    				else
    				{
    					errorCallback(SDK.JScriptRESTDataOperations._errorHandler(this));
    				}
    			}
    		};
    		req.send();
    	},
    	Update: function (id, object, type, successCallback, errorCallback)
    	{
    		var req = new XMLHttpRequest();
    
    		req.open("POST", this._ODataPath() + type + "Set(guid'" + id + "')", true);
    		req.setRequestHeader("Accept", "application/json");
    		req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    		req.setRequestHeader("X-HTTP-Method", "MERGE");
    		req.onreadystatechange = function ()
    		{
    			if (this.readyState == 4 /* complete */)
    			{
    				if (this.status == 204 || this.status == 1223)
    				{
    					successCallback();
    				}
    				else
    				{
    					errorCallback(SDK.JScriptRESTDataOperations._errorHandler(this));
    				}
    			}
    		};
    		req.send(JSON.stringify(object));
    	},
    	Delete: function (id, type, successCallback, errorCallback)
    	{
    		var req = new XMLHttpRequest();
    		req.open("POST", this._ODataPath() + type + "Set(guid'" + id + "')", true);
    		req.setRequestHeader("Accept", "application/json");
    		req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    		req.setRequestHeader("X-HTTP-Method", "DELETE");
    		req.onreadystatechange = function ()
    		{
    			if (this.readyState == 4 /* complete */)
    			{
    				if (this.status == 204 || this.status == 1223)
    				{
    					successCallback();
    				}
    				else
    				{
    					errorCallback(SDK.JScriptRESTDataOperations._errorHandler(this));
    				}
    			}
    		};
    		req.send();
    
    	},
    	RetrieveMultiple: function (type, filter, successCallback, errorCallback)
    	{
    
    		if (filter != null)
    		{
    			filter = "?" + filter;
    		}
    		else
    		{ filter = ""; }
    
    		var req = new XMLHttpRequest();
    		req.open("GET", this._ODataPath() + type + "Set" + filter, true);
    		req.setRequestHeader("Accept", "application/json");
    		req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    		req.onreadystatechange = function ()
    		{
    			if (this.readyState == 4 /* complete */)
    			{
    				if (this.status == 200)
    				{
    					successCallback(JSON.parse(this.responseText, SDK.JScriptRESTDataOperations._dateReviver).d.results);
    				}
    				else
    				{
    					errorCallback(SDK.JScriptRESTDataOperations._errorHandler(this));
    				}
    			}
    
    		};
    		req.send();
    
    
    	},
    	__namespace: true
    };
    
    
    new_/SDK.JScriptRESTDataOperations.htm
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
     <title>JScript REST Data Operations</title>
     <script src="Scripts/SDK.JScriptRESTDataOperations.js" type="text/javascript"></script>
     <script type="text/javascript">
    
     var primaryContact = null;
    
     document.onreadystatechange = function ()
     {
     if (document.readyState == "complete")
     {
     getFirstContactToBePrimaryContact();
     document.getElementById("start").onclick = createAccount;
     }
     }
    
     function createAccount()
     {
     var account = {};
     account.Name = "Test Account Name";
     account.Description = "This account was created by the JScriptRESTDataOperations sample.";
     if (primaryContact != null)
     {
     //Set a lookup value
     writeMessage("Setting the primary contact to: " + primaryContact.FullName + ".");
     account.PrimaryContactId = { Id: primaryContact.ContactId, LogicalName: "contact", Name: primaryContact.FullName };
     
     }
     //Set a picklist value
     writeMessage("Setting Preferred Contact Method to E-mail.");
     account.PreferredContactMethodCode = { Value: 2 }; //E-mail
    
     //Set a money value
     writeMessage("Setting Annual Revenue to Two Million .");
     account.Revenue = { Value: "2000000.00" }; //Set Annual Revenue
    
     //Set a Boolean value
     writeMessage("Setting Contact Method Phone to \"Do Not Allow\".");
     account.DoNotPhone = true; //Do Not Allow
    
     //Add Two Tasks
     var today = new Date();
     var startDate = new Date(today.getFullYear(), today.getMonth(), today.getDate()+3); //Set a date three days in the future.
     
     var LowPriTask = { Subject: "Low Priority Task", ScheduledStart: startDate, PriorityCode: { Value: 0} }; //Low Priority Task
     var HighPriTask = { Subject: "High Priority Task", ScheduledStart: startDate, PriorityCode: { Value: 2} }; //High Priority Task
     account.Account_Tasks = [LowPriTask, HighPriTask]
     
    
     //Create the Account
     SDK.JScriptRESTDataOperations.Create(
      account,
      "Account",
      function (account)
      {
      writeMessage("The account named \"" + account.Name + "\" was created with the AccountId : \"" + account.AccountId + "\".");
      writeMessage("Retrieving account with the AccountId: \"" + account.AccountId + "\".");
      retrieveAccount(account.AccountId)
      },
      errorHandler
     );
      this.setAttribute("disabled", "disabled");
    
     }
    
     function retrieveAccount(AccountId)
     {
     SDK.JScriptRESTDataOperations.Retrieve(
      AccountId,
      "Account",
      function (account)
      {
      writeMessage("Retrieved the account named \"" + account.Name + "\". This account was created on : \"" + account.CreatedOn + "\".");
      updateAccount(AccountId);
      },
      errorHandler
     );
     }
    
     function updateAccount(AccountId)
     {
     var account = {};
     writeMessage("Changing the account Name to \"Updated Account Name\".");
     account.Name = "Updated Account Name";
     writeMessage("Adding Address information");
     account.Address1_AddressTypeCode = { Value: 3 }; //Address 1: Address Type = Primary
     account.Address1_City = "Sammamish";
     account.Address1_Line1 = "123 Maple St.";
     account.Address1_PostalCode = "98074";
     account.Address1_StateOrProvince = "WA";
     writeMessage("Setting E-Mail address");
     account.EMailAddress1 = "someone@microsoft.com";
    
    
     SDK.JScriptRESTDataOperations.Update(
      AccountId,
      account,
      "Account",
      function ()
      {
      writeMessage("The account Name was updated to \"Updated Account Name\".");
      deleteAccount(AccountId);
      },
      errorHandler
     );
     }
    
     function deleteAccount(AccountId)
     {
     if (confirm("Do you want to delete this account record?"))
     {
     writeMessage("You chose to delete the account record.");
     SDK.JScriptRESTDataOperations.Delete(
      AccountId,
      "Account",
      function () { writeMessage("The account was deleted."); },
      errorHandler
      );
     }
     else
     {
     var li = document.createElement("li");
    
     var span = document.createElement("span");
     span.innerText = "You chose not to delete the record. You can view the record ";
    
     var link = document.createElement("a");
     link.href = SDK.JScriptRESTDataOperations._getServerUrl() + "/main.aspx?etc=1&id=%7b" + AccountId + "%7d&pagetype=entityrecord";
     link.target = "_blank";
     link.innerText = "here";
    
     li.appendChild(span);
     li.appendChild(link);
     document.getElementById("results").appendChild(li);
     }
     }
    
     function getFirstContactToBePrimaryContact()
     {
     SDK.JScriptRESTDataOperations.RetrieveMultiple(
      "Contact",
      "$select=ContactId,FullName&$top=1",
      function (results)
      {
      var firstResult = results[0];
      if (firstResult != null)
      {
      primaryContact = results[0];
      }
      else
      {
      writeMessage("No Contact records are available to set as the primary contact for the account.");
      }
      },
      errorHandler
     );
     }
    
     function errorHandler(error)
     {
     writeMessage(error.message); 
     }
    
     //Helper function to write data to this page:
     function writeMessage(message)
     {
     var li = document.createElement("li");
     li.innerText = message;
     document.getElementById("results").appendChild(li);
     }
     </script>
    </head>
    <body>
    <button id="start" title="Click this button to start the sample.">Start</button>
    <ol id="results"></ol>
    </body>
    </html>
    
    
    
    
    After you create both web resources and publish your customizations, Preview the HTML web resource and press the START button.
     The SDK.JScriptRESTOperations.js library provides more generic re-usable functions for Create, Update, Retrieve, Delete, and RetrieveMultiple. You can use them with any type of entity. You can use this library from form scripts.
    The HTML Web Resource in this case is just staging the sequence of Create, Retrieve, Update and Delete operations. It controls the actions and uses the JScript library functions to just perform the actions.
    The JavaScript in the HTML web resource also shows how to set values for various types of data, Optionsets, Money, Dates, and Lookups (Entity References). It also shows a Deep Insert example by creating two associated tasks with the creation of the account record.
    One of the big differences you will see in these samples compared to the CRM 4 samples is that these are Asynchronous. This helps prevent the browser from freezing up when these requests are executed and it is really important to get in a good habit to always use these calls asynchronously. You can do them synchronously, but it is just not a good idea.
    With these asynchronous methods you see how having call back functions becomes important. The SDK.JScriptRESTOperations library consistently provides a SuccessCallback and an errorCallback function.
    There are lots of different ways you might structure your JScript libraries, but this is my preferred pattern so far.
    Also note this sample uses more anonymous functions and literal notation than the previous sample. '{}' is the same as new Object(). '[]' is the same as new Array();
    Note I haven't included the json2.js library. This is only necessary when people are using IE7. IE8 and higher have built-in support for the JSON object (as long as the page has a proper DOCTYPE declaration to make sure the browser doesn't revert to Quirks mode).
    This sample is my starting point for re-writing the original. It doesn't have a ton of comments in it, but I hope it will make sense. Let me know if you have any questions.
    Best regards,

    Jim Daly Technical Writer Microsoft Dynamics CRM
    Friday, August 19, 2011 6:18 AM
    Answerer
  • Mostafa -
    In CRM 4 you defined a single JScript event handler for form events. That is all you could do. You didn't have JScript libraries.  You weren't expected to define functions. Your options were very limited. I think you are confused applying CRM 4 thinking to CRM 2011. I strongly recommend you read the conceptual parts of the documentation JScript Libraries for Microsoft Dynamics CRM, Write Code for Microsoft Dynamics CRM Forms; GetGlobalContext function. You may also need to simply learn more about using more advanced concepts for JScript in general. With this greater level of complexity, I can't provide all you need to know in the SDK or in a forum response. The article Create Advanced Web Applications With Object-Oriented Techniques from MSDN Magazine (2007) is a good introduction to some of these more advanced techniques.

    While the original script might be more readable from a procedural perspective, it is actually not a good example because it doesn't represent a good real-world usage. A better solution is to create and re-use a library for your commonly performed operations. You do not need to merge the HTML and the JScript library - you want to use the SDK.JScriptRESTDataOperations.js library and then create your own JScript library with functions that use the functions defined within SDK.JScriptRESTDataOperations.js. You can have multiple libraries and functions now in form events that you could never have in CRM 4.

    In CRM 2011 you have JScript Libraries and HTML web resources and you can configure multiple event handlers for form events. You have many more options. This flexibility makes it much more like creating your own application. You will typically break your JScript libraries out by function.

    The SDK.JScriptRESTDataOperations.js library I provided above is an all purpose base library for all the supported actions for the REST endpoint. If you want to use that library within an HTML web resource, you must include a <script> element to include it in the HTML page as I did in the SDK.JScriptRESTDataOperations.htm file. Once that file is loaded, you can create scripts that use it.

    For form scripts, you have to make sure to include the SDK.JScriptRESTDataOperations.js library in the form by registering it. Then the functions it provides will be available within your functions for any other event in the page. You will typically create your own JScript Web Resource for the code you use to work with new records.

    To use the SDK.JScriptRESTDataOperations.js library, review it and note that there are 5 'public' functions (Create, Retrieve, Update, Delete, and RetrieveMultiple). The other functions use the underscore character to indicate that they are intended to be 'private' - but there is not really the concept of a private function in JScript.

    Each of these functions requires the type parameter to contain the Entity Schema Name to be passed for the operation. This is what allows you to use this one library to work with records from any type of entity. If you want to create an Opportunity Product record, the Schema Name is 'OpportunityProduct'.

    To create or update a record, you need to define it and pass it as the object parameter. Note how the onclick event handler for the Start button in the HTML web resource uses this code to create a new Account record: (I've removed references to all the calls to the writeMessage function since they don't contribute to creating the record)

    The sample contains a global primaryContact variable that is set when the page loads using the getFirstContactToBePrimaryContact function in the HTML web resource. This way you will have a valid reference to a primary contact that is used to show how to set a lookup value.

    var account = {};
      account.Name = "Test Account Name";
      account.Description = "This account was created by the JScriptRESTDataOperations sample.";
      if (primaryContact != null)
      {
      //Set a lookup value
        account.PrimaryContactId = { Id: primaryContact.ContactId, LogicalName: "contact", Name: primaryContact.FullName };
      
      }
      //Set a picklist value
      account.PreferredContactMethodCode = { Value: 2 }; //E-mail
    
      //Set a money value
       account.Revenue = { Value: "2000000.00" }; //Set Annual Revenue
    
      //Set a Boolean value
       account.DoNotPhone = true; //Do Not Allow
    
      //Add Two Tasks
      var today = new Date();
      var startDate = new Date(today.getFullYear(), today.getMonth(), today.getDate()+3); //Set a date three days in the future.
      
      var LowPriTask = { Subject: "Low Priority Task", ScheduledStart: startDate, PriorityCode: { Value: 0} }; //Low Priority Task
      var HighPriTask = { Subject: "High Priority Task", ScheduledStart: startDate, PriorityCode: { Value: 2} }; //High Priority Task
      account.Account_Tasks = [LowPriTask, HighPriTask]
      
    
      //Create the Account
      SDK.JScriptRESTDataOperations.Create(
       account,
       "Account",
       function (account)
       {
       // Code you want to have happen after the account is created will go in this anonymous function.
    
       },
       errorHandler //In this sample, the function just uses the writeMessage function to show the text of the error
    
       );
    

    So, I think you can see that you just need to specify the object property values using the schemaName for each of the properties. Certain types of attributes (Lookup, Picklist, Money, Boolean & Dates) need special handling, but the rest can be handled simply like String attributes.

    Note that I've also included a sample of creating associated Task records using the Deep Insert method. This is just something that the REST endpoint allows.

    The other two parameters in the SDK.JScriptRESTDataOperations.js library are the successCallback and the errorCallback. You may note that in the HTML page I use anonymous functions. This for the create function the successCallback is passed a reference to the object that was created. You can then add code to the function you assign as the successCallback to do what you need, for example, to assign a value from the Opportunity Product record to the custom form field you mention. You don't have to use anonymous functions - but it just makes your code more efficient. You can see how I didn't use anonymous functions for the errorHandler function because I re-used that function several times.

    As a final note, when you specify entity attributes, you must use the attribute SchemaName (not Logical Name). You also need to know the integer option values when setting Picklists. I rely on the Entity Metadata Browser in the SDK to make sure I get the name of the attribute correct and the proper Picklist option value.

    I hope this helps.

    I am re-visiting the SDK documentation with hope of adding more content specifically to address issues like I believe you are experiencing. So I appreciate hearing your questions.

    Best regards,


    Jim Daly Technical Writer Microsoft Dynamics CRM
    Sunday, August 21, 2011 6:24 PM
    Answerer
  • Hello, sorry for disturbance,

     

    I know that this thread should have been terminated while ago... But I'm just letting you know that finally I have found a solution for my problem, simply the RetireveAccount.js code should be as follow:

    function Function1() 
    {
     var id = Xrm.Page.data.entity.getId();
    SDK.JScriptRESTDataOperations.Retrieve(id, "Account", Function2, function (error) { alert(error.message); });
    }
    
    function Function2(returnedAccount) 
    {
     alert(returnedAccount.Name);
    }

    thanks and best regards..

    Monday, September 5, 2011 9:48 AM

All replies

  • Instead of getting the server url context.getServerUrl();

    You need to check if the function is defined.
    Use the following function to the server url

    function getServerURL() {
        ///<summary>    ///</summary>

        var url = "";
        if (typeof GetGlobalContext == "function") {
            var context = GetGlobalContext();
            url = context.getServerUrl();
        }
        else {
            if (typeof Xrm.Page.context == "object") {
                url = Xrm.Page.context.getServerUrl();
            }
            else
            { throw new Error("Unable to access serverurl"); }
        }

        if (url.match(/\/$/)) {
            url = url.substring(0, url.length - 1);
        }

        return url;
    }

    I think the problem is with your showMessage function. The system is not finding document.getElementById("status")

    if you comment that showMessage() lines it may work.

    I can't see updateAccountRecord function either.

     

    I hope this helps.

     

     


    Amreek singh Senior CRM Consultant CDC Praxa Sydney,Australia http://mscrmshop.blogspot.com/
    • Proposed as answer by Amreek Singh Tuesday, August 16, 2011 1:54 PM
    Tuesday, August 16, 2011 1:53 PM
  • Dear Amreek,

    I have tried what you say, and now it displays an error in page

     

    please advise

     

    the js file now is as follow:

    // =====================================================================
    // This file is part of the Microsoft Dynamics CRM SDK code samples.
    //
    // Copyright (C) Microsoft Corporation. All rights reserved.
    //
    // This source code is intended only as a supplement to Microsoft
    // Development Tools and/or on-line documentation. See these other
    // materials for detailed information regarding Microsoft code samples.
    //
    // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
    // PARTICULAR PURPOSE.
    // =====================================================================
    
    // <snippetJScriptRESTDataOperations1>
    var ODataPath;
    var startTime; //So that the duration time can be captured.
    var accountName = "Sample Account";
    var serverUrl;
    
    function init()
    {
      // Set Global Variables
      //var context = GetGlobalContext(); //GetGlobalContext function exists in ClientGlobalContext.js.aspx
      //serverUrl = context.getServerUrl();
      serverUrl = getServerURL();
      ODataPath = serverUrl + "/XRMServices/2011/OrganizationData.svc";
      startTime = new Date();
      var guid = Xrm.Page.data.entity.getId();
      retrieveAccountRecord(guid);
    }
    
    function retrieveAccountRecord(Id)
    {
    //  showMessage("retrieveAccountRecord function START");
      alert("retrieveAccountRecord function START");
      var retrieveAccountReq = new XMLHttpRequest();
      retrieveAccountReq.open("GET", ODataPath + "/AccountSet(guid'" + Id + "')", true);
      retrieveAccountReq.setRequestHeader("Accept", "application/json");
      retrieveAccountReq.setRequestHeader("Content-Type", "application/json; charset=utf-8");
      retrieveAccountReq.onreadystatechange = function ()
      {
        retrieveAccountReqCallBack(this);
      };
      retrieveAccountReq.send();
      //  showMessage("retrieveAccountRecord function END.");
      alert("retrieveAccountRecord function END.");
    }
    
    function retrieveAccountReqCallBack(retrieveAccountReq)
    {
      alert("inside retrieveAccountReqCallBack");
      if (retrieveAccountReq.readyState == 4 /* complete */)
      {
        alert("inside if condition");
        if (retrieveAccountReq.status == 200)
        {
          //Success
          var retrievedAccount = JSON.parse(retrieveAccountReq.responseText).d;
          alert("ACTION: Retrieved account Name = \"" + retrievedAccount.Name + "\", AccountId = {" + retrievedAccount.AccountId + "}")
    //      showMessage("ACTION: Retrieved account Name = \"" + retrievedAccount.Name + "\", AccountId = {" + retrievedAccount.AccountId + "}");
    
          //NEXT STEP: Update the account
          //updateAccountRecord(retrievedAccount.AccountId);
          //      showMessage("retrieveAccountReqCallBack function success END");
          alert("retrieveAccountReqCallBack function success END");
        }
        else
        {
          //Failure
          errorHandler(retrieveAccountReq);
          //      showMessage("retrieveAccountReqCallBack function failure END");
          alert("retrieveAccountReqCallBack function failure END");
        }
      }
    }
    
    //Function to display information on the HTML page.
    function showMessage(message)
    {
      var duration = new Date(new Date() - startTime);
      var time = duration.getSeconds() + ":" + duration.getMilliseconds();
      var dvMessage = document.createElement("div");
      dvMessage.innerHTML = time + " : " + message;
      document.getElementById("status").appendChild(dvMessage);
    }
    
    
    //Function to handle any http errors
    function errorHandler(XmlHttpRequest)
    {
    //  showMessage("Error : " +
    //    XmlHttpRequest.status + ": " +
    //    XmlHttpRequest.statusText + ": " +
    //    JSON.parse(XmlHttpRequest.responseText).error.message.value);
      alert("Error : " +
        XmlHttpRequest.status + ": " +
        XmlHttpRequest.statusText + ": " +
        JSON.parse(XmlHttpRequest.responseText).error.message.value);
    }
    // </snippetJScriptRESTDataOperations1>
    
    function getServerURL()
    {
      ///<summary>  ///</summary>
    
      var url = "";
      if (typeof GetGlobalContext == "function")
      {
        var context = GetGlobalContext();
        url = context.getServerUrl();
      }
      else
      {
        if (typeof Xrm.Page.context == "object")
        {
          url = Xrm.Page.context.getServerUrl();
        }
        else
        { throw new Error("Unable to access serverurl"); }
      }
    
      if (url.match(/\/$/))
      {
        url = url.substring(0, url.length - 1);
      }
    
      return url;
    }


    Wednesday, August 17, 2011 9:52 AM
  • Dear Amreek, any suggestions ??
    Wednesday, August 17, 2011 12:26 PM
  • Hi Mustafa,

    The code is perfect. There is nothing wrong with the code.

    I tried the code on my machine. I call the init() function on form load and I got all the alert messages.

    It won't work on create as it is taking guid of entity as a parameter but it is definitely working on update forms

    Here are some the screen shorts.

     

     

    I hope this helps.

     

     


    Amreek singh Senior CRM Consultant CDC Praxa Sydney,Australia http://mscrmshop.blogspot.com/
    • Proposed as answer by Amreek Singh Wednesday, August 17, 2011 11:35 PM
    Wednesday, August 17, 2011 11:35 PM
  • Dear Amreek, would you please provide the code here

    Thursday, August 18, 2011 1:46 PM
  • Mostafa -

    I wrote this sample and I think one key thing to understand is that this sample is intended to be executed in the context of the sample/JScriptRESTDATAOperations.htm file included in the sample. It contains the <div id="status" /> element that the script references in the showMessage function. The HTML page also include the <script> reference to the ClientGlobalContext.js.aspx file to have the GetGlobalContext function.

    If you want to see the sample working, the easiest thing is to import the managed solution (JScriptRESTDataOperations_1_0_0_0_managed.zip ) that it is included in the SDK. To view the results, locate the sample/JScriptRESTDATAOperations.htm Web Resource that is created in the solution and preview it.

    The other key thing to understand is that the sample code, as written, uses the GetGlobalContext function and retrieves the correct server URL using the context.getServerURL() function. This will ONLY work if the code is executed from a Web Resource in the application. If you are trying to run the code from Visual Studio or from any other web site you will run into the fact that you are not being authenticated.

    As stated in the introductory topic Use the REST Endpoint for Web Resources:

    Authentication is only possible within the application

    Use of the REST endpoint is limited to JScript libraries or SilverlightWeb resources.

    Finally, this sample is really designed to provide a relatively simple working example of CRUD operations without any user input. So admittedly it isn't very specific to a real-world usage.

    Tell me what kind of basic real-world scenario you expect to want to use these operations with. Are you wanting to add a button to the ribbon? Do you want to attach a function to a form event? Do you want to create a UI to edit records? I'm always planning for new samples. I can use your feedback.

    Best regards,

     


    Jim Daly Technical Writer Microsoft Dynamics CRM
    Thursday, August 18, 2011 5:53 PM
    Answerer
  • Hello Jim,

    I was trying to develop code samples for CRUD operations for crm 2011 like the ones provided here :http://technet.microsoft.com/en-us/library/cc150864.aspx for crm 4.

    So I was trying to refine the samples provided in the SDK to be ready for future use with any crm 2011 entity after applying some minor updates.

     

    I have developed the above code sample that is not working for me but Amreek says that It's perfect !!

    so I was looking for help to know what's wrong with my code

     

    thanks in advance for your support and best regards...

    Thursday, August 18, 2011 8:55 PM
  • I created a webresource called Testjavascript.js and pasted your code in it.  I saved the webresource and published it. Then I add the referred the webresource in account form and called init function on form load.

    I did not make any changes to the code.

     


    Amreek singh Senior CRM Consultant CDC Praxa Sydney,Australia http://mscrmshop.blogspot.com/
    Friday, August 19, 2011 12:09 AM
  • did you added the json2.js or jquery.js files to the form ??
    Friday, August 19, 2011 12:17 AM
  • Mostafa -
    I'm afraid I have to admit I think the sample is not as good as it should be. It was one of the first written and it needs to be re-written.
    Here is a new one that provides better example:
    First create a JScript Web Resource named new_/Scripts/SDK.JScriptRESTDataOperations.js
    Then create an HTML Web Resource named new_/SDK.JScriptRESTDataOperations.htm
    new_/Scripts/SDK.JScriptRESTDataOperations.js
    if (typeof (SDK) == "undefined")
    { SDK = { __namespace: true }; }
    SDK.JScriptRESTDataOperations = {
    	_context: function ()
    	{
    		if (typeof GetGlobalContext != "undefined")
    		{ return GetGlobalContext(); }
    		else
    		{
    			if (typeof Xrm != "undefined")
    			{
    				return Xrm.Page.context;
    			}
    			else
    			{ return new Error("Context is not available."); }
    		}
    	},
    	_getServerUrl: function ()
    	{
    		var serverUrl = this._context().getServerUrl()
    		if (serverUrl.match(/\/$/))
    		{
    			serverUrl = serverUrl.substring(0, serverUrl.length - 1);
    		}
    		return serverUrl;
    	},
    	_ODataPath: function ()
    	{
    		return this._getServerUrl() + "/XRMServices/2011/OrganizationData.svc/";
    	},
    	_errorHandler: function (req)
    	{
    		return new Error("Error : " +
      req.status + ": " +
      req.statusText + ": " +
      JSON.parse(req.responseText).error.message.value);
    	},
    	_dateReviver: function (key, value)
    	{
    		var a;
    		if (typeof value === 'string')
    		{
    			a = /Date\(([-+]?\d+)\)/.exec(value);
    			if (a)
    			{
    				return new Date(parseInt(value.replace("/Date(", "").replace(")/", ""), 10));
    			}
    		}
    		return value;
    	},
    	Create: function (object, type, successCallback, errorCallback)
    	{
    		var req = new XMLHttpRequest();
    		req.open("POST", this._ODataPath() + type + "Set", true);
    		req.setRequestHeader("Accept", "application/json");
    		req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    		req.onreadystatechange = function ()
    		{
    			if (this.readyState == 4 /* complete */)
    			{
    				if (this.status == 201)
    				{
    					successCallback(JSON.parse(this.responseText, SDK.JScriptRESTDataOperations._dateReviver).d);
    				}
    				else
    				{
    					errorCallback(SDK.JScriptRESTDataOperations._errorHandler(this));
    				}
    			}
    		};
    		req.send(JSON.stringify(object));
    	},
    	Retrieve: function (id, type, successCallback, errorCallback)
    	{
    		var req = new XMLHttpRequest();
    		req.open("GET", this._ODataPath() + type + "Set(guid'" + id + "')", true);
    		req.setRequestHeader("Accept", "application/json");
    		req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    		req.onreadystatechange = function ()
    		{
    			if (this.readyState == 4 /* complete */)
    			{
    				if (this.status == 200)
    				{
    					successCallback(JSON.parse(this.responseText, SDK.JScriptRESTDataOperations._dateReviver).d);
    				}
    				else
    				{
    					errorCallback(SDK.JScriptRESTDataOperations._errorHandler(this));
    				}
    			}
    		};
    		req.send();
    	},
    	Update: function (id, object, type, successCallback, errorCallback)
    	{
    		var req = new XMLHttpRequest();
    
    		req.open("POST", this._ODataPath() + type + "Set(guid'" + id + "')", true);
    		req.setRequestHeader("Accept", "application/json");
    		req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    		req.setRequestHeader("X-HTTP-Method", "MERGE");
    		req.onreadystatechange = function ()
    		{
    			if (this.readyState == 4 /* complete */)
    			{
    				if (this.status == 204 || this.status == 1223)
    				{
    					successCallback();
    				}
    				else
    				{
    					errorCallback(SDK.JScriptRESTDataOperations._errorHandler(this));
    				}
    			}
    		};
    		req.send(JSON.stringify(object));
    	},
    	Delete: function (id, type, successCallback, errorCallback)
    	{
    		var req = new XMLHttpRequest();
    		req.open("POST", this._ODataPath() + type + "Set(guid'" + id + "')", true);
    		req.setRequestHeader("Accept", "application/json");
    		req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    		req.setRequestHeader("X-HTTP-Method", "DELETE");
    		req.onreadystatechange = function ()
    		{
    			if (this.readyState == 4 /* complete */)
    			{
    				if (this.status == 204 || this.status == 1223)
    				{
    					successCallback();
    				}
    				else
    				{
    					errorCallback(SDK.JScriptRESTDataOperations._errorHandler(this));
    				}
    			}
    		};
    		req.send();
    
    	},
    	RetrieveMultiple: function (type, filter, successCallback, errorCallback)
    	{
    
    		if (filter != null)
    		{
    			filter = "?" + filter;
    		}
    		else
    		{ filter = ""; }
    
    		var req = new XMLHttpRequest();
    		req.open("GET", this._ODataPath() + type + "Set" + filter, true);
    		req.setRequestHeader("Accept", "application/json");
    		req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    		req.onreadystatechange = function ()
    		{
    			if (this.readyState == 4 /* complete */)
    			{
    				if (this.status == 200)
    				{
    					successCallback(JSON.parse(this.responseText, SDK.JScriptRESTDataOperations._dateReviver).d.results);
    				}
    				else
    				{
    					errorCallback(SDK.JScriptRESTDataOperations._errorHandler(this));
    				}
    			}
    
    		};
    		req.send();
    
    
    	},
    	__namespace: true
    };
    
    
    new_/SDK.JScriptRESTDataOperations.htm
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
     <title>JScript REST Data Operations</title>
     <script src="Scripts/SDK.JScriptRESTDataOperations.js" type="text/javascript"></script>
     <script type="text/javascript">
    
     var primaryContact = null;
    
     document.onreadystatechange = function ()
     {
     if (document.readyState == "complete")
     {
     getFirstContactToBePrimaryContact();
     document.getElementById("start").onclick = createAccount;
     }
     }
    
     function createAccount()
     {
     var account = {};
     account.Name = "Test Account Name";
     account.Description = "This account was created by the JScriptRESTDataOperations sample.";
     if (primaryContact != null)
     {
     //Set a lookup value
     writeMessage("Setting the primary contact to: " + primaryContact.FullName + ".");
     account.PrimaryContactId = { Id: primaryContact.ContactId, LogicalName: "contact", Name: primaryContact.FullName };
     
     }
     //Set a picklist value
     writeMessage("Setting Preferred Contact Method to E-mail.");
     account.PreferredContactMethodCode = { Value: 2 }; //E-mail
    
     //Set a money value
     writeMessage("Setting Annual Revenue to Two Million .");
     account.Revenue = { Value: "2000000.00" }; //Set Annual Revenue
    
     //Set a Boolean value
     writeMessage("Setting Contact Method Phone to \"Do Not Allow\".");
     account.DoNotPhone = true; //Do Not Allow
    
     //Add Two Tasks
     var today = new Date();
     var startDate = new Date(today.getFullYear(), today.getMonth(), today.getDate()+3); //Set a date three days in the future.
     
     var LowPriTask = { Subject: "Low Priority Task", ScheduledStart: startDate, PriorityCode: { Value: 0} }; //Low Priority Task
     var HighPriTask = { Subject: "High Priority Task", ScheduledStart: startDate, PriorityCode: { Value: 2} }; //High Priority Task
     account.Account_Tasks = [LowPriTask, HighPriTask]
     
    
     //Create the Account
     SDK.JScriptRESTDataOperations.Create(
      account,
      "Account",
      function (account)
      {
      writeMessage("The account named \"" + account.Name + "\" was created with the AccountId : \"" + account.AccountId + "\".");
      writeMessage("Retrieving account with the AccountId: \"" + account.AccountId + "\".");
      retrieveAccount(account.AccountId)
      },
      errorHandler
     );
      this.setAttribute("disabled", "disabled");
    
     }
    
     function retrieveAccount(AccountId)
     {
     SDK.JScriptRESTDataOperations.Retrieve(
      AccountId,
      "Account",
      function (account)
      {
      writeMessage("Retrieved the account named \"" + account.Name + "\". This account was created on : \"" + account.CreatedOn + "\".");
      updateAccount(AccountId);
      },
      errorHandler
     );
     }
    
     function updateAccount(AccountId)
     {
     var account = {};
     writeMessage("Changing the account Name to \"Updated Account Name\".");
     account.Name = "Updated Account Name";
     writeMessage("Adding Address information");
     account.Address1_AddressTypeCode = { Value: 3 }; //Address 1: Address Type = Primary
     account.Address1_City = "Sammamish";
     account.Address1_Line1 = "123 Maple St.";
     account.Address1_PostalCode = "98074";
     account.Address1_StateOrProvince = "WA";
     writeMessage("Setting E-Mail address");
     account.EMailAddress1 = "someone@microsoft.com";
    
    
     SDK.JScriptRESTDataOperations.Update(
      AccountId,
      account,
      "Account",
      function ()
      {
      writeMessage("The account Name was updated to \"Updated Account Name\".");
      deleteAccount(AccountId);
      },
      errorHandler
     );
     }
    
     function deleteAccount(AccountId)
     {
     if (confirm("Do you want to delete this account record?"))
     {
     writeMessage("You chose to delete the account record.");
     SDK.JScriptRESTDataOperations.Delete(
      AccountId,
      "Account",
      function () { writeMessage("The account was deleted."); },
      errorHandler
      );
     }
     else
     {
     var li = document.createElement("li");
    
     var span = document.createElement("span");
     span.innerText = "You chose not to delete the record. You can view the record ";
    
     var link = document.createElement("a");
     link.href = SDK.JScriptRESTDataOperations._getServerUrl() + "/main.aspx?etc=1&id=%7b" + AccountId + "%7d&pagetype=entityrecord";
     link.target = "_blank";
     link.innerText = "here";
    
     li.appendChild(span);
     li.appendChild(link);
     document.getElementById("results").appendChild(li);
     }
     }
    
     function getFirstContactToBePrimaryContact()
     {
     SDK.JScriptRESTDataOperations.RetrieveMultiple(
      "Contact",
      "$select=ContactId,FullName&$top=1",
      function (results)
      {
      var firstResult = results[0];
      if (firstResult != null)
      {
      primaryContact = results[0];
      }
      else
      {
      writeMessage("No Contact records are available to set as the primary contact for the account.");
      }
      },
      errorHandler
     );
     }
    
     function errorHandler(error)
     {
     writeMessage(error.message); 
     }
    
     //Helper function to write data to this page:
     function writeMessage(message)
     {
     var li = document.createElement("li");
     li.innerText = message;
     document.getElementById("results").appendChild(li);
     }
     </script>
    </head>
    <body>
    <button id="start" title="Click this button to start the sample.">Start</button>
    <ol id="results"></ol>
    </body>
    </html>
    
    
    
    
    After you create both web resources and publish your customizations, Preview the HTML web resource and press the START button.
     The SDK.JScriptRESTOperations.js library provides more generic re-usable functions for Create, Update, Retrieve, Delete, and RetrieveMultiple. You can use them with any type of entity. You can use this library from form scripts.
    The HTML Web Resource in this case is just staging the sequence of Create, Retrieve, Update and Delete operations. It controls the actions and uses the JScript library functions to just perform the actions.
    The JavaScript in the HTML web resource also shows how to set values for various types of data, Optionsets, Money, Dates, and Lookups (Entity References). It also shows a Deep Insert example by creating two associated tasks with the creation of the account record.
    One of the big differences you will see in these samples compared to the CRM 4 samples is that these are Asynchronous. This helps prevent the browser from freezing up when these requests are executed and it is really important to get in a good habit to always use these calls asynchronously. You can do them synchronously, but it is just not a good idea.
    With these asynchronous methods you see how having call back functions becomes important. The SDK.JScriptRESTOperations library consistently provides a SuccessCallback and an errorCallback function.
    There are lots of different ways you might structure your JScript libraries, but this is my preferred pattern so far.
    Also note this sample uses more anonymous functions and literal notation than the previous sample. '{}' is the same as new Object(). '[]' is the same as new Array();
    Note I haven't included the json2.js library. This is only necessary when people are using IE7. IE8 and higher have built-in support for the JSON object (as long as the page has a proper DOCTYPE declaration to make sure the browser doesn't revert to Quirks mode).
    This sample is my starting point for re-writing the original. It doesn't have a ton of comments in it, but I hope it will make sense. Let me know if you have any questions.
    Best regards,

    Jim Daly Technical Writer Microsoft Dynamics CRM
    Friday, August 19, 2011 6:18 AM
    Answerer
  • I did add json2.js.
    Amreek singh Senior CRM Consultant CDC Praxa Sydney,Australia http://mscrmshop.blogspot.com/
    Friday, August 19, 2011 1:52 PM
  • Dear Jim, thanks alot for your support but I'm afraid that the first version was more readable !!

    Also I have another question....for a real world requirements if I need to create an Opp Product and to populate some custom fields based on the selected product....how would this .htm file ?? I think that I have to merge the the script in the .htm file and the .js script file

     

    Please correct me if I'm wrong

     

    thanks alot for your help..

    Sunday, August 21, 2011 9:40 AM
  • Hi Mostafa,

     

    Before asking another question…, maybe it is time to give the people that are helping you some credit for their work by Marking as Answer or Voting as Helpful.

    I noticed you hardly ever do.

     

    Regards,

    Jan
    Sunday, August 21, 2011 12:28 PM
  • Mostafa -
    In CRM 4 you defined a single JScript event handler for form events. That is all you could do. You didn't have JScript libraries.  You weren't expected to define functions. Your options were very limited. I think you are confused applying CRM 4 thinking to CRM 2011. I strongly recommend you read the conceptual parts of the documentation JScript Libraries for Microsoft Dynamics CRM, Write Code for Microsoft Dynamics CRM Forms; GetGlobalContext function. You may also need to simply learn more about using more advanced concepts for JScript in general. With this greater level of complexity, I can't provide all you need to know in the SDK or in a forum response. The article Create Advanced Web Applications With Object-Oriented Techniques from MSDN Magazine (2007) is a good introduction to some of these more advanced techniques.

    While the original script might be more readable from a procedural perspective, it is actually not a good example because it doesn't represent a good real-world usage. A better solution is to create and re-use a library for your commonly performed operations. You do not need to merge the HTML and the JScript library - you want to use the SDK.JScriptRESTDataOperations.js library and then create your own JScript library with functions that use the functions defined within SDK.JScriptRESTDataOperations.js. You can have multiple libraries and functions now in form events that you could never have in CRM 4.

    In CRM 2011 you have JScript Libraries and HTML web resources and you can configure multiple event handlers for form events. You have many more options. This flexibility makes it much more like creating your own application. You will typically break your JScript libraries out by function.

    The SDK.JScriptRESTDataOperations.js library I provided above is an all purpose base library for all the supported actions for the REST endpoint. If you want to use that library within an HTML web resource, you must include a <script> element to include it in the HTML page as I did in the SDK.JScriptRESTDataOperations.htm file. Once that file is loaded, you can create scripts that use it.

    For form scripts, you have to make sure to include the SDK.JScriptRESTDataOperations.js library in the form by registering it. Then the functions it provides will be available within your functions for any other event in the page. You will typically create your own JScript Web Resource for the code you use to work with new records.

    To use the SDK.JScriptRESTDataOperations.js library, review it and note that there are 5 'public' functions (Create, Retrieve, Update, Delete, and RetrieveMultiple). The other functions use the underscore character to indicate that they are intended to be 'private' - but there is not really the concept of a private function in JScript.

    Each of these functions requires the type parameter to contain the Entity Schema Name to be passed for the operation. This is what allows you to use this one library to work with records from any type of entity. If you want to create an Opportunity Product record, the Schema Name is 'OpportunityProduct'.

    To create or update a record, you need to define it and pass it as the object parameter. Note how the onclick event handler for the Start button in the HTML web resource uses this code to create a new Account record: (I've removed references to all the calls to the writeMessage function since they don't contribute to creating the record)

    The sample contains a global primaryContact variable that is set when the page loads using the getFirstContactToBePrimaryContact function in the HTML web resource. This way you will have a valid reference to a primary contact that is used to show how to set a lookup value.

    var account = {};
      account.Name = "Test Account Name";
      account.Description = "This account was created by the JScriptRESTDataOperations sample.";
      if (primaryContact != null)
      {
      //Set a lookup value
        account.PrimaryContactId = { Id: primaryContact.ContactId, LogicalName: "contact", Name: primaryContact.FullName };
      
      }
      //Set a picklist value
      account.PreferredContactMethodCode = { Value: 2 }; //E-mail
    
      //Set a money value
       account.Revenue = { Value: "2000000.00" }; //Set Annual Revenue
    
      //Set a Boolean value
       account.DoNotPhone = true; //Do Not Allow
    
      //Add Two Tasks
      var today = new Date();
      var startDate = new Date(today.getFullYear(), today.getMonth(), today.getDate()+3); //Set a date three days in the future.
      
      var LowPriTask = { Subject: "Low Priority Task", ScheduledStart: startDate, PriorityCode: { Value: 0} }; //Low Priority Task
      var HighPriTask = { Subject: "High Priority Task", ScheduledStart: startDate, PriorityCode: { Value: 2} }; //High Priority Task
      account.Account_Tasks = [LowPriTask, HighPriTask]
      
    
      //Create the Account
      SDK.JScriptRESTDataOperations.Create(
       account,
       "Account",
       function (account)
       {
       // Code you want to have happen after the account is created will go in this anonymous function.
    
       },
       errorHandler //In this sample, the function just uses the writeMessage function to show the text of the error
    
       );
    

    So, I think you can see that you just need to specify the object property values using the schemaName for each of the properties. Certain types of attributes (Lookup, Picklist, Money, Boolean & Dates) need special handling, but the rest can be handled simply like String attributes.

    Note that I've also included a sample of creating associated Task records using the Deep Insert method. This is just something that the REST endpoint allows.

    The other two parameters in the SDK.JScriptRESTDataOperations.js library are the successCallback and the errorCallback. You may note that in the HTML page I use anonymous functions. This for the create function the successCallback is passed a reference to the object that was created. You can then add code to the function you assign as the successCallback to do what you need, for example, to assign a value from the Opportunity Product record to the custom form field you mention. You don't have to use anonymous functions - but it just makes your code more efficient. You can see how I didn't use anonymous functions for the errorHandler function because I re-used that function several times.

    As a final note, when you specify entity attributes, you must use the attribute SchemaName (not Logical Name). You also need to know the integer option values when setting Picklists. I rely on the Entity Metadata Browser in the SDK to make sure I get the name of the attribute correct and the proper Picklist option value.

    I hope this helps.

    I am re-visiting the SDK documentation with hope of adding more content specifically to address issues like I believe you are experiencing. So I appreciate hearing your questions.

    Best regards,


    Jim Daly Technical Writer Microsoft Dynamics CRM
    Sunday, August 21, 2011 6:24 PM
    Answerer
  • Dear Jim,

    I really appreciate your efforts to help beginners with CRM 2011 developers,.

     

    Now I can understand how much CRM 2011 web resources are flexible, but if your .JS library is more simple, just for readability and understandability purpose it would be perfect.

    as a side note: It was nice to know that finally there is a way to use intellisense while creating JS libraries for CRM
    Tuesday, August 23, 2011 10:07 AM
  • Dear Amreek,

    I have used the code above and here the sequence of meesage that appears:

    1 - "retrieveAccountRecord function START"

    2 - "inside retrieveAccountReqCallBack"

    3 - "retrieveAccountRecord function END."

    4 - "inside retrieveAccountReqCallBack"

    5 - "inside retrieveAccountReqCallBack"

    6 - "inside retrieveAccountReqCallBack"

    7 - "inside if condition"

    and then an error in page

     

    if you could help me solve the problem so that the script work as you mentioned above it will be appreciated

    Tuesday, August 23, 2011 12:19 PM
  • Dear Jim, I know that I'm bothering you :$

    But I'm still little bit confused about creating a JS Library that use "SDK.JScriptRESTDataOperations.js" that already uses "SDK.JScriptRESTDataOperations.htm" !

     

    First of all, I have created the two web resources... then I have created a third web resource "RetrieveAccountTest.js" as follow:

    /// <reference path="XrmPageTemplate.js" />
    
    function Function1()
    {
      var id = Xrm.Page.data.entity.getId();
      Retrieve(id, "account");
    }
    

    I hve added the "SDK.JScriptRESTDataOperations.js" and "RetrieveAccountTest.js" to the account entity but I don't know how to add the htm file to the form to be used by the JS file ??

    I have added  "RetrieveAccountTest.js" to the onLoad event and called the Function1()

     

    When I try to re-load an account an "error in page" ocured....Am not wondering why but I really need help to solve it and to complete the parameter list of Retrieve() function

    Wednesday, August 24, 2011 10:23 AM
  • No worries :-)

    Understand that the CRM form is the HTML page you are working with when you are doing form scripting. The SDK.JScriptRESTOperations.js library can be used either in a form event or as a script file included in an HTML Web resource. If you are working with the CRM form - you don't need the HTML page.

    You can register up to 50 JScript libraries to a form. For a form event handler, you can choose to call any of the functions in any of those libraries. The functions in those libraries are free to call any other function the same library or another library as long as they are all loaded into the page.

    Also understand that the SDK.JScriptRESTOperations.js library uses a namespacing strategy to help achieve unique names for the functions. So I if you want to use the Retrieve function you must use the fully qualified name for it. 'SDK.JScriptRESTDataOperations.Retreive'. You must also pass in the required parameters (id, type, successCallback, errorCallback).

    BTW -  I see you have the //// <reference path="XrmPageTemplate.js" /> at the top of your script that indicates you are using the Xrm.Page Script Library Template  from the SDK. If you were to add a new JScript file to your Visual Studio solution with the contents of SDK.JScriptRESTOperations.js in it - then you could drag that file onto your RetrieveAccountTest.js and you would see  //// <reference path="SDK.JScriptRESTOperations.js" /> at the top of the page and you should then have IntelliSense support for the functions in that library.

    Your RetrieveAccountTest.js library might look like this:

    function Function1()
    {
     var id = Xrm.Page.data.entity.getId();
     SDK.JScriptRestOperations.Retrieve(id, "Account", Function2, function(error){alert(error.message);});
    }
    
    function Function2(returnedAccount)
    {
     alert(returnedAccount.Name);
    }
    
    

     

    You need two functions to do this asynchronously. So in this example Function2 is the successCallback and an anonymous function is created for the ErrorCallback. You only need to register Function1 for the form event. Since it asynchronously calls Function2 as the SuccessCallback, the actions in Function2 will be performed (or the error message will be displayed by the anonymous function).

    I know you want to do this synchronously using the style of the CRM 4 SOAP examples, but that is not wise. All the samples in the SDK will show the asynchronous pattern. In hindsight, we should have made the CRM 4 samples asynchronous but that could only have been done using anonymous functions - and those samples were complicated enough already. In CRM 4 we didn't allow you to define functions, only the contents of event handler functions we already provided. Just think of the successCallback as a continuation of the originating function that contains the logic you would have included in the originating function if the action had succeeeded. It is more complex, but with some practice you get used to it.

    The tricky thing can be when you need access to an object that only exists within the scope of the originating function. Two strategies to address that:

    1. Set that value as a global variable
    2. Modify the SDK.JScriptRESTDataOperations.js library to add a PassThroughObject parameter that you will then pass through to the successCallBack.

    But perhaps I'm getting ahead of myself. That may be a topic for another post.

    BTW - the type argument to the SDK.JScriptRESTOperations functions must be the SchemaName, "Account", not "account". The JSON results you get back will use the Schema Name rather than the Logical name, so the results are "returnedAccount.Name", not "returnedAccount.name".  Use the Entity Metadata Browser to know the proper Schema Names for entities and attributes.

    Of course, this example just retrieves the current form record from the database, but you could apply the same pattern to retrieve a related record.

    I hope this helps.


    Jim Daly Technical Writer Microsoft Dynamics CRM
    Wednesday, August 24, 2011 4:50 PM
    Answerer
  • Dear Jim, I have tried exactly what you say, but the "error in page" still appears and the ribbon is not displayed
    Thursday, August 25, 2011 11:14 AM
  • Mostafa -

    Hmmm. I guess I need details. What code is in your JScript libraries? (exact code)

    What entity? What form event?

    You really need to look into the information about debugging scripts. With only information like "Error in page" of course you don't know what to do to fix it.

     

    Debugging Scripts

    With Internet Explorer 8 and JScript libraries, debugging scripts has become much easier.

    To debug script in Microsoft Dynamics CRM

    1. While working with Microsoft Dynamics CRM try to reproduce the conditions where an error is occurring by pressing F12 to open the Internet Explorer developer tools.

    2. On the Script tab, to the right of the Start Debugging button, use the drop-down list to locate your JScript library.

    3. Set a breakpoint by clicking the left margin in your function.

    4. Click Start Debugging.

    5. If your script is in the Onload event, you may have to select the Microsoft Dynamics CRM window and press F5 to reload the window.

    For more information, see Debugging Script with Developer Tools.

    Also see http://social.technet.microsoft.com/wiki/contents/articles/how-to-debug-jscript-in-microsoft-dynamics-crm-2011.aspx


    Jim Daly Technical Writer Microsoft Dynamics CRM
    Thursday, August 25, 2011 3:25 PM
    Answerer
  • I think I see the problem. My bad.
    The snippet I provided didn't specify the name of the function correctly.
    Use this instead for your RetrieveAccountTest.js library

    function Function1() {
     var id = Xrm.Page.data.entity.getId();
     
     SDK.JScriptRESTDataOperations.Retrieve(id, "Account", Function2, function (error) { alert(error.message); });
    }
    
    function Function2(returnedAccount) {
     alert(returnedAccount.Name);
    }
    

     My previous snippet used "JScriptRestOperations"  - and JavaScript is case sensitive.

    One more thing - when I tested this in the CRM Form, I found I got a JSON is not defined error..
    I had to create another JScript library containing the contents of the JSON2.js file and include it with the form libraries.
     

    Jim Daly Technical Writer Microsoft Dynamics CRM


    Thursday, August 25, 2011 5:58 PM
    Answerer
  • After using the new script the error persist, noting that I runing the script in the onLoad event of the Account form.

     

    I tried to debug usign the instructions above and it displays the error in the below statement:

    if(lookupstyle.toLowerCase()=="single"||lookupstyle.toLowerCase()=="subject")
    

    please advise...

    Sunday, August 28, 2011 10:30 AM
  • I don't recogize that code.

    When you choose to debug the script with the IE Developer tools, it should break on the code that is causing an error. There it should show you what library the code is in.

    What library is the problem code in?

    Perhaps you have another library attached to the account form?

    Remove any libraries from the form properties except:

    1. The one that contains the SDK.JScriptRESTOperations.js

    2. The json2.js libary

    3. Your RetrieveAccountTest.js.

    Make sure your Function1 is registered for the onload event, Then

    1. Edit and update your RetrieveAccountTest.js JScript library like so:

    function Function1() {
    
    alert ("Function 1 Begin");
     var id = Xrm.Page.data.entity.getId();
     
    SDK.JScriptRESTDataOperations.Retrieve(id, "Account", Function2, function (error) { alert(error.message); });
    }
    
    function Function2(returnedAccount) {
    
    alert(Function2 begin);
     alert(returnedAccount.Name);
    }
    
    
    

    Using alerts for debugging is very primitive, but it is simple. As you become more familar with using the IE Developer tools - there are lots of better ways to debug scripts, but I can't get into that in this forum.

    After you publish your customizations and open a form to trigger the onload event. What happens first?

    Do you see the alert that Function1 began? Or does the other error occur?

    I don't think the problem is in the code to use the CRUD operations. It may be somewhere else. Finding it is a process of elimination.

     


    Jim Daly Technical Writer Microsoft Dynamics CRM
    Monday, August 29, 2011 4:15 AM
    Answerer
  • Dear Jim, the error occurs before displaying any messages !!!

     

    as a side note how shoud I refer to Function1() in the Function field ??

    when I use Function1 it displays: The value of the property 'Function1' is null or undefined, not a Function object error, when I use Function1() it displays the error on page although the migrated scripts in all forms uses Form_onload and Form_onsave

     

    I know that I'm causing a big headache and somebody like you is for sure very busy but I'm really feeling that there is something weird is going on here

     

    thanks and best regards

    Monday, August 29, 2011 9:41 AM
  • Don't include the parentheses in the function field when you register the function for the event. You want to refer to the function as an object - not to actual call the function. The event handler will do that for you.

    When an error refers to a function saying it is null or undefined, that is just the way the page says "What is this thing?". Perhaps the name is not correct (remember that JScript is case sensitive) or sometimes there is a syntax errror in the function so that it doesn't get fully defined.

    Since the error occurs before the function is called, it is likely to be an issue with the way the library is defined. The problem may be with one of the migrated scripts.

    Debugging JScript in the CRM form is a pain. Since I know you have the Xrm.Page Script Library template I would recommend using that as the first level of testing to make sure that there are no syntax errors in your functions. If you have CRM On-Premises, you can do some testing for XMLHttpRequests outside of Microsoft CRM. As noted in the Readme.docx for the template:

    ·         You cannot test OData or SOAP XMLHttpRequests using Microsoft Dynamics CRM Online with the Xrm.Page Script Library Template.

    o    If you add your local development server (or localhost) to a trusted group within your Internet Explorer security settings and then configure that trusted group to allow cross-domain requests you may be able to test XMLHttpRequests with Microsoft Dynamics CRM On-Premises or other Web sites or Web services that support these requests.

    When testing, you may be prompted to enter your credentials if you access your CRM web site using credentials other than the one you are currently using.

    After you verify that your JScript library is free of syntax errors and that it works in the Xrm.Page Script Library Template environment, you can then add it to a form event and test it within the applicaiton.

    I would definately try removing the migrated form script libraries and see if the errors still occur.


    Jim Daly Technical Writer Microsoft Dynamics CRM
    Monday, August 29, 2011 4:47 PM
    Answerer
  • Dear Jim, I have already removed all migrated scripts however can you see any syntax errors in my function ??

     

    thanks and best regards...

    Monday, August 29, 2011 7:58 PM
  • Please paste the contents of the entire JScript library containing your function:

    (if it is included in a larger library, please break it out so that you are only testing the topic of this conversation (the CRUD Operations) :-)

    If you are editing the functions in Visual Studio, you should see red squigglies where there are syntax isssues.


    Jim Daly Technical Writer Microsoft Dynamics CRM
    Monday, August 29, 2011 8:11 PM
    Answerer
  • Dear Jim,

    This is all the contents of the library:

     

    function Function1() {
    
    alert ("Function 1 Begin");
     var id = Xrm.Page.data.entity.getId();
     
    SDK.JScriptRESTDataOperations.Retrieve(id, "Account", Function2, function (error) { alert(error.message); });
    }
    
    function Function2(returnedAccount) {
    
    alert(Function2 begin);
     alert(returnedAccount.Name);
    }

    and yes I'm editing the function in visual studio and there is no red squigglies :)

    thanks and best regards..

     

    Monday, August 29, 2011 8:16 PM
  • Dear Jim, sorry for disturbance,↲Any updates about my problem ?↲Thanks and best regards

    Wednesday, August 31, 2011 1:26 PM
  • Mostafa -

    I responded to your post because you indicated problems with the CRUD samples, but based on your responses it appears that the CRUD samples are not the problem.

    Based what you have told me so far I understand these steps reproduce the issue you are experiencing:

    1. Remove all Jscript libraries from a form.

    2. Add a single library with a single function:

    function MyFunction()
    {
     alert("test");
    }
    

    3. Configure the onload event handler to call the MyFunction function

    4. Save and close, and publish customizations

    5. Open an entity form.

    Expected Results: An alert displays "test".

    Actual Results: An error occurs related to the statement below:

    if(lookupstyle.toLowerCase()=="single"||lookupstyle.toLowerCase()=="subject")
    

    If this is the case, we have factored out the CRUD operation samples and there is some other issue.

     - My suspicion is that you have some unsupported code from CRM 4 that is still somehow being run in the form. Perhaps someone has altered the Global.js file? Perhaps not all the upgraded code has been removed?

    Unfortunatly, without access to your environment I'm not able to see what this underlying issue may be. 

    If the repro steps I've listed above describe what you are experiencing, I recommend you post a new question on the forum. Perhaps someone else can provide better guidance about the root cause of that problem.

    Once this issue is fixed, I'm confident that you will be able to run the CRUD samples. I have tested them and I know that they work.


    Jim Daly Technical Writer Microsoft Dynamics CRM
    Wednesday, August 31, 2011 4:42 PM
    Answerer
  • DearJim,

     

    Actually for the above steps everything goes well and the result are: "test"

    But the problem occurs when the "SDK.JScriptRESTDataOperations.js", "RetrieveAccountTest.js" and the "JSON2.js" libraries are added and the onLoad event is configured to call the Function1

     

    best regards.. 

    Wednesday, August 31, 2011 9:37 PM
  • Hello Daniel, would you please go straight to the point, as I don't know what am I supposed to do after this great speech !!
    Thursday, September 1, 2011 1:42 AM
  • Mostafa,

    We have certainly been here before, and though your inquiries have troubled and frustrated me personally in the past, I have noticed improvement on your part toward making better use of this forum as an informational and educational resource.  I have unflagged Dan's post as abusive for these reasons:

    1. He did not discriminate against you in any racial, sexist, or profane manner;
    2. I believe he honestly believes the things he has posted with earnest conviction, and opinions with which you disagree are not grounds for censorship; and
    3. Dan is empathetic to Jim's efforts, and though Jim may not agree with Dan's opinion (in whole or part), Dan is quite obviously astounded at the level of dedication that Jim--who does not frequent these forums as a contributor--in assessing the cause of trouble that you have with the SDK samples.  This is based on my reading of this thread, which again is my own opinion on the matter.

    Suffice it to say that I agree with Dan that Jim has been more than reasonably committed to coming to your aid in working with the SDK sample code, and I also feel that Jim has made unsurpassed efforts to help you discover the root of the problem.  We also seem to be agreed that the cause of the problem must be so far removed from the SDK documentation that Jim's herculean, magnanimous efforts in assisting you appear to be to the detriment of his valuable time, rather than the benefit of the product or the SDK documentation.  However, the commitment is Jim's to make.  Simply know that you have spent half of a month in the direct, personal limelight of one of Microsoft's best resources, and Daniel feels that the effort was wasted.

    That is not a condemnation to be taken lightly, as Daniel is a very highly regarded resource in this community and has personally produced more helpful utilities for CRM than any other contributor I know.  If his opinion is that Jim's time has been wasted, it deserves a gravity that can only be implied and never succinctly defined in words.  Now, Daniel doesn't exactly have a right to step in on anyone's behalf and condemn you, and he certainly isn't speaking for Jim.  His participation in this thread adds no value to it, and to that end I hope he finds this message and considers a friendly counsel against such additional outbursts in the future.  I will not condemn him for his right to express his opinion (certainly when he has not violated any standing rules of participation), nor will I myself condemn you in a similar fashion.

    You and I have crossed paths before, and when I took strong words with you in the past, I did so with a commitment to assist you, hoping that my example would lead others who have such strong opinions about the contributions of this dedicated community to perform similarly.  In the future, I hope Daniel will take these words to heart:  it is not sufficient to chide or reprimand those in error, without guidance and assistance to be correct; else, nothing has been accomplished but to satisfy ones self by dubious catharsis.  Such is wholly unbecoming of any kind of leader.


    Dave Berry - MVP Dynamics CRM - http:\\crmentropy.blogspot.com Please follow the forum guidelines when inquiring of the dedicated CRM community for assistance.
    Thursday, September 1, 2011 5:14 AM
    Moderator
  • Dears,

     

    First of all I would like to thank you all for all your great support for a year and half of my work with CRM.

    Reagarding wasting Jim precious time to solve my problem: please note that I haven't forced him to do it, he did it as he wish, so when you post a response for this thread please take this into considerations and do not blame me for wasting his time solving a problem posted in a forum that has been created to solve people problems !!!

     

    The last thing: when you don't know the answer for a problem then please say: "I don't know", I think it's more easier than wasting your time writing these speeches and wasting my time reading it

     

    thanks and best regards :)

    Thursday, September 1, 2011 8:04 PM
  • Daniel,

    If you are trying to make some suggestions about how to assist each other by best leveraging the available resource and effort, I think my post is not the best place to do this !

    My calling for Jim name was normal as I was replying to posts he did as he wish !!

    You can't post something and ask me to not reply it specially if it was wrong !!!

     

    thanks

    Thursday, September 1, 2011 8:19 PM
  • Hello, sorry for disturbance,

     

    I know that this thread should have been terminated while ago... But I'm just letting you know that finally I have found a solution for my problem, simply the RetireveAccount.js code should be as follow:

    function Function1() 
    {
     var id = Xrm.Page.data.entity.getId();
    SDK.JScriptRESTDataOperations.Retrieve(id, "Account", Function2, function (error) { alert(error.message); });
    }
    
    function Function2(returnedAccount) 
    {
     alert(returnedAccount.Name);
    }

    thanks and best regards..

    Monday, September 5, 2011 9:48 AM
  • It is always appreciated when the original poster returns with an affirmation of the complete resolution of the problem and any additional steps that were taken to get there.  Thank you for following through.
    Dave Berry - MVP Dynamics CRM - http:\\crmentropy.blogspot.com Please follow the forum guidelines when inquiring of the dedicated CRM community for assistance.
    Tuesday, September 6, 2011 6:58 PM
    Moderator
  • Dears Colleagues,

    I would like to thank you all for your nice feedback, however this code was not worked out completely by my own, it's based on the library created by Jim in addition to the discovered error by Amreek in the following thread:

    http://social.microsoft.com/Forums/en-US/crmdevelopment/thread/68a0106a-f3c9-47e4-9b02-7f6f9ff16f44

    I'm posting it here for anybody who's interested in following my progress with creating javascript code samples

     

    Now I think it's time to terminate this thread for the last time.

     

    Thanks and best regards

    Wednesday, September 7, 2011 9:35 AM
  • I am trying this now for a few days and can't figure it out. It seems to break at the JSON object when calling

    successCallback(JSON.parse(this.responseText, SDK.JScriptRESTDataOperations._dateReviver).d.results);

    I am using IE9. DOCTYPE is declared in the web resource. In another thread I saw that people used it with this.parents.JSON but in my case parents is not defined. I then copied the code from https://github.com/douglascrockford/JSON-js/blob/master/json2.js, put it in another JScript web resource and included it in the HTML web resource like this  

    <SCRIPT type=text/javascript src="./new_json2"></SCRIPT>

    It still breaks at the same place and it seems still because of JSON. So what am I doing wrong?

    Btw, the way I am trying to debug this is with alert statements. It seems like going back to the old days of programming. Is there a better way to actually step through html/script and examine the values of the object?

    Sunday, September 18, 2011 6:08 PM
  • Actually I'm using the above code with CRM, if you are doing the same let me know to help you, otherwise I'm sorry but I won't be able to do

    thanks

    Sunday, September 18, 2011 8:01 PM
  • Ok, I started from scratch again. I named the files a bit different so had to adjust the script tag to <SCRIPT type=text/javascript src="./new_SDK.JScriptRESTDataOperations"></SCRIPT>. Note the missing .js. I had to remove that in order to work. I then had the error:

    "Object doesn’t support property or method ‘getServerUrl’

    I fixed that by adding the following to the html web  resource

    <SCRIPT src="./ClientGlobalContext.js.aspx"></SCRIPT>

    With that the Preview did load without an error showing the Start button. I then added the html web resource to the account property. However, at this point the callback to RetrieveMultiple is never being called. I am saying this because in the code below I only see the message "Before RetrieveMultiple", "After RetrieveMultiple", but never "RetrieveMultiple Callback Enter". I then added another alert in the RetrieveMultiple function and I get the popup "Before JSON" but not "After JSON". 

    Excerpt from RetrieveMultiple:
      req.onreadystatechange = function ()
      {
       if (this.readyState == 4 /* complete */)
       {
        if (this.status == 200)
        {
    alert("Before JSON");
         successCallback(JSON.parse(this.responseText, SDK.JScriptRESTDataOperations._dateReviver).d.results);
    alert("After JSON");
        }
        else
        {
         errorCallback(SDK.JScriptRESTDataOperations._errorHandler(this));
        }
       }

      };

     function getFirstContactToBePrimaryContact()
     {
    alert("Before RetrieveMultiple");
     SDK.JScriptRESTDataOperations.RetrieveMultiple(
      "Contact",
      "$select=ContactId,FullName&$top=1",
      function (results)
      {
    alert("RetrieveMultiple Callback Enter");
      var firstResult = results[0];
      if (firstResult != null)
      {
      primaryContact = results[0];
      }
      else
      {
      writeMessage("No Contact records are available to set as the primary contact for the account.");
      }
    alert("RetrieveMultiple Callback Exit");
      },
      errorHandler
     );
    alert("After RetrieveMultiple");
     }

    Sunday, September 18, 2011 9:10 PM
  • I've placed a managed solution named JavaScriptRESTDataOperations_1_0_0_0_managed.zip on my skydrive at : https://skydrive.live.com/redir.aspx?cid=6da54d3e37b39948&resid=6DA54D3E37B39948!186

    Just install the Managed solution by importing it. On the Configuration page of the JavaScript REST Data Operations managed solution you will see a Start button to start the sample.

    When you run the sample you should see output like this when the first contact record retrieved is Adrian Dumitrascu (sample). You will have the option to delete the account record created or you can choose not to - then a link will be displayed so that you can view the record you created.

    1. Setting the primary contact to: Adrian Dumitrascu (sample).
    2. Setting Preferred Contact Method to E-mail.
    3. Setting Annual Revenue to Two Million .
    4. Setting Contact Method Phone to "Do Not Allow".
    5. The account named "Test Account Name" was created with the AccountId : "f0c2110f-4ae2-e011-a894-00155d01680c".
    6. Retrieving account with the AccountId: "f0c2110f-4ae2-e011-a894-00155d01680c".
    7. Retrieved the account named "Test Account Name". This account was created on : "Sun Sep 18 16:00:53 PDT 2011".
    8. Changing the account Name to "Updated Account Name".
    9. Adding Address information
    10. Setting E-Mail address
    11. The account Name was updated to "Updated Account Name".
    12. You chose to delete the account record.
    13. The account was deleted.

    After you run the sample, you can click the Reset button to run it again.

    This managed solution contains two Web Resources:

    jdaly_/Scripts/SDK.JScriptRESTDataOperations.js

     

    if (typeof (SDK) == "undefined")
    { SDK = { __namespace: true }; }
    SDK.JScriptRESTDataOperations = {
    	_context: function ()
    	{
    		if (typeof GetGlobalContext != "undefined")
    		{ return GetGlobalContext(); }
    		else
    		{
    			if (typeof Xrm != "undefined")
    			{
    				return Xrm.Page.context;
    			}
    			else
    			{ return new Error("Context is not available."); }
    		}
    	},
    	_getServerUrl: function ()
    	{
    		var serverUrl = this._context().getServerUrl()
    		if (serverUrl.match(/\/$/))
    		{
    			serverUrl = serverUrl.substring(0, serverUrl.length - 1);
    		}
    		return serverUrl;
    	},
    	_ODataPath: function ()
    	{
    		return this._getServerUrl() + "/XRMServices/2011/OrganizationData.svc/";
    	},
    	_errorHandler: function (req)
    	{
    		return new Error("Error : " +
            req.status + ": " +
            req.statusText + ": " +
            JSON.parse(req.responseText).error.message.value);
    	},
    	_dateReviver: function (key, value)
    	{
    		var a;
    		if (typeof value === 'string')
    		{
    			a = /Date\(([-+]?\d+)\)/.exec(value);
    			if (a)
    			{
    				return new Date(parseInt(value.replace("/Date(", "").replace(")/", ""), 10));
    			}
    		}
    		return value;
    	},
    	Create: function (object, type, successCallback, errorCallback)
    	{
    		var req = new XMLHttpRequest();
    		req.open("POST", this._ODataPath() + type + "Set", true);
    		req.setRequestHeader("Accept", "application/json");
    		req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    		req.onreadystatechange = function ()
    		{
    			if (this.readyState == 4 /* complete */)
    			{
    				if (this.status == 201)
    				{
    					successCallback(JSON.parse(this.responseText, SDK.JScriptRESTDataOperations._dateReviver).d);
    				}
    				else
    				{
    					errorCallback(SDK.JScriptRESTDataOperations._errorHandler(this));
    				}
    			}
    		};
    		req.send(JSON.stringify(object));
    	},
    	Retrieve: function (id, type, successCallback, errorCallback)
    	{
    		var req = new XMLHttpRequest();
    		req.open("GET", this._ODataPath() + type + "Set(guid'" + id + "')", true);
    		req.setRequestHeader("Accept", "application/json");
    		req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    		req.onreadystatechange = function ()
    		{
    			if (this.readyState == 4 /* complete */)
    			{
    				if (this.status == 200)
    				{
    					successCallback(JSON.parse(this.responseText, SDK.JScriptRESTDataOperations._dateReviver).d);
    				}
    				else
    				{
    					errorCallback(SDK.JScriptRESTDataOperations._errorHandler(this));
    				}
    			}
    		};
    		req.send();
    	},
    	Update: function (id, object, type, successCallback, errorCallback)
    	{
    		var req = new XMLHttpRequest();
    
    		req.open("POST", this._ODataPath() + type + "Set(guid'" + id + "')", true);
    		req.setRequestHeader("Accept", "application/json");
    		req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    		req.setRequestHeader("X-HTTP-Method", "MERGE");
    		req.onreadystatechange = function ()
    		{
    			if (this.readyState == 4 /* complete */)
    			{
    				if (this.status == 204 || this.status == 1223)
    				{
    					successCallback();
    				}
    				else
    				{
    					errorCallback(SDK.JScriptRESTDataOperations._errorHandler(this));
    				}
    			}
    		};
    		req.send(JSON.stringify(object));
    	},
    	Delete: function (id, type, successCallback, errorCallback)
    	{
    		var req = new XMLHttpRequest();
    		req.open("POST", this._ODataPath() + type + "Set(guid'" + id + "')", true);
    		req.setRequestHeader("Accept", "application/json");
    		req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    		req.setRequestHeader("X-HTTP-Method", "DELETE");
    		req.onreadystatechange = function ()
    		{
    
    			if (this.readyState == 4 /* complete */)
    			{
    				if (this.status == 204 || this.status == 1223)
    				{
    					successCallback();
    				}
    				else
    				{
    					errorCallback(SDK.JScriptRESTDataOperations._errorHandler(this));
    				}
    			}
    		};
    		req.send();
    
    	},
    	RetrieveMultiple: function (type, filter, successCallback, errorCallback)
    	{
    
    		if (filter != null)
    		{
    			filter = "?" + filter;
    		}
    		else
    		{ filter = ""; }
    
    		var req = new XMLHttpRequest();
    		req.open("GET", this._ODataPath() + type + "Set" + filter, true);
    		req.setRequestHeader("Accept", "application/json");
    		req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    		req.onreadystatechange = function ()
    		{
    			if (this.readyState == 4 /* complete */)
    			{
    				if (this.status == 200)
    				{
    					successCallback(JSON.parse(this.responseText, SDK.JScriptRESTDataOperations._dateReviver).d.results);
    				}
    				else
    				{
    					errorCallback(SDK.JScriptRESTDataOperations._errorHandler(this));
    				}
    			}
    
    		};
    		req.send();
    
    
    	},
    	__namespace: true
    };
    
    

    jdaly_/TestPage.htm

     

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>JScript REST Data Operations</title>
     <script src="Scripts/SDK.JScriptRESTDataOperations.js" type="text/javascript"></script>
     <script src="../ClientGlobalContext.js.aspx"></script>
     <script type="text/javascript">
    
      var primaryContact = null;
    
      document.onreadystatechange = function ()
      {
       if (document.readyState == "complete")
       {
        getFirstContactToBePrimaryContact();
        document.getElementById("start").onclick = createAccount;
        document.getElementById("reset").onclick = resetSample;
       }
      }
    
      function createAccount()
      {
       var account = {};
       account.Name = "Test Account Name";
       account.Description = "This account was created by the JScriptRESTDataOperations sample.";
       if (primaryContact != null)
       {
        //Set a lookup value
        writeMessage("Setting the primary contact to: " + primaryContact.FullName + ".");
        account.PrimaryContactId = { Id: primaryContact.ContactId, LogicalName: "contact", Name: primaryContact.FullName };
    
       }
       //Set a picklist value
       writeMessage("Setting Preferred Contact Method to E-mail.");
       account.PreferredContactMethodCode = { Value: 2 }; //E-mail
    
       //Set a money value
       writeMessage("Setting Annual Revenue to Two Million .");
       account.Revenue = { Value: "2000000.00" }; //Set Annual Revenue
    
       //Set a Boolean value
       writeMessage("Setting Contact Method Phone to \"Do Not Allow\".");
       account.DoNotPhone = true; //Do Not Allow
    
       //Add Two Tasks
       var today = new Date();
       var startDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 3); //Set a date three days in the future.
    
       var LowPriTask = { Subject: "Low Priority Task", ScheduledStart: startDate, PriorityCode: { Value: 0} }; //Low Priority Task
       var HighPriTask = { Subject: "High Priority Task", ScheduledStart: startDate, PriorityCode: { Value: 2} }; //High Priority Task
       account.Account_Tasks = [LowPriTask, HighPriTask]
    
    
       //Create the Account
       SDK.JScriptRESTDataOperations.Create(
         account,
         "Account",
         function (account)
         {
          writeMessage("The account named \"" + account.Name + "\" was created with the AccountId : \"" + account.AccountId + "\".");
          writeMessage("Retrieving account with the AccountId: \"" + account.AccountId + "\".");
          retrieveAccount(account.AccountId)
         },
         errorHandler
       );
       this.setAttribute("disabled", "disabled");
      }
    
      function retrieveAccount(AccountId)
      {
       SDK.JScriptRESTDataOperations.Retrieve(
         AccountId,
         "Account",
         function (account)
         {
          writeMessage("Retrieved the account named \"" + account.Name + "\". This account was created on : \"" + account.CreatedOn + "\".");
          updateAccount(AccountId);
         },
         errorHandler
       );
      }
    
      function updateAccount(AccountId)
      {
       var account = {};
       writeMessage("Changing the account Name to \"Updated Account Name\".");
       account.Name = "Updated Account Name";
       writeMessage("Adding Address information");
       account.Address1_AddressTypeCode = { Value: 3 }; //Address 1: Address Type = Primary
       account.Address1_City = "Sammamish";
       account.Address1_Line1 = "123 Maple St.";
       account.Address1_PostalCode = "98074";
       account.Address1_StateOrProvince = "WA";
       writeMessage("Setting E-Mail address");
       account.EMailAddress1 = "someone@microsoft.com";
    
    
       SDK.JScriptRESTDataOperations.Update(
         AccountId,
         account,
         "Account",
         function ()
         {
          writeMessage("The account Name was updated to \"Updated Account Name\".");
          deleteAccount(AccountId);
         },
         errorHandler
       );
      }
    
      function deleteAccount(AccountId)
      {
       if (confirm("Do you want to delete this account record?"))
       {
        writeMessage("You chose to delete the account record.");
        SDK.JScriptRESTDataOperations.Delete(
           AccountId,
           "Account",
           function ()
           {
            writeMessage("The account was deleted.");
            enableResetButton(); 
           },
           errorHandler
         );
       }
       else
       {
        var li = document.createElement("li");
    
        var span = document.createElement("span");
        span.innerText = "You chose not to delete the record. You can view the record ";
    
        var link = document.createElement("a");
        link.href = SDK.JScriptRESTDataOperations._getServerUrl() + "/main.aspx?etc=1&id=%7b" + AccountId + "%7d&pagetype=entityrecord";
        link.target = "_blank";
        link.innerText = "here";
    
        li.appendChild(span);
        li.appendChild(link);
        document.getElementById("results").appendChild(li);
        enableResetButton();
        
       }
      }
    
      function getFirstContactToBePrimaryContact()
      {
       SDK.JScriptRESTDataOperations.RetrieveMultiple(
         "Contact",
         "$select=ContactId,FullName&$top=1",
         function (results)
         {
          var firstResult = results[0];
          if (firstResult != null)
          {
           primaryContact = results[0];
          }
          else
          {
           writeMessage("No Contact records are available to set as the primary contact for the account.");
          }
         },
         errorHandler
       );
      }
    
      function errorHandler(error)
      {
       writeMessage(error.message);
      }
    
      function enableResetButton()
      {
       document.getElementById("reset").removeAttribute("disabled");
      }
    
      function resetSample()
      {
       document.getElementById("results").innerHTML = "";
       document.getElementById("start").removeAttribute("disabled");
       document.getElementById("reset").setAttribute("disabled", "disabled");
      }
    
      //Helper function to write data to this page:
      function writeMessage(message)
      {
       var li = document.createElement("li");
       li.innerText = message;
       document.getElementById("results").appendChild(li);
      }
     </script>
    </head>
    <body style="background-color:White;">
    <button id="start" title="Click this button to start the sample.">Start</button>
    <button id="reset" title="Click this button to reset the sample." disabled="disabled">Reset</button>
    <ol id="results"></ol>
    </body>
    </html>
    
    
    
    

    I think this should be enough to verify that this sample works for what it is intended to do: simply perform basic operations from an HTML Web Resource.

     

    To use the SDK.JScriptRESTDataOperations library in a form event I did the following to create an account in the load event of a custom entity form:

    1. Added these libraries to the form libraries:

    • A web Resource with the contents of json2.js (This is required)
    • The jdaly_/Scripts/SDK.JScriptRESTDataOperations.js library mentione above
    • A new library called new_TEST that only contains the following createAccountTest function:

    function createAccountTEST()
      {
       var account = {};
       account.Name = "Test Account Name";
       account.Description = "This account was created by the JScriptRESTDataOperations sample.";
    
       //Set a picklist value
       account.PreferredContactMethodCode = { Value: 2 }; //E-mail
    
       //Set a money value
       account.Revenue = { Value: "2000000.00" }; //Set Annual Revenue
    
       //Set a Boolean value
       account.DoNotPhone = true; //Do Not Allow
    
       //Add Two Tasks
       var today = new Date();
       var startDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 3); //Set a date three days in the future.
    
       var LowPriTask = { Subject: "Low Priority Task", ScheduledStart: startDate, PriorityCode: { Value: 0} }; //Low Priority Task
       var HighPriTask = { Subject: "High Priority Task", ScheduledStart: startDate, PriorityCode: { Value: 2} }; //High Priority Task
       account.Account_Tasks = [LowPriTask, HighPriTask]
    
       //Create the Account
       SDK.JScriptRESTDataOperations.Create(
         account,
         "Account",
         function (account)
         {
          alert("The account named \"" + account.Name + "\" was created with the AccountId : \"" + account.AccountId + "\".");
         },
         function (error) { alert(error.message); }
       );
      }

    This function is just a modified version of the createAccount function used in the HTML web resource jdaly_/TestPage.htm mentioned above. I removed references to other messages so there were no dependencies on other functions. (except SDK.JScriptRESTDataOperations.Create, of course).

    In the Onload event handler for the form I set:

    • Library: new_Test
    • Function: createAccountTest
    • Enabled = (checked)

    After saving and publishing customizations I verified that this works.

    I expect all the other functions in the SDK.JScriptRESTDataOperations library to work as well. I'll do some additional testing before I include it as a sample in the SDK.

    I hope that these examples help answer your questions and improve your experience writing scripts for CRM.

     Best regards,

     

     


    Jim Daly Technical Writer Microsoft Dynamics CRM
    Sunday, September 18, 2011 11:11 PM
    Answerer
  • In reference to you question about a better way than using alert statements everywhere.

    First of all, make sure your IE Browser settings are set up right. Go to Internet Options, Advanced Tab.

    In the Browsing group, make sure that Disable script debugging is NOT checked and that Display a notification about every script error IS checked.

    This way you will get prompted to open the page for debugging in the IE developer tools. With those tools you can set breakpoints, add varilables to the watch list and generally have full control to debug scripts. Press F12 to open the IE developer tools any time.

    Having said that, it is still somewhat hard because so many scripts are loaded with the CRM forms. You have to choose from quite a long list and sometimes there are duplicate entries. Which to choose?

    Personally, I recommend using the solution from the SDK described in this blog post: JScript IntelliSense for Form Script Libraries.

    Although this solution is primarily about providing IntelliSense, as a side benefit you can do some testing of your form scripts using the same framework needed for generating the IntelliSense. Of course, you still need to in CRM, but for issues involving basic interactions with the Xrm.Page, this should help you out.

    Since this solution allows you to work with JavaScript libraries without uploading them as web resources, debugging with the IE Developer tools is significantly simpler.

    (Please note the issues at the bottom of the blog post)


    Jim Daly Technical Writer Microsoft Dynamics CRM
    Sunday, September 18, 2011 11:45 PM
    Answerer
  • Jim, thanks a LOT! I downloaded the managed solution which worked. I still had some issues getting it to run on an entity but it finally worked with your comments. Here are some pitfalls that might help others:

    * The function name is case sensitive. So in the above example the Function you define for the OnLoad even is createAccountTEST (rather than createAccountTest)

    * Get the json2 code from here: https://raw.github.com/douglascrockford/JSON-js/master/json2.js If you go to the site I mentioned before switch to the raw view and copy that text.

    * I don't know if it matters but on the "Form Properties" the sequence of my libraries is new_json2, new_SDK.JScriptRestDataOperations, new_Test (note that my naming is a bit different then in this example so the names might not match but it should be obvious what is what).

    If everything works ok you will get one popup saying that the account was created. Go to accounts and check for it. If you want to be sure delete the account (not deactivate) and run it again.

    Monday, September 19, 2011 5:36 AM