Showing posts with label JavaScript. Show all posts
Showing posts with label JavaScript. Show all posts

Saturday, 21 August 2010

Can I use a picklist/lookup as my primary attribute?

In my experience it's safe to say that CRM's approach of always needing the primary attribute of a custom entity to be an nvarchar attribute  unfortunately doesn't fit all business scenarios.

Take the example where an organisation has a complex product management scenario where the standard product functionality has to be suppressed and replaced with something more suitable. Within the Account record you may want to see all of the products owned by the organisation. If you have a standard catalogue of products, having to name each one whenever a customer acquires said product is far from ideal.

There are a couple of ways of dealing with this, but I generally like to use JavaScript to plug the 'gap'.

So say we want to have a picklist of 'Product' for our primary attribute on our new product entity, we could do the following:
  1. Create the new entity, schema name of new_product.
  2. In the new entity, give the primary attribute a name of new_productname (Name this 'Product Name').
  3. Create a new pick-list attribute called new_productnamepicklist (Also name this 'Product Name').
  4. Place the picklist attribute front & centre on your entity
  5. Place the primary attribute elsewhere and disable it... I generally use a 'System' section on an 'Admin' tab.
Now we just need some JavaScript to automatically populate the new_productname attribute with the value from new_productnamepicklist attribute. Place the following code in the onChange event of the new_productnamepicklist attribute:

var CRM_FORM_TYPE_CREATE = 1;
var CRM_FORM_TYPE_UPDATE = 2;

if ((crmForm.FormType == CRM_FORM_TYPE_CREATE) || (crmForm.FormType == CRM_FORM_TYPE_UPDATE)){
    //get our value from the picklist - make sure you use SelectedValue, not DataValue as we want the text, not the item code.
    var recName = crmForm.all.new_productnamepicklist.SelectedValue;

    //Set the attribute name
    crmForm.all.new_productname.DataValue = recName;
    //Don't forget to force submit as the attribute is disabled on the form.
    crmForm.all.new_productname.ForceSubmit = true;    
}

Please be aware that while this particular approach is very simple to implement, it has its limitations. As the work is being done on the form, the onChange event will not fire if the record is updated as part of a bulk edit, or if it is created via an import or through the web services etc. If you need a more sophisticated solution to handle those scenarios, you will want to use a plugin - I'll try and write a follow-up with more detail on that approach when I get a chance.

Lastly, if you face this challenge, please be sure to give some thought to your views. If you look at a list of products, there are few things less helpful than seeing row after row of a single product. You will need to add extra attributes to your view columns to make it easy for your user to identify the data they need.

Thursday, 19 August 2010

Changing the label of a field and a section

I've previously written about changing the name of a field dynamically using client script. Well now a requirement has come up to not only change the label of an attribute, but also a section heading... easy right? No chance!

As it turns out, with an attribute we have a nice and easy element Id that we can always use to address that field in confidence that we are looking at the right thing. We don't have this with section labels. So my problem was two-fold:
  1. Find a way to iterate through all the sections until I found the one that I wanted, and then change its name.
  2. Then I had to come up with a way of storing that change, so that next time I needed to rename it I knew what to look for; once I've  renamed the section, the original name I used to find it obviously wont work anymore (Duh).
Blatant plagiarism note: Big thanks to Irfan Saeed who did all the heavy lifting with regards the JavaScript to actually rename the sections.

So now that I have a way to find and rename the section, all I need is a way to keep a handle on what the current name is... enter the global variable:

While generally speaking best practice says that you should always prefix your JavaScript variable declarations with the 'var' keyword, it is legal to omit said keyword. If you don't use the 'var' keyword, then the script interpreter will grant that variable global scope... meaning that we can access it and use it anywhere on our form. Sounds like a perfect way to keep a handle on the current name of our se3ction doesn't it?

So in order to do this, create a variable in the forms onLoad event, omitting the 'var' keyword as discussed:

currentSectionName = "";

Now in the onChange and onLoad events of our attribute and form respectively, paste the following code (obviously updating the field names to your own requirements:

//only do something if there is data in the attribute
if (crmForm.all.new_projectcategory.DataValue != null) {
    //get the attribute value
    var pCat = crmForm.all.new_projectcategory.DataValue;

    //define the default section name
    var defaultSectionName = "Delivery";

    //check if the current section name is different to the default name
    if (currentSectionName == "")
        currentSectionName = defaultSectionName;

    //work through the values
    switch (pCat) {
        case "1":
            crmForm.all.ownerid_c.innerText = "Project Manager";
            if (currentSectionName != "Delivery") { UpdateSectionName("tab0", currentSectionName, "Delivery"); }
            currentSectionName = "Delivery";
            break;
        case "2":
            crmForm.all.ownerid_c.innerText = "Project Administrator";
            if (currentSectionName != "Implementation") { UpdateSectionName("tab0", currentSectionName, "Implementation"); }
            currentSectionName = "Implementation";
            break;
    }
}

function UpdateSectionName(TabNumber, CurrentSectionName, NewSectionName) {
    var anchorNode = document.getElementById(TabNumber);
    var secBarCss = "ms-crm-Form-SectionBar";
    var addrSecElm = null;
    var results = getElementsByClassName(secBarCss, anchorNode);
    for (var i = 0; i < results.length; i++) {
        if (results[i].innerText == CurrentSectionName) {
            addrSecElm = results[i];
            break;
        }
    }
    if (addrSecElm != null) {
        addrSecElm.innerText = NewSectionName;
    }
}

function getElementsByClassName(className, anchorNode) {
    if (!anchorNode) anchorNode = document.body;
    var result = [];
    var regEx = new RegExp("\\b" + className + "\\b");
    var children = anchorNode.getElementsByTagName("*");
    for (var i = 0; i < children.length; i++) {
        if (regEx.test(children[i].className)) result.push(children[i]);
    }
    return result;
}

And there you have it; now when we change our field, we are dynamically updating both the attribute and section labels.

Before:


After:



Note: Please be aware, while this approach works, it is currently unsupported by Microsoft so it may stop working in a future release.

Dynamically changing the label of a field

A question came up on the CRM forums and I thought I'd so a quick article on it.

In order to change a fields label, we need to modify the fieldname_c.innerText property. Take for example the following example where we have 'Project Category' and 'Project Manager' attributes:



Now for the sake of argument, say I want to rename my 'Project Manager' attribute based upon the option selected in the 'Project Category' attribute. I would have to put the following code in to the form's onLoad and attributes onChange events:

//only do something if there is data in the attribute
if (crmForm.all.new_projectcategory.DataValue != null) {
    //get the attribute value
    var pCat = crmForm.all.new_projectcategory.DataValue;

    //work through the values
    switch (pCat) {
        case "1": crmForm.all.ownerid_c.innerText = "Project Manager";
            break;
        case "2": crmForm.all.ownerid_c.innerText = "Project Administrator";
            break;
    }
}

Eureka! Now when I set my Project Category value, I get different names on the Owner attribute label...