Wednesday, October 22, 2014

OnChange part 2: addOnChange with context awareness

The Microsoft Dynamics CRM SDK comes with lots of functionality to affect how CRM behaves on the client side.
In a couple of articles I will discuss the opportunities ventilate my frustration over run-time event handling manipulation.

My first article in this series covered problems when trying to remove design-time defined onChange events in run-time.

This article will cover some issues with "context awareness" that you may encounter when using addOnChange to alter the events firing from changes in the form.

A javascript web resource for a form typically has some event handlers and some business logic methods, all contained in a javascript "namespace" according to recommendations in the SDK, see section Namespaced library names.

It may look something like this:

Cinteros.JR.account = {

    // ---- Event handlers ---- //

    formLoad: function () {
        var attribute = Xrm.Page.getAttribute("numberofemployees");
        attribute.removeOnChange(Cinteros.JR.account.numberofemployees_onChange);
        attribute.addOnChange(Cinteros.JR.account.numberofemployees_onChange_with_vat);
    },

    numberofemployees_onChange: function () {
        var avgSalary = Xrm.Page.getAttribute("jr_avgsalary").getValue();
        var empCount = Xrm.Page.getAttribute("numberofemployees").getValue();
        var salaryCost = this.multiply(avgSalary, empCount);
        Xrm.Page.getAttribute("jr_salarycost").setValue(salaryCost);
    },

    numberofemployees_onChange_with_vat: function () {
        var avgSalary = Xrm.Page.getAttribute("jr_avgsalary").getValue();
        var empCount = Xrm.Page.getAttribute("numberofemployees").getValue();
        var vat = Xrm.Page.getAttribute("jr_vat_level").getValue();
        var salaryCost = this.multiply(avgSalary, empCount);
        var netsalaryCost = salaryCost - this.multiply(salaryCost, vat);
        Xrm.Page.getAttribute("jr_salarycost").setValue(netsalaryCost);
    },

    // ---- Business logic methods ---- //

    multiply: function (a, b) {
        return a * b;
    }
}

Notice how the internal method multiply is called by using syntax

  this.multiply(a, b);
This indicates that it is a method in the same (current) "namespace". This works fine and as expected when the event handler (numberofemployees_onChange in this case) has been added in the form customizations.

But when the event handler is added using attibute.addOnChange, it is not the call to the method that is passed to the function, but rather the "contents" of it. This in turn does not have a clue about my namespace, when executed.

An alternative could be to try to wrap my my function in a caller when adding it:

attribute.addOnChange(function () { Cinteros.JR.account.numberofemployees_onChange_with_vat(); });

This works, but makes the code harder to read and easier to get wrong. Note that the same – exactly the same – wrapper method must be used if you want to un-hook the event handler using attribute.removeOnChange. And even when trying that, I failed to get the remove function to work as expected.

Alternative javascript strategy

The alternative strategy described in the SDK discuss making methods unique by simply defining a specific prefix for all functions, and declaring them somthing like this:

// ---- Event handlers ----
function Cinteros_JR_account_formLoad() {
    var nameAttribute = Xrm.Page.getAttribute("name");
    nameAttribute.removeOnChange(Cinteros_JR_account_name_onChange);
    nameAttribute.addOnChange(Cinteros_JR_account_name_onChange_special);
    nameAttribute.addOnChange(function () { Cinteros_JR_account_name_onChange_special(); });
};

function Cinteros_JR_account_name_onChange() {
    var avgSalary = Xrm.Page.getAttribute("jr_avgsalary").getValue();
    var empCount = Xrm.Page.getAttribute("numberofemployees").getValue();
    var salaryCost = Cinteros_JR_account_multiply(avgSalary, empCount);
    Xrm.Page.getAttribute("jr_salarycost").setValue(salaryCost);
};

function Cinteros_JR_account_name_onChange_special() {
    var avgSalary = Xrm.Page.getAttribute("jr_avgsalary").getValue();
    var empCount = Xrm.Page.getAttribute("numberofemployees").getValue();
    var salaryCost = Cinteros_JR_account_multiply(avgSalary, empCount);
    var vat = Xrm.Page.getAttribute("jr_vat_level").getValue();
    var netsalaryCost = Cinteros_JR_account_multiply(salaryCost, vat);
    Xrm.Page.getAttribute("jr_salarycost").setValue(netsalaryCost);
};

    // ---- Business logic methods ----

function Cinteros_JR_account_multiply(a, b) {
    return a * b;
};

This is a strategy I don't like very much. The "JScript object to create a kind of namespace" (as Microsoft puts it) provides better source code management and maintainability, and is simply far more appealing.
But unfortunately, it does not work when you use the SDK functions to manipulate event handlers.

(Again, I wish someone will prove me wrong in this. Can't wait to write that apology to the Dynamics team…!)

---

To Be Continued… OnChange part 3: Where is the clearOnChange method?

No comments:

Post a Comment