2010/05/21
CRM Installation Tips
Whether you are upgrading an existing CRM 3 installation or installing a fresh new CRM 4 installation, you will have to pay close attention to all of the system requirements prior to starting your installation or upgrade process. The CRM installation program has a fairly good pre-requisites test that will assess your infrastructure and give you a report of your readiness prior to actually performing the installation.
Some of the things that you should be aware of before you start. Install the latest version of .Net framework on the CRM server, and be aware that CRM 4 requires both SQL Server 2005 and SQL Server Reporting Services 2005, both with Service Pack 2. I recommend that you take care of these prerequisited before starting the CRM 4 installation program, otherwise your prerequisites will fail and the installation program will not let you continue with the installation.
Upgrading from CRM3
Backup everything - including your existing CRM 3 operating system. Backup your databases as well. If your CRM 4 upgrade fails, you will be left with having to reinstall CRM 3 from scratch. So backup everything. Consider yourself forewarned!
Many CRM 3 installations are still running on a SQL 2000 / SQL 2000 Reporting Services Infrastructure. The initial installation guides do not cover the topic of upgrading your SQL infrastructure before you start the CRM 4 upgrade process, but it is an absolute requirement. I highly recommend that to prepare for a CRM 4 upgrade, you upgrade your existing CRM 3 installation to SQL 2005 and ensure that it is all working before you start an upgrade to CRM 4. I would even recommend that you upgrade to SQL 2005 and take a new backup so that your restore point is not so far back. You may even wish to split your upgrade process into a couple of weekends, upgrading your SQL infrastructure on the first weekend, then the CRM system on the next weekend.
*IMPORTANT NOTE*
If you upgrade your database to SQL 2005, you *MUST* ensure that full-text indexing is enabled on your SQL 2005 database prior to running the installation progam. The CRM 4 installation does not test for this, and if full text indexing is not enabled, you will encounter an installation error and be left with the only option of reinstalling CRM 3 and restarting the entire process. You may even have to reinstall your operating system or restore the OS from backups as you may have difficulties with the CRM 3 installation process as well after a botched CRM 4 installation. Full text indexing should be on for SQL 2005 databases, but if you mount a SQL 2000 database in SQL 2005, it might not be enabled. You can enable full text indexing by issuing the following SQL command on that database:
sp_fulltext_database @action='enable'
Installing Reporting Services SP2
You may have a problem trying to patch Reporting Services with Service Pack 2. The upgrade program creates bad SQL that it runs in the process, and that results in the installation failing. The solution is to locate the upgrade log, then strip out the SQL, make the required changes, then run it manually. For example, if your reporting server database is CRM_ReportingServices, the SQL commands will include a USE statement of: USE CRM_[CRM_ReportingServices], which is not valid. I have not found any other way to fix this problem. That said, CRM 4 will require Reporting Services 2005 Service Pack 2, so I recommend you ensure that your reporting services is properly patched prior to installing and configuring CRM 4.
Before you start the CRM 4 upgrade/installation process, be sure to test your Reporting Services url.
404.1 Error Accessing Reporting Services from the CRM Server
You may encounter an issue where the reporting services url does not work from the server that you are installing CRM from. When you browse the reporting services url, you are challenged for credentials 3 times and then given a generic 401.1 error. The fix is to either disable the LSA loopback check, or add the url to the list of valid host names on the reporting services server. See http://support.microsoft.com/?id=896861 for more information.
Email Router Configuration
The email router can be installed and configured seperately. When you install an instance of CRM 4, you will have to provide a friendly name and a convenient name for your CRM organization. This short name for the organization is used as part of the url (keep in mind that CRM 4 is multi-tenant capable). The url is normally not case sensitive, EXCEPT for the email router configuraiton. If you do not get the case properly, it will fail to load the users and will give you false instructions on how to determine your url. Do not follow those instructions - open the deployment manager and use the exact organization name from that program instead. Once you get the exact url, with case proper, you should be able to configure the email gateway.
Access Denied and SPNs
On one of my installations, the CRM web interface would prompt me for credentials, then give me a 401.1 access denied error. I had installed CRM 4 on a website that had custom host headers (custom urls). When I used the custom url (that was not the domain name of the machine), the integrated security would fail. During the installation program, it raised a warning that the website did not resolve to the machine name, so the CRM installation program did tell me about the problem. I noticed that if I used localhost for the url, the website would work fine, but it would not work using the custom url for the site. The solution to the problem was to download the SETSPN.EXE program from the Microsoft website and issue a SETSPN -A HOST/[url] [machine-name] command. Once I ran this, everything worked properly. Welcome to the world of SPNs.
Note that this can also cause an issue with Reporting services. If you are using a custom url for reporting services, integrated security might also fail. Unfortunately, the fix that is promoted is to change the NTAuthenticationProviders IIS metabase property to "NTLM". This will make Reporting Services work, but is incompatible with CRM, as CRM needs "Negotiate, NTLM". That said, the better way to fix reporting services is to register the SPN for that url too, without making modifications to the NTAuthenticationProviders metabase attribute. This will ensure that both Reporting Services and CRM will function on the same machine.
2010/05/18
Point a IFrame to a external website
To do this, one sets up a little code for the form On Load event that puts the Accounts web site URL into the IFRAME’s URL. It’s actually pretty simple. Here is the code (assumes you already know how to set up an IFRAME). IFRAME_WebSite is the name of the IFRAME and .src references the URL it uses.
// Load web site URL
{
var AccountURL = crmForm.all.websiteurl.DataValue;
if (AccountURL != null)
{
crmForm.all.IFRAME_WebSite.src = AccountURL;
}
}
Note:
When working with iframes in Microsoft Dynamics CRM, you may find that you need to set the source of the iframe to a blank page (often I set the source to a blank page in the form configuration – I almost always have javascript dynamically build the url for the source during the page load). Traditionally, CRM developers and consultants (myself included in this bunch) have always been setting the iframe source to about:blank. While this has been adequate for many situations, I’ve started to notice there can be issues when viewing the iframe in a browser that has Enhanced Security Configuration installed.
The issue that crops up is that about:blank will trigger warning notifications in Internet Explorer – if you’re pulling the pages up from the server (which is how I’m usually doing config work – since I’m an outside consultant), this can be a bit of a nuisance. But I’ve also been in a situation at a client who had a very tightly controlled and locked-down Active Directory environment and about:blank was throwing security warning on the client XP machines. Needless to say, this was not acceptable.
Fortunately, there is a CRM url you may use to display a blank page. Instead of setting the iframe source to about:blank, I’ve gotten into the habit of setting it to /_root/Blank.aspx. This gives you a valid url to set the iframe source to while also providing a blank space within the iframe. This method works in any iframe situation without throwing the security warnings, so I’ve made it a standard practice to always use /_root/blank.aspx when I need a blank page.
2010/05/17
Making a field required based on picklist value
//or use to .SelectedIndex or .DataValue
{
crmForm.SetFieldReqLevel("theOtherField".id, 1);
//set it to required
}
else
{
crmForm.SetFieldReqLevel("theOtherField".id, 0);
//set it to not required
}
Enabled and disable fields based on a user role
function UserHasRole(roleName)
{
//get Current User Roles, oXml is an object
var oXml = GetCurrentUserRoles();
if(oXml != null)
{
//select the node text
var roles = oXml.selectNodes("//BusinessEntity/q1:name");
if(roles != null)
{
for( i = 0; i < roles.length; i++)
{
if(roles[i].text == roleName)
{
//return true if user has this role
return true;
}
}
}
}
//otherwise return false
return false;
}
function GetCurrentUserRoles()
{
var xml = "" +
"<?xml version="1.0" encoding="utf-8"?>" +
"<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">" +
GenerateAuthenticationHeader() +
" <soap:Body>" +
" <RetrieveMultiple xmlns="http://schemas.microsoft.com/crm/2007/WebServices">" +
" <query xmlns:q1="http://schemas.microsoft.com/crm/2006/Query" xsi:type="q1:QueryExpression">" +
" <q1:EntityName>role</q1:EntityName>" +
" <q1:ColumnSet xsi:type="q1:ColumnSet">" +
" <q1:Attributes>" +
" <q1:Attribute>name</q1:Attribute>" +
" </q1:Attributes>" +
" </q1:ColumnSet>" +
" <q1:Distinct>false</q1:Distinct>" +
" <q1:LinkEntities>" +
" <q1:LinkEntity>" +
" <q1:LinkFromAttributeName>roleid</q1:LinkFromAttributeName>" +
" <q1:LinkFromEntityName>role</q1:LinkFromEntityName>" +
" <q1:LinkToEntityName>systemuserroles</q1:LinkToEntityName>" +
" <q1:LinkToAttributeName>roleid</q1:LinkToAttributeName>" +
" <q1:JoinOperator>Inner</q1:JoinOperator>" +
" <q1:LinkEntities>" +
" <q1:LinkEntity>" +
" <q1:LinkFromAttributeName>systemuserid</q1:LinkFromAttributeName>" +
" <q1:LinkFromEntityName>systemuserroles</q1:LinkFromEntityName>" +
" <q1:LinkToEntityName>systemuser</q1:LinkToEntityName>" +
" <q1:LinkToAttributeName>systemuserid</q1:LinkToAttributeName>" +
" <q1:JoinOperator>Inner</q1:JoinOperator>" +
" <q1:LinkCriteria>" +
" <q1:FilterOperator>And</q1:FilterOperator>" +
" <q1:Conditions>" +
" <q1:Condition>" +
" <q1:AttributeName>systemuserid</q1:AttributeName>" +
" <q1:Operator>EqualUserId</q1:Operator>" +
" </q1:Condition>" +
" </q1:Conditions>" +
" </q1:LinkCriteria>" +
" </q1:LinkEntity>" +
" </q1:LinkEntities>" +
" </q1:LinkEntity>" +
" </q1:LinkEntities>" +
" </query>" +
" </RetrieveMultiple>" +
" </soap:Body>" +
"</soap:Envelope>" +
"";
var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
xmlHttpRequest.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
xmlHttpRequest.setRequestHeader("SOAPAction"," http://schemas.microsoft.com/crm/2007/WebServices/RetrieveMultiple");
xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
xmlHttpRequest.send(xml);
var resultXml = xmlHttpRequest.responseXML;
return(resultXml);
}
//This is the code used to enable or disable the fields in CRM 4.
try
{
if(crmForm.all.isp_goodwill.DataValue == '1') //List box selected = yes
{
if(UserHasRole("Goodwill Manager")) //Role to look for
{
//Fields to enable.
crmForm.all.isp_goodwillrequestedbyid.Disabled = false;
crmForm.all.isp_goodwillapprovedbyid.Disabled = false;
crmForm.all.isp_goodwillvalueapproved.Disabled = false;
}
}
}
catch(e)
{
alert(this.gErrorMessage("Authorisation Form",e.description ));
}
2010/05/05
Getting a quick introduction into CRM 4
I used this website to quickly get a overview of how MS CRM works.
This helped me alot.
Enjoy!
2010/05/04
Some more useful javascripts for MS CRM
Enjoy!
Replacing the content of an IFRAME
If you really want to do some funny things in your CRM form, you can create an IFRAME serving as a placeholder for your real HTML code. Create an IFRAME in an entity and name it "IFRAME_TEST". In the Onload event put the following code:
crmForm.all.IFRAME_TEST_d.innerHTML ="<b>Some HTML text</b>";
Note the "_d" at the end of IFRAME_TEST.
This single line replaces the whole IFRAME element with your own HTML.
Adding additional values to duration fields
The following script adds the new option "42 Minutes" to the actualdurationminutes field in a task:
var duration = crmForm.all.actualdurationminutesSelect;
var tables = duration.getElementsByTagName("table");
var table = tables[1];var row = table.insertRow();
var newOption = row.insertCell(-1);
var newValue = "42 Minutes";
newOption.setAttribute("val", newValue);
newOption.innerText = newValue;
Changing the title of a CRM form
document.title = "Hello World!";
Changing the Link of a Ticker Symbol from MSN Money to Yahoo Finances
This is an unsupported solution as it requires modifications to server files!
Open the following directory on your CRM server: C:Inetpubwwwroot_formscontrols (may vary depending on your installation)Open the following file: INPUT.text.ticker.htcSearch the following function:
function Launch()
{
if(value.length > 0)
{
Parse();
window.open("http://go.microsoft.com/fwlink?linkid=8506&Symbol=" + encodeURIComponent(value), "", "height=" + (screen.availHeight * .75) + ",width=" + (screen.availWidth * .75) + ,scrollbars=1,resizable=1,status=1,toolbar=1,menubar=1,location=1"); }}
Replace it with the following:
function Launch()
{
if(value.length > 0)
{
Parse();
//window.open("http://go.microsoft.com/fwlink?linkid=8506&Symbol=" + encodeURIComponent(value), "", "height=" + (screen.availHeight * .75) + ",width=" + (screen.availWidth * .75) + ,scrollbars=1,resizable=1,status=1,toolbar=1,menubar=1,location=1");
window.open("http://finance.yahoo.com/q?s=" + encodeURIComponent(value), "", "height=" + (screen.availHeight * .75) + ",width=" + (screen.availWidth * .75) + ",scrollbars=1,resizable=1,status=1,toolbar=1,menubar=1,location=1");
}
}
Save the file.On your client machine: Open Internet Explorer and remove all temporary internet files, so that the new htc source is retrieved. Navigate to any ticker symbol, enter a valid value and double click it.
Counting the number of backslashes in a string
Of course you can use the two code snippets with any character you like.
Solution 1
var text = "hardware\printers\Epson";var paths = text.split("\");alert(paths.length -1);
Solution 2
var text = "hardware\printers\Epson";var numBs = 0;
var index = 0;while(index != -1) { index = text.indexOf("\", index); if (index != -1) { numBs++; index++; }}alert(numBs);
Changing the label of a field at runtime
Lets say you want to change the label of the revenue field, then you simply write
crmForm.all.revenue_c.innerText = "Amount Euro";
Simply append the "_c" to the field name. This should work for most labels.
Changing the colour of a single option in a picklist
You can change the colour and background colour of an option element using the following syntax://TODO: Replace <name of your picklist field> with the schema name of the picklist in question.var list = crmForm.all.<name of your picklist field>;
//using the first option here, you need to find the one you need.var option = list.options[0];
//Set the background colour to red.option.style.backgroundColor = "#FF0000";
//Set the text colour to white.option.style.color = "#FFFFFF";
Opening a new window without the IE menu and status bars
The following is a sample HTML page that you can use to test. Replace the link with the URL of your web page.
<html>
<head/>
<body>
<p><a href="javascript:;" onClick="window.open('http://www.domain.com/crm','domain','toolbar=no,status=no,menubar=no,scrollbars=yes,resizable=yes'); return false">Domain</a>
</p>
</body>
</html>
Retrieving the text of a lookup control
var lookup = crmForm.all.<lookupfield>.DataValue;if (lookup[0] != null) { var theText = lookup[0].name;}
Disabling a form at runtime
document.body.disabled = true;
This will disable all controls except the Notes tab, where you still can enter new notes.
Automatically changing the value of a text field to capitals
Put the following script in the OnChange event of the text field:
crmForm.all.<name of text field>.DataValue = crmForm.all.<name of text field>.DataValue.toUpperCase();
Getting notified when the user selects a another tab on a form
Put the following in the OnLoad event of your form:
crmForm.all.tab0Tab.onclick = function()
{
alert("Tab 0 clicked");
}
crmForm.all.tab1Tab.onclick = function()
{
alert("Tab 1 clicked");
}
It seems that the tab pages are always named "tabxTab", where x is a number starting from 0 (first tab) to (n-1), where n is the total number of tabs. Maybe you find better events that handle the activation instead of a click, but that's the general way it should work.
The current record id
The primary key is always available in
crmForm.ObjectId
It is set to null, if the record hasn't been saved yet. There are some other useful properties. Look at the "Form Object Model" in the SDK docs for more information.
The current object type code
The type code of the entity being displayed in a CRM form is available through
crmForm.ObjectTypeCode
There are some other useful properties. Look at the "Form Object Model" in the SDK docs for more information.
 
Initializing a date field with the current date
crmForm.all.<attribute>.DataValue = new Date();
Changing the URL of an IFRAME at runtime
Set the initial URL of the IFRAME to about:blank and execute the following line:
document.all.IFRAME_<name of iframe>.src = http://domain/file;
Displaying a picture in a CRM form, using a text field to store the data source
Let's say you have a custom field storing the image name called new_pictureName. Then in the OnLoad event do the following:
if ((crmForm.all.new_pictureName != null) && (crmForm.all.new_pictureName.DataValue != null))
{
document.all.IFRAME_<name of iframe>.src = http://server/dir/images/ + crmForm.all.new_pictureName.DataValue;
}
In the OnChange event of the new_pictureName place the following:
if (crmForm.all.new_pictureName.DataValue != null)
{
document.all.IFRAME_<name of iframe>.src = http://server/dir/images/ + crmForm.all.new_pictureName.DataValue;
}
else
{
document.all.IFRAME_<name of iframe>.src = "about:blank"}
Checking crmForm.all.new_pictureName for null in the OnLoad event is a sanity check. Bulk edit forms will not display all fields and if it is not contained in the form, it will be null. This check is not required in the OnChange event, as this event will never fire if the field does not exist.
Changing the default lookup type
When opening a lookup that can select from multiple entity types, you may want to use a different default entity in the lookup dialog. The following script changes the default entity of the regardingobjectid field in an activity to contacts:
if (crmForm.all.regardingobjectid != null)
{
crmForm.all.regardingobjectid.setAttribute("defaulttype", "2");
}
Checking if a field is present in a form
if (crmForm.all.<name of field> != null)
{
//do your JavaScript processing here}
You should always check if a field is present before accessing it, because your script may throw an exception if it is not included in a form (like in quick create forms if your field is not required or recommended).
Calculating a date field based upon the selection of a picklist
The following script assumes that you have a picklist with three values: "1 year", "2 years" and "3 years".
var years = 0;
switch(crmForm.all.<name of picklist field>.DataValue)
{
//You need to check the picklist values if they have the values 1, 2 and 3. Look at the attribute in the entity customization and note the option values.
case "1": years = 1;
break;
case "2": years = 2;
break;
case "3": years = 3;
break;
}
if (years != 0)
{
var date = new Date();
date.setYear(date.getYear() + years);
crmForm.all.<name of date field>.DataValue = date;
}
Creating a new email when double-clicking a standard text field
if (crmForm.all.<name of text field> != null)
{
crmForm.all.<name of text field>.ondblclick = function()
{
var email = crmForm.all.<name of text field>.DataValue;
if ((email != null) && (email.length > 0))
{
window.navigate("mailto:" + email);
}
}}
Resetting a field value if validation fails
In the OnLoad event, place the following code:
crmForm.all.<field to check>.setAttribute("lastKnownGood", crmForm.all.<field to check>.DataValue);
This adds a new attribute to field for later access. In the OnChange event put the following:
//checkFailed contains either true or false and must be set to the result of your validation routineif (checkFailed)
{
crmForm.all.<field to check>.DataValue = crmForm.all.fieldToCheck.getAttribute("lastKnownGood");
else
{
crmForm.all.<field to check>.setAttribute("lastKnownGood", crmForm.all.fieldToCheck.DataValue);
}
Inserting line breaks in a text area control
Put the following in the OnLoad event:
crmForm.all.<your text field>.style.whiteSpace = "pre";
Hiding an entire row of a CRM form
//the field you want to hidevar field = crmForm.all.name;//search the enclosing table rowwhile ((field.parentNode != null) && (field.tagName.toLowerCase() != "tr"))
{
field = field.parentNode;
}
//if we found a row, disable itif (field.tagName.toLowerCase() == "tr")
{
field.style.display = "none";
}
Disabling the time selection of a date/time field
You can dynamically enable or disable the time selection box of a date/time field using the following syntax:
var dateField = crmForm.all.<name of datetime field>;
//Check the existence of the time field. It is null if the control is setup to only display the date.
if (dateField.all.time != null)
{
//Disable the time field
dateField.all.time.disable();
//Enable the time field
dateField.all.time.enable();
}
CRM automatically enables the time selection box if the date value changes to a non-null value, so in order to always disable the time selection box, you need to add the following OnChange event script to the datetime field:
var dateField = crmForm.all.<name of datetime field>;
//Check the existence of the time field. It is null if the control is setup to only display the date.
if (dateField.all.time != null)
{
//Disable the time field
dateField.all.time.disable();
}
To initially disable the time field, put the following into the form OnLoad event:
var dateField = crmForm.all.<name of datetime field>;
//Check the existence of the datetime field. It may not be included in a quick create form!
if (dateField != null)
{
//Call the OnChange event handler
dateField.FireOnChange();
}
Changing the time interval in date/time fields
The time selection box of a date/time field uses a 30 minute interval. You can change it to a different interval using the following code in an OnLoad event:
var dateField = crmForm.all.<name of datetime field>;
//Check the existence of the datetime field. It may not be included in a quick create form!
if (dateField != null)
{
var timeField = dateField.all.time;
//Check the existence of the time field. It is null if the control is setup to only display the date.
if (timeField != null)
{
//The new interval in minutes.
var interval = 15;
var tables = timeField.getElementsByTagName("table");
if ((tables != null) && (tables.length > 0))
{
var table = tables[1];
//Remove all existing values from the selection box while (table.firstChild != null)
{
table.removeChild(table.firstChild);
}
//Add the new values for (hour = 0; hour < 24; hour++)
{
for (min = 0; min < 60; min += interval)
{
var row = table.insertRow();
var cell = row.insertCell();
var time = ((hour < 10) ? "0" : "") + hour + ":" + ((min < 10) ? "0" : "") + min;
cell.setAttribute("val", time);
cell.innerText = time;
}
}
}
}
}
Getting the quote id inside a quotedetail form
There's a hidden field in the quotedetail form storing the quote id. You can access it using
alert("Quote ID = " + crmForm.all.quoteid.DataValue);
How to know which button triggered the OnSave event
You can use the event.Mode property in the OnSave event. It will tell you which button was used to save an entity. Not all values are documented in the SDK, e.g the "Save as completed" in a phone call has a value of 58. As it's not documented, this number may change in future releases.
To get started enter the following into your OnSave script:
alert ("event.Mode = " + event.Mode);
event.returnValue = false;
return false;
This codes makes it impossible to save the entity, but allows you to write down all of the values passed to OnSave. You will see 1 for a normal save operation and 2 for Save and Close. Most but not all constants are defined in /_common/scripts/formevt.js.
Accessing the CRM database from JavaScript
To access the CRM database from JavaScript, use the following as a template:
var connection = new ActiveXObject("ADODB.Connection");
var connectionString = "Provider=SQLOLEDB;Server=xxxxx;Database=xxxx_mscrm;Integrated Security=sspi";
connection.Open(connectionString);
var query = "SELECT name FROM FilteredAccount";
var rs = new ActiveXObject("ADODB.Recordset");
rs.Open(query, connection, /*adOpenKeyset*/1, /*adLockPessimistic*/2);
rs.moveFirst();
var values = "";
while (!rs.eof)
{
values += rs.Fields(0).Value.toString() + " ";
rs.moveNext();
}
connection.Close();
alert(values);
You may need to adjust your security settings in IE in order to run this code.
Modifying the Quick Create Form
Allthough the quick create form isn't listed in the forms and views dialog, it uses the same scripts entered in the main application form. You can use the following in the OnLoad script to run code when inside of a quick create form:
if (crmForm.FormType == 5 /* Quick Create Form */)
{
//modify the form using DHTML
}
Forcing the selection of an account in the potential customer field of an opportunity
Put the following into the opportunity's OnLoad event:
crmForm.all.customerid.setAttribute("lookuptypes", "1");
This tells the customer lookup to only include the account (object type code 1). A value of 2 forces the lookup to only display contacts.
Hiding and showing fields dynamically based on other field values
You can do this in client-side JavaScript. Let's say you have a picklist named "new_category" and you have assigned the following options to it:
1 – Business
2 – Private
3 - Unspecified
Let's further assume that you want to hide the fields "new_a" and "new_b" whenever a user selects "Private", but they should be visible if a different option is selected in the picklist. Your OnChange event script of the new_category field will be:
var hideValues = (crmForm.all.new_category != null) && (crmForm.all.new_category.DataValue == "2");
var displayStyle = hideValues ? "none" : "";
crmForm.all.new_a.style.display = displayStyle;
crmForm.all.new_b.style.display = displayStyle;
To run the code when the form is initially loaded, put the following into the OnLoad event:
if (crmForm.all.new_category != null)
{
crmForm.all.new_category.FireOnChange();
}
The test for a null value of crmForm.all.new_category is included to avoid errors if the field is not available on a form. This is true for quick create forms if the field required level is not set to business required or business recommended.
Removing the value from a lookup field
crmForm.all.<lookupField>.DataValue = null;
Hiding tabs at runtime
You can hide and show entire tabs dynamically using the following code:
//Add your condition here, usually a field comparison in an OnChange event
if (condition == true)
{
//hide the second tab
crmForm.all.tab1Tab.style.display = "none";
}
else
{
//show the second tab
crmForm.all.tab1Tab.style.display = "";
}
The tabs are named "tab0Tab", "tab1Tab", "tab2Tab" and so on.
Setting the active tab
From the SDK docs:SetFocus: Sets the focus, changes tabs, and scrolls the window as necessary to show the specified field. Example:
// Set focus to the field.
crmForm.all.SOME_FIELD_ID.SetFocus();
Displaying related entities instead of the form when opening a record
To open a related entity view of the left navigation bar (like contacts in an account entity), you can use the following:
loadArea("areaContacts");
Each entry in the navigation bar has a unique area name (areaContacts is just a sample). If you haven't already done it, download and install the Internet Explorer Developer Toolbar to find the name of the entry you're after. Some instructions on how to use it are in http://www.stunnware.com/crm2/topic.aspx?id=CrmLookAndFeel1; though it is a different topic, I included some screenshots using the developer toolbar in CRM.
Modifying the color of disabled form fields
Disabled form fields are sometimes hard to read. Making the color a little bit darker greatly helps to read all of the content without loosing the information that a field is read-only.
Open the following file in your CRM web: /_forms/controls/controls.css. Then find the following style:
INPUT.ro,TEXTAREA.ro,DIV.ro,SPAN.ro
{
background-color: #ffffff;
color: #808080;
border-color: #808080;
}
The color attribute is the light gray you're seeing by default. You may change it to #404040 to get a slightly darker gray. If you don't see the new colors in IE, hit CTRL-F5 to reload the source files, including the updated css file.
Removing a navigation bar entry at runtime
To remove a navigation bar entry dynamically, you can use the following code:
var navigationBarEntry = document.getElementById("navProds");
if (navigationBarEntry != null)
{
var lbArea = navigationBarEntry.parentNode;
if (lbArea != null)
{
lbArea.removeChild(navigationBarEntry);
}
}
If you haven't already done it, download and install the Internet Explorer Developer Toolbar to find the name of the navigation bar entry.
Changing the colour of a label
var label = crmForm.all.<field name>_c;
label.innerHTML = "<font colour='#FF0000'>" + label.innerText + "</font>";
Hide a section on a form
crmForm.all.new_attribute_c.parentElement.parentElement.parentElement.style.display = 'none'
To show the section again:
crmForm.all.new_attribute_c.parentElement.parentElement.parentElement.style.display = 'block'
AJAX - CRM 3 only
checkResult = function()
{
var oXmlDoc = new ActiveXObject("Microsoft.XMLDOM");
oXmlDoc.async = false;
var s;
s = "http://localhost:5555/adv_postback.aspx?fundid=" + crmForm.all.adv_fundid.DataValue[0].id;
oXmlDoc.load(s);
var oNode = oXmlDoc.selectSingleNode("results/fundcode");
if (oNode != null)
{
res = oNode.text;
crmForm.all.adv_adminno.DataValue = res
}
}
Maximise Screen
if (window.screen)
{
var aw = screen.availWidth;
var ah = screen.availHeight;
window.moveTo(0, 0);
window.resizeTo(aw, ah);
}
Date Functions
var currentTime = new Date()
var month = currentTime.getMonth() + 1
var day = currentTime.getDate()
var year = currentTime.getFullYear()
document.write(month + "/" + day + "/" + year)
var currentTime = new Date()
var hours = currentTime.getHours()
var minutes = currentTime.getMinutes()
if (minutes < 10)
minutes = "0" + minutes
document.write(hours + ":" + minutes + " ")
if(hours > 11)
{
document.write("PM")
}
else
{
document.write("AM")
}
getTime() - Number of milliseconds since 1/1/1970 @ 12:00 AM
getSeconds() - Number of seconds (0-59)
getMinutes() - Number of minutes (0-59)
getHours() - Number of hours (0-23)
getDay() - Day of the week(0-6). 0 = Sunday, ... , 6 = Saturday
getDate() - Day of the month (0-31)
getMonth() - Number of month (0-11)
getFullYear() - The four digit year (1970-9999)
MAKE THE RECIPIENT BLANK FOR NEW EMAILS
var CRM_CREATE_FORM = 1;
var formType = crmForm.FormType;
if(formType == CRM_CREATE_FORM)
{
crmForm.all.to.DataValue = null;
}
FORMAT PHONE NUMBER
// Attempt to auto-format basic US phone numbers. This method supports
// 7 and 10 digit numbers. Example: (410) 555-1212
// Get the field that fired the event
var oField = event.srcElement;
// If we have the field and all is well
if (typeof(oField) != "undefined" && oField != null)
{
// Remove any non-numeric characters
var sTmp = oField.DataValue.replace(/[^0-9]/g, "");
// If the number is a length we expect and support, format the number
switch (sTmp.length)
{
case "4105551212".length:
oField.DataValue = "(" + sTmp.substr(0, 3) + ") " + sTmp.substr(3, 3) + "-" + sTmp.substr(6, 4);
break;
case "5551212".length:
oField.DataValue = sTmp.substr(0, 3) + "-" + sTmp.substr(3, 4);
break;
}
}
CONFIRM SAVE
if (confirm("Are you sure you want to save this Stakeholder Contact?"))
{
//save the form.
}
else
{
event.returnValue = false;
validationErrors = true;
}
 
HIDE CONVERT BUTTON ON LEAD
switch(crmForm.FormType)
{
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
break;
case 2:
if(myField == [ValueThatEqualsYes])
{
var btConvert = "_MBconvertLead";
var menuButton = document.getElementById(btConvert);
if (menuButton != null)
{
menuButton.style.display='';
}
}
else
{
var btConvert = "_MBconvertLead";
var menuButton = document.getElementById(btConvert);
if (menuButton != null)
{
menuButton.style.display='none';
}
}
break;
}
MAKE THE TAB TITLE BLUE
crmForm.all.tab0Tab.style.backgroundColor="blue";
SHOW NUMBER OF NOTES ON THE NOTES TAB (CRM 3)
This is in the form load event of the aspx page:
Dim NoteCount As Integer, ID As String, SQL As String, _ con As SqlConnection, com As SqlCommand, ds As New DataSet
ID = Request("ID")
SQL = "Select count(*) from annotation where ObjectID='" + ID + "'"
con = New SqlConnection
con.ConnectionString = "server=galatea;database=IOM_MSCRM;User Id=crmcustom;Password=crm@custom;"
con.Open()
com = New SqlCommand
com.CommandText = SQL
com.Connection = con
Dim da As New SqlDataAdapter(com)
da.Fill(ds)
For Each dr As DataRow In ds.Tables(0).Rows
NoteCount = dr.Item(0)
Next
con.Close()
Response.Clear()
Response.ContentType = "textXML"
Response.Write("<results><result>" & NoteCount & "</result></results>")
Response.End()
This is in the form load event of the CMR form:
DISPLAY NUMBER OF NOTES
var oXmlDoc = new ActiveXObject("Microsoft.XMLDOM");
oXmlDoc.async = false; // We want to wait for a response
var site;
site = window.location.protocol + "//" + window.location.host + "/ISP_IOM/CountNotes.aspx?id=" + crmForm.ObjectId
oXmlDoc.load(site);
var oNode = oXmlDoc.selectSingleNode("results/result");
if (oNode != null)
{
res = oNode.text;
crmForm.all.tab3Tab.innerText = "Notes (" + res + ")";
}
CHECK FOR . IN STRING
if (myString.indexOf(".")>=0)
{
alert("yes!");
}
else
{
alert("no!");
}
window.alert('demoalert');
Buttons
This is an example on how to create a button on the form, using java script. First of all we need to create a nvarchar attribute and put it on the form where we want our button. I assume that everybody knows how to create an attribute and put in on the form, so i won't talk about this.
In this example my attribute's schema name is new_button. Here is the code:
// This is how we call the button, what we see
crmForm.all.new_button.DataValue = "Ok";
// We could align it a bit
crmForm.all.new_button.style.textAlign = "center";
crmForm.all.new_button.vAlign = "middle";
//we make the mouse look as a hand when we're moving over
crmForm.all.new_button.style.cursor = "hand";
crmForm.all.new_button.style.backgroundColor = "#CADFFC";
crmForm.all.new_button.style.color = "#FF0000";
crmForm.all.new_button.style.borderColor = "#330066";
crmForm.all.new_button.style.fontWeight = "bold";
crmForm.all.new_button.contentEditable = false;
//we attach some events in order to make it look nice :)
crmForm.all.new_button.attachEvent("onmousedown",color1);
crmForm.all.new_button.attachEvent("onmouseup",color2);
crmForm.all.new_button.attachEvent("onmouseover",color3);
crmForm.all.new_button.attachEvent("onmouseleave",color4);
function color3()
{
crmForm.all.new_button.style.backgroundColor = "#6699FF";
}
function color4()
{
crmForm.all.new_button.style.backgroundColor = "CADFFC";
}
function color1()
{
crmForm.all.new_button.style.color = "000099";
}
function color2()
{
crmForm.all.new_button.style.color = "FF0000";
}
//here we attach what we want our button do
crmForm.all.new_button.attachEvent("onclick",someFunction);
 
Integer Validation
This script Validates weather an entry is an integer field or not using the isNAN function
var my_string= crmForm.all.new_accountnumber.DataValue
if(isNaN(my_string))
{
alert ("Please do not enter text into the account number field");
event.returnValue = false;
}
else
{alert("Account Number Accepted ");
if (crmForm.all.new_accountnumber.DataValue.length != 11)
{
alert ("Please enter in a numeric account number that is 11 digits long");
event.returnValue = false;
}
}
Replacing the content of an IFRAME
If you really want to do some funny things in your CRM form, you can create an IFRAME serving as a placeholder for your real HTML code. Create an IFRAME in an entity and name it "IFRAME_TEST". In the Onload event put the following code:
crmForm.all.IFRAME_TEST_d.innerHTML ="<b>Some HTML text</b>";
Note the "_d" at the end of IFRAME_TEST. This single line replaces the whole IFRAME element with your own HTML.
Adding additional values to duration fields
The following script adds the new option "42 Minutes" to the actualdurationminutes field in a task:
var duration = crmForm.all.actualdurationminutesSelect;
var tables = duration.getElementsByTagName("table");
var table = tables[1];
var row = table.insertRow();
var newOption = row.insertCell(-1);
var newValue = "42 Minutes";
newOption.setAttribute("val", newValue);
newOption.innerText = newValue;
Changing the title of a CRM form
document.title = "Hello World!";
Changing the Link of a Ticker Symbol from MSN Money to Yahoo Finances
This is an unsupported solution as it requires modifications to server files!
Open the following directory on your CRM server: C:Inetpubwwwroot_formscontrols (may vary depending on your installation)
Open the following file: INPUT.text.ticker.htc
Search the following function:
function Launch()
{
if(value.length > 0)
{
Parse();
window.open("http://go.microsoft.com/fwlink?linkid=8506&Symbol=" +
encodeURIComponent(value), "", "height=" + (screen.availHeight * .75) +
",width=" + (screen.availWidth * .75) +
,scrollbars=1,resizable=1,status=1,toolbar=1,menubar=1,location=1");
}
}
Replace it with the following:
function Launch()
{
if(value.length > 0)
{
Parse();
//window.open("http://go.microsoft.com/fwlink?linkid=8506&Symbol=" +
encodeURIComponent(value), "", "height=" + (screen.availHeight * .75) +
",width=" + (screen.availWidth * .75) +
,scrollbars=1,resizable=1,status=1,toolbar=1,menubar=1,location=1");
window.open("http://finance.yahoo.com/q?s=" +
encodeURIComponent(value), "", "height=" + (screen.availHeight * .75) +
",width=" + (screen.availWidth * .75) +
",scrollbars=1,resizable=1,status=1,toolbar=1,menubar=1,location=1");
}
}
Save the file.
On your client machine: Open Internet Explorer and remove all temporary internet files, so that the new htc source is retrieved. Navigate to any ticker symbol, enter a valid value and double click it.
Counting the number of backslashes in a string
Of course you can use the two code snippets with any character you like.
Solution 1
var text = "hardware\printers\Epson";
var paths = text.split("\");
alert(paths.length -1);
Solution 2)
var text = "hardware\printers\Epson";
var numBs = 0;
var index = 0;
while(index != -1) {
index = text.indexOf("\", index);
if (index != -1) {
numBs++;
index++;
}
}
alert(numBs);
Changing the label of a field at runtime
Lets say you want to change the label of the revenue field, then you simply write
crmForm.all.revenue_c.innerText = "Amount Euro";
Simply append the "_c" to the field name. This should work for most labels.
Changing the color of a single option in a picklist
You can change the color and background color of an option element using the following syntax:
//TODO: Replace <name of your picklist field> with the schema name of the picklist in question.
var list = crmForm.all.<name of your picklist field>;
//using the first option here, you need to find the one you need.
var option = list.options[0];
//Set the background color to red.
option.style.backgroundColor = "#FF0000";
//Set the text color to white.
option.style.color = "#FFFFFF";
Opening a new window without the IE menu and status bars
The following is a sample HTML page that you can use to test. Replace the link with the URL of your web page.
<html>
<head/>
<body>
<p>
<a href="javascript:;"
onClick="window.open('http://www.stunnware.com/crm','stunnware','toolbar=no,status=no,menubar=no,scrollbars=yes,resizable=yes');
return false">Stunnware</a>
</p>
</body>
</html>
Retrieving the text of a lookup control
var lookup = crmForm.all.<lookupfield>.DataValue;
if (lookup[0] != null) {
var theText = lookup[0].name;
}
Disabling a form at runtime
document.body.disabled = true;
This will disable all controls except the Notes tab, where you still can
enter new notes.
Automatically changing the value of a text field to capitals
Put the following script in the OnChange event of the text field:
crmForm.all.<name of text field>.DataValue = crmForm.all.<name of text field>.DataValue.toUpperCase();
Getting notified when the user selects a another tab on a form
Put the following in the OnLoad event of your form:
crmForm.all.tab0Tab.onclick = function() {
alert("Tab 0 clicked");
}
crmForm.all.tab1Tab.onclick = function() {
alert("Tab 1 clicked");
}
It seems that the tab pages are always named "tabxTab", where x is a number starting from 0 (first tab) to (n-1), where n is the total number of tabs. Maybe you find better events that handle the activation instead of a click, but that's the general way it should work.
The current record id
The primary key is always available in
crmForm.ObjectId
It is set to null, if the record hasn't been saved yet. There are some other useful properties. Look at the "Form Object Model" in the SDK docs for more information.
The current object type code
The type code of the entity being displayed in a CRM form is available through
crmForm.ObjectTypeCode
There are some other useful properties. Look at the "Form Object Model" in the SDK docs for more information.
Initializing a date field with the current date
crmForm.all.<attribute>.DataValue = new Date();
Changing the URL of an IFRAME at runtime
Set the initial URL of the IFRAME to about:blank and execute the following line:
document.all.IFRAME_<name of iframe>.src = http://domain/file;
Displaying a picture in a CRM form, using a text field to store the data source
Let's say you have a custom field storing the image name called new_pictureName. Then in
the OnLoad event do the following:
if ((crmForm.all.new_pictureName != null) && (crmForm.all.new_pictureName.DataValue != null)) {
document.all.IFRAME_<name of iframe>.src = http://server/dir/images/ + crmForm.all.new_pictureName.DataValue;
}
In the OnChange event of the new_pictureName place the following:
if (crmForm.all.new_pictureName.DataValue != null) {
document.all.IFRAME_<name of iframe>.src = http://server/dir/images/ +
crmForm.all.new_pictureName.DataValue;
}
else {
document.all.IFRAME_<name of iframe>.src = "about:blank"
}
Checking crmForm.all.new_pictureName for null in the OnLoad event is a sanity check. Bulk edit forms will not display all fields and if it is not contained in the form, it will be null. This check is not required in the OnChange event, as this event will never fire if the field does not exist.
Changing the default lookup type
When opening a lookup that can select from multiple entity types, you may want to use a different default entity in the lookup dialog. The following script changes the default entity of the regardingobjectid field in an activity to contacts:
if (crmForm.all.regardingobjectid != null) {
crmForm.all.regardingobjectid.setAttribute("defaulttype", "2");
}
Checking if a field is present in a form
if (crmForm.all.<name of field> != null) {
//do your JavaScript processing here
}
You should always check if a field is present before accessing it, because your script may throw an exception if it is not included in a form (like in quick create forms if your field is not required or recommended).
Calculating a date field based upon the selection of a picklist
The following script assumes that you have a picklist with three values: "1 year", "2 years" and "3 years".
var years = 0;
switch(crmForm.all.<name of picklist field>.DataValue) {
//You need to check the picklist values if they have the values 1, 2 and 3. Look at the attribute in the entity customization and note the option values.
case "1":
years = 1;
break;
case "2":
years = 2;
break;
case "3":
years = 3;
break;
}
if (years != 0) {
var date = new Date();
date.setYear(date.getYear() + years);
crmForm.all.<name of date field>.DataValue = date;
}
Creating a new email when double-clicking a standard text field
if (crmForm.all.<name of text field> != null) {
crmForm.all.<name of text field>.ondblclick = function() {
var email = crmForm.all.<name of text field>.DataValue;
if ((email != null) && (email.length > 0)) {
window.navigate("mailto:" + email);
}
}
}
Resetting a field value if validation fails
In the OnLoad event, place the following code:
crmForm.all.<field to check>.setAttribute("lastKnownGood", crmForm.all.<field to check>.DataValue);
This adds a new attribute to field for later access. In the OnChange event put the following:
//checkFailed contains either true or false and must be set to the result of your validation routine
if (checkFailed) {
crmForm.all.<field to check>.DataValue = crmForm.all.fieldToCheck.getAttribute("lastKnownGood");
}
else {
crmForm.all.<field to check>.setAttribute("lastKnownGood", crmForm.all.fieldToCheck.DataValue);
}
Inserting line breaks in a text area control
Put the following in the OnLoad event:
crmForm.all.<your text field>.style.whiteSpace = "pre";
Hiding an entire row of a CRM form
//the field you want to hide
var field = crmForm.all.name;
//search the enclosing table row
while ((field.parentNode != null) && (field.tagName.toLowerCase() != "tr")) {
field = field.parentNode;
}
//if we found a row, disable it
if (field.tagName.toLowerCase() == "tr") {
field.style.display = "none";
}
It's been a while since I posted these small JavaScript snippets (More JavaScript Code, More JavaScript Code Part 2), so after half a year I'm adding the third part today, containing 23 new samples. Hope you find it as useful as the first two articles.
Setting the background color of a CRM form
If you want to make it easier for a user to note what type of entity a CRM form is displaying, you can change the background color with only one line of code:
document.body.style.backgroundColor = 'red';
Put it into the OnLoad event of the form you want to change and replace 'red' with the color of your choice.
Why can't we use VBScript to write client-side code?
As CRM is a web application, client-side code deals with DHTML objects and the supported languages in IE are JavaScript and VBScript. JavaScript is the standard used in the internet, while VBScript is IE only, so it will never work with any browser other than IE.
One could argue that CRM is tied to IE6 and above, but allowing and supporting VBScript would make it impossible for the product team to even think about supporting other browsers. I never saw an official statement as to why VBScript is not supported though.
Some calculated fields on the CRM form are not saved in the database
If the field is disabled, which is a common practice for calculated fields, it is not sent to the server when the form is saved. To override this behavior, add this line to your code:
crmForm.all.your_field.ForceSubmit = true;
Changing detailed tooltips when hovering over fields in a CRM form
This article explains in depth how to do it: http://blogs.msdn.com/crm/archive/2006/11/17/using-the-attachevent-method-to-show-users-context-sensitive-help.aspx
Fixing problems with orphaned OnChange event handlers
When changing entire field definitions in client-side script (e.g. Overcoming relationship restrictions in Microsoft CRM v3.0 or Converting the country field to a combobox) the OnChange event code may not be triggered.
To overcome this situation, place the following in your script after you have replaced the HTML code:
crmForm.all.your_field.onchange = function() {
//add code here
}
If you need to initially call the OnChange code, use this instead:
your_field_OnChange = function() {
//add code here
}
//attaching the event
crmForm.all.your_field.onchange = your_field_OnChange;
//calling the event (same idea as FireOnChange())
your_field_OnChange();
Setting the field required level at runtime
You can access the current required level of any field by using the RequiredLevel property:
var reqLevel = crmForm.all.your_Field.RequiredLevel.
However, as it is a read-only property, you cannot use it to change the required level. There is an undocumented (and therefore unsupported) method on the crmForm object allowing to make a field required or not required:
function SetFieldReqLevel(sField, bRequired)
If bRequired is set to 0 (false), it is not required. If bRequired is set to anything else (true), the field is required.
Note: you cannot use this method to make a field business recommended. Here's the code to set a field to any of the possible states:
//No Requirement
//------------------------------
crmForm.all.your_field.setAttribute("req", 0);
crmForm.all.your_field_c.className = "n";
//Recommended
//------------------------------
crmForm.all.your_field.setAttribute("req", 1);
crmForm.all.your_field_c.className = "rec";
//Required
//------------------------------
crmForm.all.your_field.setAttribute("req", 2);
crmForm.all.your_field_c.className = "req";
Replace "your_field" and "your_field_c" with the name of the field you want to set, e.g. to set the accountnumber field to business recommended, you specify
crmForm.all.accountnumber.setAttribute("req", 1);
crmForm.all.accountnumber_c.className = "rec";
Setting a default time in a date field
A date field in a CRM form always contains a date part and optionally a time part. Sometimes it is useful to set a default time, like 8:30am, but unless the user has entered a valid date, the time selection box is disabled. Here's the code to set the default time once the user has specified the date part:
OnLoad
----------------------------------------
//check if the field exists on the form
if (crmForm.all.your_DateField != null) {
//save the value for future reference. Note that it is a global variable.
_previousValue = crmForm.all.your_DateField.DataValue;
}
OnChange of your date field
----------------------------------------
var dateField = crmForm.all.your_DateField;
var currentValue = dateField.DataValue;
//If the user changes the date field from null to a valid date, set the
//time portion to 8:30
if ((currentValue != null) && (_previousValue == null)) {
dateField.DataValue = new Date(currentValue.getYear(), currentValue.getMonth(), currentValue.getDate(), 8, 30);
}
//update _previousValue
_previousValue = currentValue;
I tried it with the scheduledend field of a task and it worked. The time combo of the scheduledend field is disabled as long as you put a date into the date field. This triggers the OnChange event and sets the default time.
User interaction with yes/no style message boxes
You can use window.confirm to present the user a yes/no-style dialog. The buttons are actually named "Ok" and "Cancel", so you have to use a good explanation in the message text. The result is either true (Ok) or false (Cancel):
var answer = window.confirm("Click Ok to proceed or Cancel to abort the operation.");
if (answer) {
//User selected OK
}
else {
//User selected Cancel
}
Testing if a lookup field has a value
It's the same as as for any other field type:
if (crmForm.all.your_field.DataValue == null) {
//code here
}
Comparing date values
It is a common mistake to directly compare two date values like this:
var date1 = new Date(2007, 4, 30);
var date2 = new Date(2007, 5, 1);
if (date1 > date2) {
//some code here
}
You may expect it to work, but you have to use the valueOf method of the Date object:
var date1 = new Date(2007, 4, 30);
var date2 = new Date(2007, 5, 1);
if (date1.valueOf() > date2.valueOf()) {
//some code here
}
Getting notified when the user enters a form field
The OnChange event of a form field is fired when you are leaving a field (and of course have changed the field value). If you want to perform an action when the users enters the field, use the onfocus event:
crmForm.all.your_field.onfocusin = function() {
alert("Received focus");
}
Overriding the click event of a lookup field
If you need to run code whenever a user clicks on a lookup button, use the following code to override the standard implementation of the click event:
//overrides the default click handler
crmForm.all.your_lookupField.onclick = function() {
alert("Lookup dialog is opening now");
//open the lookup dialog
crmForm.all.your_lookupField.Lookup(true);
alert("Lookup dialog closed");
}
Be careful with this as some lookup fields specify additional settings in their click events.
Formatting a date to YYYYMMDD
There is no toString() implementation in JavaScript allowing you to format a data value, so you have to build it on your own:
getYear returns the current year
getMonth returns the month starting from 0 for January to 11 for December
getDate returns the day of the month (1-31)
A common error is to use getDay instead of getDate. getDate returns the day of the week.
var now = new Date();
var year = now.getYear().toString();
var month = (now.getMonth() + 1).toString();
var dayOfMonth = now.getDate().toString();
if (month.length == 1) {
month = "0" + month;
}
if (dayOfMonth.length == 1) {
dayOfMonth = "0" + dayOfMonth;
}
var yyyymmdd = year + month + dayOfMonth;
The DataValue of a picklist is a string!
The DataValue of a picklist is a string, not an integer. You would expect it to be an integer as a picklist value is stored as an integer in the database and the Picklist class in the WSDL also specifies an integer value. Anyway when comparing the value of a picklist in client-side code, make sure to use a string value:
if (crmForm.all.your_picklist.DataValue == "1") {
}
Setting a picklist's default value in code
If a default option is specified in the attribute definition of a picklist, CRM assumes that you don't want to allow an empty value. This may not be true in all circumstances, so here's the workaround: In the attribute definition change the default value back to unassigned. This adds back the empty option in the picklist. Open the form's OnLoad event and add the following code:
if (crmForm.ObjectId == null) {
crmForm.all.your_picklist.DataValue = "1";
}
The code selects the first option in the picklist when inside a create form. It does not change the value once the form has been saved.
Starting an application from a CRM form
var shell = new ActiveXObject("WScript.Shell");
if (shell != null) {
shell.Run("c:\directory\application.exe " + crmForm.ObjectId);
}
You may face security issues preventing your code from being executed. An alternative is to use a custom .NET assembly as outlined in Using .NET assemblies in JavaScript code.
Changing the available entity types in a lookup dialog
Use one of the following in your OnLoad event:
//Allow only accounts to be selected
crmForm.all.regardingobjectid.setAttribute("lookuptypes", "1");
//Allow only contacts to be selected
crmForm.all.regardingobjectid.setAttribute("lookuptypes", "2");
//Allow accounts or contacts to be selected
crmForm.all.regardingobjectid.setAttribute("lookuptypes", "1,2");
It does not change the behavior of the form assistant, but the lookup dialog will not display any other entity. The attribute values are entity type codes and are documented in the SDK help file. You can use the code for any field allowing multiple entity types (usually customer fields and the regarding field).
Setting the text of the "Save As Completed" button
To display the text "Save as completed" in the toolbar button performing this action, put this code into the OnLoad event:
document.all._MBSaveAsCompleted.children[0].innerHTML += "Save as completed";
This is unsupported and may not work in the future.
Calculating durations
The following code subtracts the current date from a date specified on a CRM form and calculates the remaining or elapsed days, hours or minutes.
var displayField = crmForm.all.<name of a string field>;
var formDate = crmForm.all.<name of a datetime field>.DataValue;
var now = new Date();
var ms = formDate.valueOf() - now.valueOf();
var minutes = ms / 1000 / 60;
var hours = minutes / 60;
var days = hours / 24;
if (days >= 1) {
displayField.DataValue = Math.floor(days) + " day(s) left";
}
else if (hours >= 1) {
displayField.DataValue = Math.floor(hours) + " hour(s) left";
}
else if (minutes >= 1) {
displayField.DataValue = Math.floor(minutes) + " minute(s) left";
}
else if (days <= -1) {
displayField.DataValue = Math.floor(-days) + " day(s) late";
}
else if (hours <= -1) {
displayField.DataValue = Math.floor(-hours) + " hour(s) late";
}
else if (minutes <= -1) {
displayField.DataValue = Math.floor(-minutes) + " minute(s) late";
}
else {
displayField.DataValue = "NOW";
}
Hiding a single field
crmForm.all.<fieldname>_c.style.display = "none"; //hides the label
crmForm.all.<fieldname>_d.style.display = "none"; //hides the field
Setting a text field to the name of the entity referenced in a lookup control
if (crmForm.all.<lookupField>.DataValue == null) {
crmForm.all.<textField>.DataValue = null;
}
else {
crmForm.all.<textField>.DataValue = crmForm.all.<lookupField>.DataValue[0].name;
}
Getting notified when a user changes a checkbox value before leaving the field
The OnChange event of a bit field is fired when you leave the field. Often you want the event to be triggered as soon as the user clicks on a checkbox before tabbing out. Here's the code:
In the OnLoad event create a new event handler like this:
crmForm.all.your_checkboxfield.onclick = function() {
crmForm.all.your_checkboxfield.FireOnChange();
}
Obviously the onclick event is raised when clicking on the checkbox. Not so obvious is the fact that it also fires when you change the value using the keyboard (space bar). The above code calls your OnChange event handler after the value changed but before the control looses focus, so OnChange will be triggered again when you tab out. If that's a problem, place the existing OnChange event handler into the onclick event:
crmForm.all.your_checkboxfield.onclick = function() {
//add existing OnChange implementation here
}
Now you can deactivate your OnChange event.
Preventing an OnChange event handler from executing when the form closes
To prevent an OnChange event handler from running if the form is closing, add the following code to your OnLoad event:
//declaring a global variable
_windowClosing = false;
//onbeforeunload is called when you close the form but before the OnChange event is triggered
window.onbeforeunload = function() {
_windowClosing = true;
}
In your OnChange script, do the following:
if (!_windowClosing) {
//add your existing script code
}
Maximizing a form
Put the following two lines of code into any OnLoad event to maximize the form:
window.moveTo(0,0);
window.resizeTo(screen.availWidth, screen.availHeight);
moveTo moves the window to the specified location and resizeTo resizes it. screen is a global object and give you the available screen width and height in the corresponding properties.
Using a toolbar button to open a referenced entity
Let's say you have added a lookup field to a form referencing one of your custom entities and you have added a toolbar button in isv.config.xml. When clicked it should open the entity shown in the lookup field, which basically is the same as clicking the link in the lookup itself.
Here's the code:
var lookup = crmForm.all.your_lookup_field;
if ((lookup != null) && (lookup.DataValue != null)) {
var objectTypeCode = lookup[0].type;
var objectId = lookup[0].id;
var url = '/userdefined/edit.aspx?id=" + objectId + '&etc=' + objectTypeCode;
window.open(url);
}
Note that when using a system entity, you have to replace /userdefined/edit.aspx with the appropriate edit URL of the system entity. The code first checks for the availability of the lookup field. If it's not included on the form (lookup will be null) or no data value has been set then no action is performed; otherwise the complete edit URL is stored in the url variable and passed to the window.open method.
Calculating the sum of two or more fields
Though it seems straightforward to sum up field values in a form, you can easily run into problems with null values. Here's a sample script that sums up three fields (your_field1, your_field2, your_field3) and stores the sum in your_sum:
//A field is accessed with crmForm.all.<the_field_name>
//A field value is accessed through it's DataValue property
var value1 = crmForm.all.your_field1.DataValue;
var value2 = crmForm.all.your_field2.DataValue;
var value3 = crmForm.all.your_field3.DataValue;
//The DataValue of an empty field is null, so in order to
//sum up the values, you have to check for null values
value1 = (value1 == null) ? 0 : value1;
value2 = (value2 == null) ? 0 : value2;
value3 = (value3 == null) ? 0 : value3;
//Setting a value follows the same rules used for retrieving
crmForm.all.your_sum.DataValue = value1 + value2 + value3;
Calculating the total charge based on actural duration, hourly rate, trip charge and tax rate
Instead of trying to make this one generic, I'm repeating the original question:
"I created 4 attributes under the Case entity as follows:
• new_hourlyrate - picklist (95.00 and 125.00 for values)
• new_taxrate - picklist (.06 and .07 for values)
• new_tripcharge - picklist (0.00, 15.00, 30.00, 60.00 for values)
• new_totalcharge - money
I need to populate the totalcharge field based on the actualdurationminutes attribute with the following math equation:
totalcharge = actualdurationminutes/60 (to get hours) multiplied by the hourly rate, add the tripcharge, multiplied by the tax rate. Take that value and add it to the hourly rate multiplied by the hours, and add the trip charge, to get total charge."
And here's the code:
var hourlyRateField = crmForm.all.new_hourlyrate;
var taxRateField = crmForm.all.new_taxrate;
var tripChargeField = crmForm.all.new_tripcharge;
var totalChargeField = crmForm.all.new_totalcharge;
var actualDurationMinutesField = crmForm.all.actualdurationminutes;
//Sanity check: if at least one of the fields is not available on the form,
the following condition is not met
if (hourlyRateField && taxRateField && tripChargeField && totalChargeField && actualDurationMinutesField) {
var hourlyRate = (hourlyRateField.DataValue == null) ? 0 : parseFloat(hourlyRateField.SelectedText);
var taxRate = (taxRateField.DataValue == null) ? 0 : parseFloat(taxRateField.SelectedText);
var tripCharge = (tripChargeField.DataValue == null) ? 0 : parseFloat(tripChargeField.SelectedText);
var actualDurationMinutes = (actualDurationMinutesField.DataValue == null) ? 0 : parseFloat(actualDurationMinutesField.DataValue);
var totalCharge = (actualDurationMinutes/60 * hourlyRate) + tripCharge;
var totalTax = totalCharge * taxRate;
totalChargeField.DataValue = totalCharge + totalTax;
}
Note that in the above code parseFloat uses the SelectedText of the picklists instead of the DataValue.
Changing error messages in CRM forms
Sometimes the error messages displayed when entering an incorrect value may not be correct. An example from a Dutch system: when a user tries to input a date by hand, for example 14/05/1998 an error is displayed because the correct format is 14-05-1998. However the error message tells you to specify the date as D/M/YYYY, which isn't correct.
You can change these error messages on the fly by simply replacing the appropriate variable in OnLoad. Note the error message and search for it in in the page source. You will find something like this:
var LOCID_ALERT_ENTER_VALID_DATE = "De opgegeven datum is ongeldig. voer een datum in met de notatie: D/M/YYYY.";
To change it, put the following line in your OnLoad event:
LOCID_ALERT_ENTER_VALID_DATE = "De opgegeven datum is ongeldig. voer een datum in met de notatie: DD-MM-YYYY.";
Setting a custom date field to another date minus 60 days
Date calculation problems are still popping up in the newsgroups, so here's another quick example. Let's say you want to calculate a date based on the value of the effectiveto field in your crmForm minus 60 days. Here's the code:
var effectiveTo = crmForm.all.effectiveto.DataValue;
var remindOn = new Date(
effectiveTo.getYear(),
effectiveTo.getMonth(),
effectiveTo.getDate() - 60);
or
var effectiveTo = crmForm.all.effectiveto.DataValue;
var remindOn = new Date(
effectiveTo.getYear(),
effectiveTo.getMonth() - 2,
effectiveTo.getDate());
The difference between the codes is that the first subtracts exactly 60 days, whereas the second subtracts two months, which is between 58 and 62 days.
Changing the default height of the lookup window
This is an unsupported change, but if you want to change the initial size of a lookup window, open /_controls/lookup/lookup.js in the CRM web and search for the function BuildFeatures(lookupStyle). Inside of this function search for the following:
switch (lookupStyle)
{
case "multi":
oFeatures.height = "460px";
oFeatures.width = "600px";
break;
case "single":
oFeatures.height = "488px";
oFeatures.width = "600px";
break;
oFeatures.height and oFeatures.width are the initial lookup dimensions. After changing them, clear your browser cache to reload the include files the next time a lookup is accessed, otherwise IE will still use the cached values and you don't see a difference.
Accessing the previous field value in OnChange
Sometimes you need to know the previous field value in an OnChange event or you need to know the initial value after the form has loaded. This information of course is lost in OnChange, as the field value already has changed. Here's a simple workaround:
// OnLoad event
// no var statement here to declare a global variable
_oldDateValue = crmForm.all.the_fieldName.DataValue;
// OnChange event
if (_oldDateValue == null) {
//no previous value
}
else {
var currentValue = crmForm.all.the_fieldName.DataValue;
DoStuff(currentValue, _oldValue);
//update the old value with the new value, if appropriate
_oldDateValue = currentValue;
}
Automatically calculate the tax value in an invoice line (invoicedetail)
CRM does not calculate the tax amount for you and if you want to automate it you have to add custom script. Sp here's an example for the invoicedetail. In the OnLoad event of the invoicedetail form add the following:
CalculateTax = function() {
var pricePerUnit = 0;
var quantity = 0;
var manualDiscount = 0;
if (crmForm.all.priceperunit.DataValue != null) {
pricePerUnit = crmForm.all.priceperunit.DataValue;
}
if (crmForm.all.quantity.DataValue != null) {
quantity = crmForm.all.quantity.DataValue;
}
if (crmForm.all.manualdiscountamount.DataValue != null) {
manualDiscount = crmForm.all.manualdiscountamount.DataValue;
}
crmForm.all.tax.DataValue = (pricePerUnit * quantity - manualDiscount) * 0.175;
}
In the OnChange events of priceperunit, quantity and manualdiscountamount add:
CalculateTax();
Change 0.175 (17.5%) to the tax you need to charge. You can also add a new field instead of using a fixed value in the script code.
Performing an action when a CRM form closes
If you want to execute your script whenever the form closes, whether it is saved or not, you can subscribe to the onunload event:
window.onunload = function() {
//add code here
}
Hooking into the "Lookup Address" feature in the order form
To get notified when a user presses the "Lookup Address" button in the order form, put this code into the order's OnLoad event:
if (document.all._MBLookupAddress != null) {
document.all._MBLookupAddress.onclick = function() {
LookupAddress();
alert("Address lookup closed");
}
}
The alert pops up after the address lookup dialog closes but there's no way to distinguish if the user selected an address or canceled the dialog. Of course this is an unsupported customization as it uses undocumented functions.
Changing the form title (not the browser title)
A CRM form displays the primary field of an entity in a large bold font just below the toolbar buttons. If you want to change the displayed text, put the following code into your OnLoad event:
var cells = document.getElementsByTagName("td");
for (var i = 0; i < cells.length; i++) {
if (cells[i].className == "formTitle") {
cells[i].innerText = "Ticket: 123456";
break;
}
}
Passing parameters from a toolbar button to a CRM form
Sometimes you add buttons to a form's toolbar that simply create a new entity. However in the created entity you need to know if it was created from your toolbar button or not. Here's an easy solution to pass an additional parameter that you can check in the OnLoad entity of the new entity form.
First of all let's start with the toolbar button. It usually has a Url attribute like "/userdefined/edit.aspx?etc=10018". To differentiate add an additional parameter like "/userdefined/edit.aspx?etc=10018&template=1".
Note: using the entity type code is dangerous, as it may break when deploying your solution to another server.
In the OnLoad event of the target entity (with object type code 10018) use the following code to extract the template parameter from the query string:
var QueryString = ParseQueryString();
var template = QueryString["template"];
alert(template);
if (template == "1") {
// "New Template" clicked
}
else {
// "New" clicked
}
function ParseQueryString() {
var dict = new Object();
if ((document.location.search != null) && (document.location.search != "?")) {
var qsParts = document.location.search.substr(1).split("&");
var index;
for(index in qsParts) {
var keyValue = qsParts[index].split("=");
dict[keyValue[0]] = unescape(decodeURIComponent(keyValue[1]));
}
}
return dict;
}
The basic idea is passing additional parameters to the form and reading them in OnLoad. As the default "New" button does not add the template parameter, you can use it as an indicator which button initiated the creation of the new entity.
Aborting an OnChange operation
"I populated a picklist with some values and add code to the OnChange() event. When users change the picklist value by selection in the dropdown, a message box will pop up and ask the user to confirm the change. If the user chooses No, how could I restore the original value selected in the picklist?"
// OnLoad
// Note that the "var" keyword is missing intentionally to declare prevPicklistValue as a global variable
prevPicklistValue = crmForm.all.the_picklist.DataValue;
// OnChange of prevPicklistValue
var currentPicklistValue = crmForm.all.the_picklist.DataValue;
if (prevPicklistValue == currentPicklistValue) {
//Reaching this line when restoring the previous value
return;
}
var answer = window.confirm("Click Ok to proceed or Cancel to abort the operation.");
if (answer) {
//User selected OK -> Save the current value as the last accepted value
prevPicklistValue = currentPicklistValue;
}
else {
//User selected Cancel -> Restore the previous value.
crmForm.all.the_picklist.DataValue = prevPicklistValue
}
Disabling the time selection of a date/time field
You can dynamically enable or disable the time selection box of a date/time field using the following syntax:
var dateField = crmForm.all.<name of datetime field>;
//Check the existence of the time field. It is null if the control is setup to only display the date.
if (dateField.all.time != null) {
//Disable the time field
dateField.all.time.disable();
//Enable the time field
dateField.all.time.enable();
}
CRM automatically enables the time selection box if the date value changes to a non-null value, so in order to always disable the time selection box, you need to add the following OnChange event script to the datetime field:
var dateField = crmForm.all.<name of datetime field>;
//Check the existence of the time field. It is null if the control is setup to only display the date.
if (dateField.all.time != null) {
//Disable the time field
dateField.all.time.disable();
}
To initially disable the time field, put the following into the form OnLoad event:
var dateField = crmForm.all.<name of datetime field>;
//Check the existence of the datetime field. It may not be included in a quick create form!
if (dateField != null) {
//Call the OnChange event handler
dateField.FireOnChange();
}
Changing the time interval in date/time fields
The time selection box of a date/time field uses a 30 minute interval. You can change it to a different interval using the following code in an OnLoad event:
var dateField = crmForm.all.<name of datetime field>;
//Check the existence of the datetime field. It may not be included in a quick create form!
if (dateField != null) {
var timeField = dateField.all.time;
//Check the existence of the time field. It is null if the control is setup to only display the date.
if (timeField != null) {
//The new interval in minutes.
var interval = 15;
var tables = timeField.getElementsByTagName("table");
if ((tables != null) && (tables.length > 0)) {
var table = tables[1];
//Remove all existing values from the selection box
while (table.firstChild != null) {
table.removeChild(table.firstChild);
}
//Add the new values
for (hour = 0; hour < 24; hour++) {
for (min = 0; min < 60; min += interval) {
var row = table.insertRow();
var cell = row.insertCell();
var time = ((hour < 10) ? "0" : "") + hour + ":" + ((min < 10) ? "0" : "") + min;
cell.setAttribute("val", time);
cell.innerText = time;
}
}
}
}
}
Getting the quote id inside a quotedetail form
There's a hidden field in the quotedetail form storing the quote id. You can access it using
alert("Quote ID = " + crmForm.all.quoteid.DataValue);
How to know which button triggered the OnSave event
You can use the event.Mode property in the OnSave event. It will tell you which button was used to save an entity. Not all values are documented in the SDK, e.g the "Save as completed" in a phone call has a value of 58. As it's not documented, this number may change in future releases.
To get started enter the following into your OnSave script:
alert ("event.Mode = " + event.Mode);
event.returnValue = false;
return false;
This codes makes it impossible to save the entity, but allows you to write down all of the values passed to OnSave. You will see 1 for a normal save operation and 2 for Save and Close. Most but not all constants are defined in /_common/scripts/formevt.js.
Accessing the CRM database from JavaScript
To access the CRM database from JavaScript, use the following as a template:
var connection = new ActiveXObject("ADODB.Connection");
var connectionString = "Provider=SQLOLEDB;Server=STUNNWARECRM;Database=stunnware_mscrm;Integrated Security=sspi";
connection.Open(connectionString);
var query = "SELECT name FROM FilteredAccount";
var rs = new ActiveXObject("ADODB.Recordset");
rs.Open(query, connection, /*adOpenKeyset*/1, /*adLockPessimistic*/2);
rs.moveFirst();
var values = "";
while (!rs.eof) {
values += rs.Fields(0).Value.toString() + " ";
rs.moveNext();
}
connection.Close();
alert(values);
You may need to adjust your security settings in IE in order to run this code.
Modifying the Quick Create Form
Allthough the quick create form isn't listed in the forms and views dialog, it uses the same scripts entered in the main application form. You can use the following in the OnLoad script to run code when inside of a quick create form:
if (crmForm.FormType == 5 /* Quick Create Form */) {
//modify the form using DHTML
}
Forcing the selection of an account in the potential customer field of an opportunity
Put the following into the opportunity's OnLoad event:
crmForm.all.customerid.setAttribute("lookuptypes", "1");
This tells the customer lookup to only include the account (object type code 1). A value of 2 forces the lookup to only display contacts.
Hiding and showing fields dynamically based on other field values
You can do this in client-side JavaScript. Let's say you have a picklist named "new_category" and you have assigned the following options to it:
1 - Business
2 - Private
3 - Unspecified
Let's further assume that you want to hide the fields "new_a" and "new_b" whenever a user selects "Private", but they should be visible if a different option is selected in the picklist. Your OnChange event script of the new_category field will be:
var hideValues = (crmForm.all.new_category != null) && (crmForm.all.new_category.DataValue == "2");
var displayStyle = hideValues ? "none" : "";
crmForm.all.new_a.style.display = displayStyle;
crmForm.all.new_b.style.display = displayStyle;
To run the code when the form is initially loaded, put the following into the OnLoad event:
if (crmForm.all.new_category != null) {
crmForm.all.new_category.FireOnChange();
}
The test for a null value of crmForm.all.new_category is included to avoid errors if the field is not available on a form. This is true for quick create forms if the field required level is not set to business required or business recommended.
Removing the value from a lookup field
crmForm.all.<lookupField>.DataValue = null;
Hiding tabs at runtime
You can hide and show entire tabs dynamically using the following code:
//Add your condition here, usually a field comparison in an OnChange event
if (condition == true) {
//hide the second tab
crmForm.all.tab1Tab.style.display = "none";
}
else {
//show the second tab
crmForm.all.tab1Tab.style.display = "";
}
The tabs are named "tab0Tab", "tab1Tab", "tab2Tab" and so on.
Setting the active tab
From the SDK docs:
SetFocus: Sets the focus, changes tabs, and scrolls the window as necessary to show the specified field. Example:
// Set focus to the field.
crmForm.all.SOME_FIELD_ID.SetFocus();
Displaying related entities instead of the form when opening a record
To open a related entity view of the left navigation bar (like contacts in an account entity), you can use the following:
loadArea("areaContacts");
Each entry in the navigation bar has a unique area name (areaContacts is just a sample). If you haven't already done it, download and install the Internet Explorer Developer Toolbar to find the name of the entry you're after. Some instructions on how to use it are in http://www.stunnware.com/crm2/topic.aspx?id=CrmLookAndFeel1; though it is a different topic, I included some screenshots using the developer toolbar in CRM.
Modifying the color of disabled form fields
Disabled form fields are sometimes hard to read. Making the color a little bit darker greatly helps to read all of the content without loosing the information that a field is read-only.
Open the following file in your CRM web: /_forms/controls/controls.css. Then find the following style:
INPUT.ro,TEXTAREA.ro,DIV.ro,SPAN.ro
{
background-color: #ffffff;
color: #808080;
border-color: #808080;
}
The color attribute is the light gray you're seeing by default. You may change it to #404040 to get a slightly darker gray. If you don't see the new colors in IE, hit CTRL-F5 to reload the source files, including the updated css file.
Removing a navigation bar entry at runtime
To remove a navigation bar entry dynamically, you can use the following code:
var navigationBarEntry = document.getElementById("navProds");
if (navigationBarEntry != null) {
var lbArea = navigationBarEntry.parentNode;
if (lbArea != null) {
lbArea.removeChild(navigationBarEntry);
}
}
If you haven't already done it, download and install the Internet Explorer Developer Toolbar to find the name of the navigation bar entry.
Changing the color of a label
var label = crmForm.all.<field name>_c;
label.innerHTML = "<font color='#FF0000'>" + label.innerText + "</font>";
It's time to continue the "More JavaScript" series. Here's the fifth article containing more snippets and I hope that you find them as valuable as the four previous articles. I also updated the JavaScript Snippets Directory.
Formatting international phone numbers
The CRM SDK contains a sample to format US phone numbers and it works pretty well. However, there are customers outside the US and the sample doesn't work with international phone numbers. An easy formatting rule is replacing any occurrence of '(', ')' or a space with a dash. People can then enter the phone number in their preferred way, but get the same output.
var originalPhoneNumber = "+49 (89) 12345678";
var formattedPhoneNumber = originalPhoneNumber.replace(/[^0-9,+]/g, "-");
formattedPhoneNumber = formattedPhoneNumber.replace(/-+/g, "-");
alert(formattedPhoneNumber);
The first call to the replace method changes every character in the input string that is not a digit and not the plus sign (which is used for international
numbers) to the dash symbol. However, the output is +49--89--12345678, so the second call replaces all occurrences of multiple dashes with a single
one, giving a final result of +49-89-12345678.
Rounding numerical fields
Rounding fields is done similar to any other programming language. The following method rounds a float value using a precision of two decimal places:
function round(value) {
return Math.round(value * 100) / 100;
}
The Math object defines three methods for rounding operations:
• Math.ceil(arg): Returns an integer value equal to the smallest integer greater than or equal to its numeric argument.
• Math.floor(arg): Returns an integer value equal to the greatest integer less than or equal to its numeric argument.
• Math.round(arg): If the decimal portion of number is 0.5 or greater, the return value is equal to the smallest integer greater than number. Otherwise, round returns the largest integer less than or equal to number.
Be aware of null values in Boolean fields
When working with Boolean fields it's seems natural to compare the value to either true or false:
var value = crmForm.all.my_bool.DataValue;
if (value == true) {
//do something
}
else {
//do something else
}
However, the value may also be null and you should make sure that your code handles it correctly:
var value = crmForm.all.my_bool.DataValue;
if (value == null) {
//do appropriate steps if there is no value
}
else if (value == true) {
//do something
}
else {
//do something else
}
If you want to execute either the "true" part or the "false" part when no value is set, then I recommended the following (including the comment, to make it obvious):
var value = crmForm.all.my_bool.DataValue;
//Default to true if no value is set
if (value == null) {
value = true;
}
if (value == true) {
//do something
}
else {
//do something else
}
Reusing code in OnLoad and OnChange event handlers
I often see code like this:
OnLoad:
if (crmForm.all.my_lookup_field.DataValue != null) {
crmForm.all.my_text_field.DataValue = crmForm.all.my_lookup_field.DataValue[0].name;
}
OnChange:
if (crmForm.all.my_lookup_field.DataValue != null) {
crmForm.all.my_text_field.DataValue = crmForm.all.my_lookup_field.DataValue[0].name;
}
The code is identical in both events: it copies the display name of the selected item in a lookup field to a text box. Later you notice that it doesn't remove an existing value in the text field when the user removes the lookup selection and change the OnChange code to this:
if (crmForm.all.my_lookup_field.DataValue != null) {
crmForm.all.my_text_field.DataValue = crmForm.all.my_lookup_field.DataValue[0].name;
}
else {
crmForm.all.my_text_field.DataValue = null;
}
It sometimes happens that you forget to change the OnLoad code as well, so instead of copy paste the code between OnChange and OnLoad, you should use one of the following implementation styles:
1. Implementing the code in the OnChange event handler and using FireOnChange to execute it in the OnLoad event:
OnLoad
crmForm.all.my_lookup_field.FireOnChange();
OnChange
if (crmForm.all.my_lookup_field.DataValue != null) {
crmForm.all.my_text_field.DataValue = crmForm.all.my_lookup_field.DataValue[0].name;
}
else {
crmForm.all.my_text_field.DataValue = null;
}
2. Implementing the code in OnLoad and calling it from the OnChange event:
OnLoad
MyLookup_OnChange = function() {
if (crmForm.all.my_lookup_field.DataValue != null) {
crmForm.all.my_text_field.DataValue = crmForm.all.my_lookup_field.DataValue[0].name;
}
else {
crmForm.all.my_text_field.DataValue = null;
}
}
MyLookup_OnChange();
OnChange
MyLookup_OnChange();
The second implementation has the benefit that all of your code is in a single place.
Tip: The code can be rewritten to this:
var lookupValue = crmForm.all.my_lookup_field.DataValue;
crmForm.all.my_text_field.DataValue = (lookupValue == null) ? null : lookupValue[0].name;
Getting notified when the user selected an address in the address picker (quote, order, invoice)
When working with quotes, orders and invoices, you pick an address to specify the bill to and ship to address. Though the address fields are properly filled with the selected values, no OnChange event is executed in the CRM form. Here's an easy but unsupported way to be informed when the user closes the address lookup dialog:
if (document.all._MBLookupAddress != null) {
document.all._MBLookupAddress.onclick = function() {
LookupAddress();
alert("Address lookup closed");
}
}
You don't know if the user selected an address or canceled the operation though. You can use the same idea to override other built-in functions, but as said, it's unsupported and may break in future releases.
What is "if (condition) ? statement1 : statement2"?
I'm using the above notation a lot because it's an easy way to set a value to one out of two values. This construct is available in C, C++, Java, C# and I'm sure that most other languages have similar commands. I noticed though that it's not as as clear as I thought, so here's the same using a standard if/then/else:
if (condition) {
statement1;
}
else {
statement2;
}
And here's a real example:
var s = (crmForm.all.my_lookup.DataValue == null) ? null : crmForm.all.my_lookup.DataValue[0].name;
And the same with an if/then/else:
var s;
if (crmForm.all.my_lookup.DataValue == null) {
s = null;
}
else {
s = crmForm.all.my_lookup.DataValue[0].name;
}
Copying the display name of a selcted lookup value into a textbox
See the sample above.
Retrieving all fields inside a CRM form
If you want to loop over all fields (input fields) on a CRM form, you can use the following script as a starting point:
for (var index in crmForm.all) {
var control = crmForm.all[index];
if (control.req && (control.Disabled != null)) {
//control is a CRM form field
}
}
The conditions mean that a control must have the "req" attribute and the "Disabled" method. This seems a good indicator for a CRM form field.
Knowing if you are running in CRM 3.0 or CRM 4.0
It's fairly easy to differentiate if your code is running on CRM 4.0 or not. Just pick a method or variable that did not exist in CRM 3.0 and check if it is available:
if (typeof(GenerateAuthenticationHeader) == "undefined") {
alert("Version 3");
}
else {
alert("Version 4");
}
GenerateAuthenticationHeader was introduced in CRM 4.0 and is a global function available in all forms.
Showing/Hiding tabs based on the selection in a picklist
The next script shows one out of three tabs based on the selection in the new_combo field. It hides all tabs if no selection is made or a different value is selected.
OnLoad:
//Sanity check: if new_combo is not present on the form, then don't call FireOnChange
if (crmForm.all.new_combo != null) {
crmForm.all.new_combo.FireOnChange();
}
OnChange:
//Check for create, update, read-only or disabled form
if ((crmForm.FormType >= 1) && (crmForm.FormType <= 4)) {
var value = crmForm.all.new_combo.DataValue;
crmForm.all.tab1Tab.style.display = (value == "1") ? "none" : "";
crmForm.all.tab2Tab.style.display = (value == "2") ? "none" : "";
crmForm.all.tab3Tab.style.display = (value == "3") ? "none" : "";
}
Changing the background color of a form (CRM 4.0)
Instead of explaining in detail, simply copy the following code into the OnLoad event:
document.all.areaForm.style.backgroundColor = 'yellow';
document.all.tab0.style.backgroundColor = 'red';
document.all.tab1.style.backgroundColor = 'blue';
document.all.tab2.style.backgroundColor = 'green';
document.all.tab3.style.backgroundColor = 'cyan';
The above is for an entity with 4 tabs, like the default account form. If you have less tabs, then remove some of the lines at the end (tab0 = the first tab, tab1 = the second tab, ...).
Instead of using color names, you can also specify RGB values, e.g.
document.all.tab0.style.backgroundColor = '#A040FF';
Calculating the difference of two numerical fields
Seems easy enough:
var result = crmForm.all.num_field1.DataValue - crmForm.all.num_field2.DataValue;
It will break though if either of the two fields is null. Use the following instead:
var fieldValue1 = (crmForm.all.num_field1.DataValue == null) ? 0 : crmForm.all.num_field1.DataValue;
var fieldValue2 = (crmForm.all.num_field2.DataValue == null) ? 0 : crmForm.all.num_field2.DataValue;
var diff = fieldValue1 - fieldValue2;
You can use a different notation to check for the null value:
var fieldValue1 = crmForm.all.num_field1.DataValue ? 0 : crmForm.all.num_field1.DataValue;
var fieldValue2 = crmForm.all.num_field2.DataValue ? 0 : crmForm.all.num_field2.DataValue;
var diff = fieldValue1 - fieldValue2;
It depends on your coding style which version you prefer. The second is smaller but doesn't really tell what you are comparing, while the first explicitly cheks for a null value.
Disable all fields on a form
This is just a variation of the code shown in the "Retrieving all fields inside a CRM form" sample:
for (var index in crmForm.all) {
var control = crmForm.all[index];
if (control.req && (control.Disabled != null)) {
control.Disabled = true;
}
}
When events do not fire anymore
If your OnLoad, OnSave or OnChange code isn't executed at all, make sure that you have enabled the event first. It's always a good idea to check the obvious things first. If you have enabled the event and are not testing the code in the form preview, then ask yourself if you have published the changes.
If your code still isn't executed, place an alert('TEST'); as the first line into your code and try again. If you don't see the alert message, then you have a syntax problem in your code. The most common reason is a missing curly brace somewhere in your code and it may be in any event you have added to the CRM form. It may also be related to an inline comment using the "// my comment" notation. Try using "/* my comment */" instead.