04 Mar
Posted by: mitch in: Customization, Dynamics CRM
In the article, Including custom JavaScript files in MSCRM 3.0, I discussed using an external JavaScript code library to help reduce maintenace costs. Seemed like a good idea, at the time. Then I got to thinking about the CRM Laptop client and how it is disconnected from the main CRM server and realized that I had just created an unsupportable and unviable installation.
Shorely thereafter, Michaeljon Miller posted an article discussing the same situation: Using the CRM SDK offline
So I'm thinking I'm relatively screwed, uh, technically speaking, of course.
I was also starting work on a custom mortgage calculation entity that contained about 40 different formulas. And it got me to thinking about how, and more importantly, where, I was placing my JavaScript calculation code.
I was basically converting an Excel worksheet that had all of the calculations necessary to make the Mortgage Calculator function. The problem is, Excel allows you to reference cells from other cells and automagically perform calculations when any referenced cell changes value.
What I needed was a method for replicating that exact functionality. The only problem was knowing where to put the calculations. Ideally, I would put them behind the Field that contained the value I wanted to calculate. That would simplify the calculations by retaining them in a singular location that would automatically update itself. The only issue with that idea was the fact that most of these Fields were not meant to be edited. They would be read-only. Since the OnChange event doesn't fire unless you manually change and leave a Field, the calculation would never fire.
It was just then that I remembered seeing this really neat looking CRM 3.0 client-side function called FireOnChange().
What Does the FireOnChange() Do?
Well, it fires the Field's OnChange() event. Pretty cool huh?
Starting from Scratch
So let's rewrite the solution we created in our original article using supported and therefore supportable technologies. Here is our user interface:

and here is our JavaScript calculator:
crmForm.all.new_total.DataValue=
crmForm.all.new_price.DataValue*
crmForm.all.new_quantity.DataValue
Again, we can put the above code in the OnChange events of Price and Quantity, but that really doesn't help our goal of maintainability. So I'll tell you what, let's put that code only in the Total field's OnChange event. And before you start calling me eight kinds of idiot, let me finish.
In the OnChange event for both Price and Quantity, I want you to put the following code:
crmForm.all.new_total.FireOnChange();
Now, go Preview the form and enter amounts into Price and Quantity. It should have updated Total with the correct number.
Caveats
I only ran into one issue using FireOnChange(). You can't execute a second FireOnChange() event while an existing FireOnChange() event is executing. For example:
My guess is that the Stack Error is caused by the FireOnChange() being called recursively and it wasn't built for that.
To make matters worse, the actual error specifies Line 0 as the location. I had 40+ separate calculations so it was of no help at all. It was only after a number of hours of manually tracing and debugging that I finally figured out what was happening.
Other Considerations
You may be wondering why I told you to put the FireOnChange() into both Price and Quantity fields and not just the Quantity field. That is because you can never count on a user doing the exact thing in the right order.
So, if you are thinking that users will always enter Price, then Quantity, you're setting yourself up for failure, since they could change their mind and change the Price for some reason. Without the total.FireOnChange() in Price's OnChange event, we would never pick up that change. That would result in a bad user experience and a support call.
In Summary
I can't stress enough how much proper planning will help you here. My biggest mistake was not knowing and understanding that I couldn't recursively call FireOnChange() and when I found out, I had already had all of my calculations in place which makes for a bunch of spaghetti-code.
So, figure out where you need your calculations, which ones can be stand-alone and which need to be executed remotely ( via FireOnChange() ). After that, it is simply a matter of defining the execution rules for the calculation and making sure that no one calculation calls FileOnChange() recursively.
Next Steps
I have it on my list to create an unsupported application that will read the Form from the database and extract the JavaScript code and stick it into a Word document for documentation purposes. I'll let everyone know when I'm done with that. I'll probably charge a gazillion dollars (US) for it too.
I can't get to Vegas on my looks, after all. :)
4 Responses
Robert MacLean
07|Mar|2006 1I had a total, grand total and other fields where the value was worked out based on other fields (very similar to your design). I made the total and grand total read only using the disable option on the form designer. The values would be propergated using javascript and this worked fine.
Interesting the values of the field would be stored correctly in the database and so, on the creation of the item but if you tried changing them later on the values would not be updated in the database even though they were changing successfully with the javascript.
Appeared to me that disabled fields with values in them and null values in the database get written to the database while disabled fields with values in them with non-null values in the database do not get written.
mitch
07|Mar|2006 2Robert,
You are correct. Disabled fields do not get written back to the database.
Ryan Farley has a great article on Read Only and Disabled fields:
http://crmdeveloper.com/blog/archive/2006/03/06/17312.aspx
Check that out.
In addition, look at the CRM 3.0 SDK for the following command: .ForceSubmit
It is supposed to set a flag that will cause it be write back to the database regardless of state.
Mitch
Robert MacLean
08|Mar|2006 3Thanks for the information on ForceSubmit. I noticed in the SDK while looking at it that for disabled it states
The second part about being submitted is definately not correct :(
edwardsdna
28|Feb|2007 4Hi,
I just used your .FireOnChange(); suggestion and it worked great! Thank you for the solution.
Donna
Leave a reply
Search
Categories
Archives
Meta
Most Viewed
Tags