Thursday, May 18, 2017

Showing and hiding sections on a form + quirks

Showing and hiding sections on a form is not possible to do in a fully supported way without hiding all components of the section.
This can be quite arduous, and in addition someone might have created a business rule that messes that up for you and all of a sudden, it's visible anyway.

To help with that there's a great little function hidden in the client side library that you could use
Xrm.Page.ui.tabs. get(delegate: MatchingDelegate<T>): T[];
This little bit lets you query for a tab using a filter in form of a function. This will let you specify criteria like section content and other properties/attributes.
This means you loop through all tabs on the form and filter by the criteria specified. I’m doing the following (please notice that I’m writing in typescript):
var parentTab = parent.Xrm.Page.ui.tabs.get(t => {
    return t.sections.get("MyUniqueSectionName") !== null
});
var mySection = parentTab[0].sections.get("MyUniqueSectionName");
mySection.setVisible(false);
For the first part, I’m creating a delegate function of t, where t is the current tab being iterated over.
Inside the delegate there’s a short one-liner which returns true if there is a section in the tab with the unique name. This will generate an array of tabs as a result set. I made sure that I used a unique name for my section so I will only get one tab in return.
Next I collect the section from the result set, and finally set the visibility to false.
Putting this into a reusable function with two parameters, I can now utilize it as follows:
setSectionVisibility("MyFirstUniqueSectionName", false);
setSectionVisibility("MySecondUniqueSectionName", true);
Now I can reuse the function and trigger it on field changes or form load to make sure I only show the sections applicable for a given scenario.

Quirks!

I found one quirk with this which was kind of annoying, but luckily the fix isn’t particularly hard. If you have a business rule which shows and hides fields in a section, and that section is hidden at the same time (for example if you trigger on change of a field, and both the business rule and this javascript snippet triggers on the same change), then the following quirk happens.
The section stays visible, but if you write out console.log(mySection.getVisible()) it will say “false”. I assumed there was some race condition going on, because if I hid a field with a business rule at the same time as I hid the section with this javascript, then the section stayed visible but the field got hidden. I did some testing and found out that if you put it inside a setTimeout() then it works like a charm. What I ended up doing was this:
setTimeout(f => {
    setSectionVisibility("MyFirstUniqueSectionName", false);
    setSectionVisibility("MySecondUniqueSectionName", true);
}, 10);

Even though the timeout is only 10 milliseconds that’s still enough to prevent the race condition, and everything works brilliantly.


Another thing I found is that the tab has another, undocumented function: Xrm.Page.ui.tabs. getByFilter(delegate: MatchingDelegate<T>): T[];

This does the same thing as get(…), but has a longer, more explicit function name. Seeing that this is undocumented I would stay away from it as long as both do the same thing (using Daryl Labar’s DefinitelyTyped definitions for Xrm in Typescript will only show the documented function).


Edit:
I have been notified that the xrm definitions of DefinitelyTyped is an effort made by the following:
David Berry
Matt Ngan
Markus Mauch
Daryl LaBar
Tully H

Refreshing web resources and iframes, the supported way

I wrote this great (IMO) little snippet to refresh web resources and iframes (which is basically the same thing) on an MSDYN365 form.
A great thing about the client side library is that it allows you to control form components even when you're operating from inside another frame (be aware that this is also a risk, and something you should be aware of).

I made a web resource which lists out data from several different child-entity records, lets call it RecordLister, and then I have another web resource which allows for creation of different child-entity records, lets call it RecordCreator.

When I create new child records from the RecordCreator I want to refresh the RecordListener resource, but as you might know the "Xrm.Page.data.refresh" function does not reload the web resources, it only reloads the data in the native fields. However, since the web resources are basically iframes it should be possible to reload the content, and luckily for us there is.

First off, I'm doing this in typescript, and I'm using the excellent DefinitelyTyped definitions made by Daryl Labar. You should too!

So here's the code I used to get this to work, and I'll explain it step by step.


var recordLister = parent.Xrm.Page.ui.controls.get("WebResource_recordlister") as Xrm.Page.FramedControl;

What this bit does is just getting the web resource using the Xrm client library, and since I’m using DefinitelyTyped I’m casting it to the specific type I know it is (in this case a FramedControl).

var oldSource = recordLister.getSrc();

Next I’m getting the source url from the control and store it in a local variable, followed by setting the source to about:blank. This way I’m keeping the relative URL that was used before, and I’m changing the source value to make sure the client recognizes the change (setting the value to the same thing doesn’t trigger a reload). I use about:blank just to make sure that should anything crash, hang or otherwise work improperly then at least the frame is blank.

recordLister.SetSrc(oldSource);

Finally I’m setting the source back to its original value, triggering a reload of the contents.

So that’s an easy way to reload your web resource or iframe using supported javascript (typescript) client side functions.

Edit:
I have been notified that the xrm definitions of DefinitelyTyped is an effort made by the following:
David Berry
Matt Ngan
Markus Mauch
Daryl LaBar
Tully H

Tuesday, May 9, 2017

Dynamics 365 REST API implementation changed for _workerid_value

I just noticed one little finicky change to the MSDYN365 REST API.
We have two custom buttons on the case form in one of our customer solutions

  1. Pick case: Lets you pick the case if it is in a queue and you are not the owner
  2. Release case: Lets you release the case back into the queue if you are the owner


So I do the following query against the MSDYN365 REST api through JS (I know, I know, there's other ways to do this, but this has the lowest latency cost):
>xhr.open("GET", apiUrl + "incidents(" + caseId + ")?$select=title&$expand=Incident_QueueItem($select=queueitemid,_workerid_value)", true);

Back in the old days of version 8.0CRM2016 if there was no worker assigned to the queue item, then this would be the case:
>JSON.parse(xhr.response).Incident_QueueItem[0]._wokerid_value
undefined

But in the modern days of version 8.2MSDYN365 if there is no worker assigned to the queue item, then it would return this:
>JSON.parse(xhr.response).Incident_QueueItem[0]._wokerid_value
null

Having hacked this together in a hurry, someone (fingers crossed it wasn't me) decided to do the following check:
>if (queueItem._workerid_value !== null) { ... }

Which worked perfectly, until recently when we upgraded to MSDYN365. Now it returned true all the time, even when there was no worker assigned. The only thing that tipped us off was that it was still working in an old environment still running 8.0, so that's how we found out about it.
The workaround is, as you might already see, very simple. Just do an empty check instead of value specific check, which works for both null and undefined:
>if (queueItem._workerid_value) { ... }


Lesson learned: Implementation will change, so make (doubly) sure to plan accordingly.
EDIT: This apparently only happens for _workerid_value, so there seemed to be a mistake in the 8.0 implementation in CRM2016.
Also, to be more specific, I'm using api/data/8.0 in both examples.

Sunday, May 7, 2017

Deleting Azure Resources when portal is stuck and unable to acquire token for subscription

So that's a long headline!
I recently had some issues trying to delete a resource in one of my resource groups in Azure, and when I decided to try and use powershell to do it I ran into more problems. So here is a short recap of what I did, in case somebody else runs into the same issues.

Background:
I have both an MPN account for my own company and an MSDN account from a company I work for, both are connected to the same Microsoft ID but are different subscriptions on different tenants.

So here's my issue. I wanted to delete some old resoures in the Azure portal so save some of my dev credits, but whenever I opened the resources in the portal they were just stuck loading forever.
After two days of this I got fed up and decided to fix it with powershell (which I probably should have been doing in the first place.
So what I did was fire up powershell, and run the following lines:

Import-Module Azure
Login-AzureRmAccount
Get-Subscription

And that produced the following error:
WARNING: Unable to acquire token for tenant 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'

So first I was met with one issue, then a new one when I tried the other, recommended path of fixing things.
Luckily for me this annoyed me, so I did some very quick google searching to find a workaround to get my workaround fixed, so I simply did this to log in instead:

Login-AzureRmAccount -TenantId 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
Get-Subscription


And voilá! The subscription was available and everything was working fine.
And just to clearify how to delete the resources, just run the following commands

Get-AzureRmResource
Remove-AzureRmResource

Nothing big, but here's the entire solution in one blog post. Also, I'm a much happier man now with plenty of credits left on his Azure subscription.