Introduction
CRM2011 provides the following out-of-the-box event handlers:- At a form level, OnLoad and OnSave
- At a field level, OnChange.
As a seasoned event-driven developer, I find this depressing. I want more. For example, I want to be able to modify some other field as I type. Or, I want to execute complex validations involving multiple fields, without cluttering up the form.
This article shows how to do that.
To understand the steps in this article it would be beneficial to first review this article: Create a general use Javascript library for CRM 2011
The concept
This solution revolves around the idea that you can register event handlers in Javascript.
Here is a very simple web page with an event handler:
<HTML>
<BODY>
<label id="Label1" onClick="DoTheThing()">Click
me</label>
<SCRIPT>
function DoTheThing() {
alert('thing was done');
}
</SCRIPT>
</BODY>
</HTML>
However, in CRM I do not have access to the onClick event handler. But, I do have access to the CRM form's onLoad event; which fires when the form is loaded. This is where you will register the onClick event for the form to fire when the user clicks the form.
This is what it looks like with simple HTML; on load of the body the events were wired up dynamically. The advantage here is that I did not know need access to the label control by code, but I can "reach" it from the onload event of the body.
<HTML>
<BODY onload="SetupEvents()">
<label id="myLabel"
>Click me</label>
</BODY>
<SCRIPT>
function DoTheThing() {
alert('thing was done');
}
function SetupEvents() {
var label = document.getElementById('myLabel');
label.attachEvent('onclick',
DoTheThing);
}
</SCRIPT>
</HTML>So this is what will be used in the CRM form - we can access the html field's events by injecting it into the OnLoad event of the form.
Script library
Firstly, add the following scripts as a general function library in the solution's web resources. Call it FcbGenericScripts.
//=======================================================================
//========================[ Generic
library ]============================ //=======================================================================
// Read a page's GET URL variables and return them as
an associative array.
// For example:
//
http://www.example.com/?me=myValue&name2=SomeOtherValue
// Calling getUrlVars() function would return you the
following array:
// {
// "me" : "myValue",
// "name2"
: "SomeOtherValue"
// }
// To get a value of first parameter you would do this:
// var
first = getUrlVars()["me"];
// To get the second parameter:
// var
second = getUrlVars()["name2"];
function getUrlVars() {
var vars = [], hash;
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
for (var i = 0; i
< hashes.length; i++) {
hash
= hashes[i].split('=');
vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
}
//=======================================================================
// This receives the passed in context from the form,
(done with the UI form designer, use the checkbox)
// then interrogates the context to which entity member
fired the event, then
// extracts the value from that entity control,
// strips out the formatting crap and makes sure it is
a real number.
// If the number is proper (10 numeric) it formats the number back into a string
// then writes the string back to the control.
//If the number is not proper, it squeals at the user.
//
function FormatPhoneNumber(context) {
var oField = context.getEventSource().getValue();
var sAllNumeric = oField;
if (typeof (oField)
!= "undefined" && oField !=
null) {
sAllNumeric = oField.replace(/[^0-9]/g, "");
switch (sAllNumeric.length) {
case 10:
sFormattedPhoneNumber = "(" + sAllNumeric.substr(0, 3) + ") " + sAllNumeric.substr(3, 3) + "-" + sAllNumeric.substr(6, 4)
break;
default:
alert("Phone must contain 10
numbers.")
break;
}
}
context.getEventSource().setValue(sFormattedPhoneNumber);
}
//=======================================================================
//This returns a reference to the control that is
hosting the XRM entity object
function getEntityFieldControl(sEntityFieldName,
bShowErrorOnFail) {
var returnValue =
document.getElementById(sEntityFieldName);
if (returnValue == null)
{
if (bShowErrorOnFail)
alert('Could not locate the html control
named ' + sEntityFieldName);
return null;
}
return returnValue;
}
//=======================================================================
//This returns a reference to the XRM entity object
itself
function getEntityFieldObject(sEntityFieldName,
bShowErrorOnFail) {
var returnValue =
Xrm.Page.getAttribute(sEntityFieldName)
if (returnValue == null)
{
if (bShowErrorOnFail)
alert('Could not locate the entity object
named ' + sEntityFieldName);
return null;
}
return returnValue;
}
//=======================================================================
//This returns the *value* of the XRM entity object's
*control*
//This is sometimes necessary, because the XRM DOM is
not updated immediately as the field's contents are changing.
function getEntityFieldControlValue(sEntityFieldName,
bShowErrorOnFail) {
var entityFieldObjectControl =
getEntityFieldControl(sEntityFieldName, bShowErrorOnFail)
if (entityFieldObjectControl == null) {
return (null);
}
else {
return entityFieldObjectControl.value;
}
}
//=======================================================================
//This returns the *value* of the XRM entity object
function getEntityFieldValue(sEntityFieldName,
bShowErrorOnFail) {
var entityFieldObject =
getEntityFieldObject(sEntityFieldName, bShowErrorOnFail)
if (entityFieldObject == null)
{
return ("");
}
else {
return entityFieldObject.getValue();
}
}
//=======================================================================
//This sets the *value* of the XRM entity object, which
hopefully will update the control too.
function setEntityFieldValue(sEntityFieldName,
sNewValue, bShowErrorOnFail) {
var entityFieldObject =
getEntityFieldObject(sEntityFieldName, bShowErrorOnFail);
if (entityFieldObject != null)
{
entityFieldObject.setValue(sNewValue);
}
}
//=======================================================================
//Sets up the control of an entity object to execute
another javascript method when a specified JS event occurs to the control
//i.e. "execute my javascript called
ValidateEmail() method when the user modifies the email address. (onkeyup)
function
registerEntityFieldEventHandler(sEntityFieldName, sTriggeringEvent,
oMethodToCall, bShowErrorOnFail) {
var entityFieldControl =
getEntityFieldControl(sEntityFieldName, bShowErrorOnFail)
if (entityFieldControl != null)
{
entityFieldControl.attachEvent(sTriggeringEvent,
oMethodToCall);
}
}
fcbCustomJs =
{
getUrlVars: function () {
var vars = [], hash;
var hashes =
window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
for (var i = 0; i
< hashes.length; i++) {
hash =
hashes[i].split('=');
vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
},
isEmpty: function (val) {
if (val) {
return ((val === null)
|| val.length == 0 || /^\s+$/.test(val));
} else {
return true;
}
},
replaceData: function (s) {
var arr1 = new Array('%20', '%21', '%40', '%23', '%24', '%25', '%5e', '%26', '%2a', '%28', '%29', '%2b', '%3d', '%2c', '%3c', '%3e', '%2f', '%3f', '%5b', '%5d', '%7b', '%7d', '%3a', '%7c');
var arr2 = new Array(' ', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '+', '=', ',', '<', '>', '/', '?', '[', ']', '{', '}', ':', '|');
if (this.isEmpty(s)) return "";
var re;
for (var x = 0, i =
arr1.length; x < i; x++) {
re = new RegExp(arr1[x], 'g');
s =
s.replace(re, arr2[x]); //swap arr1 item with
matching item from arr2
}
return s;
}
}
|
Ok - all that library does is provide a javascript with the ability to interact with the XRM DOM field objects when you know the name of the CRM field. In addition, it provides a wrapper for attaching events to the XRM fields, when we know what the XRM field name is. Its just a library at this point, it does not do anything on its own. But, the next script will make use of it.
Now add another web resource, but this time this is not a generic library. It is specific to the entity I want to update, but it makes use of some of the functionality in the generic library we added previously. Call it jc_ServiceScripts. For the purpose of this example, assume there are 3 custom fields that are involved here: fcb_rangelow, fcb_rangehigh and jc_defaultquantity
Now add another web resource, but this time this is not a generic library. It is specific to the entity I want to update, but it makes use of some of the functionality in the generic library we added previously. Call it jc_ServiceScripts. For the purpose of this example, assume there are 3 custom fields that are involved here: fcb_rangelow, fcb_rangehigh and jc_defaultquantity
|
Ok - now both scripts are on the page but dont do anything yet. The last step is to plug in the SetupEvents function into the CRM form's onload event.
On the same form, click the Add button for the Event handlers for the page, making sure that the control is Form and the event is OnLoad. Specify the Library as jc_ServiceScripts and the function name as setupEvents. Note - do not put parenthesis on the function name, otherwise the page will crash.
On the same form, click the Add button for the Event handlers for the page, making sure that the control is Form and the event is OnLoad. Specify the Library as jc_ServiceScripts and the function name as setupEvents. Note - do not put parenthesis on the function name, otherwise the page will crash.
No comments:
Post a Comment