Monday, July 21, 2008

Client Side Scripting - More JavaScript Code - Part 3 (STUNNWARE)

STUNNWARE

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
}
 

No comments: