Working with the grid onLoad event

By | February 19, 2020

Sometimes, I get a feeling that, as far as Dynamics/Model-Driven javascript event handlers are concerned, everything has already been said and done. However, I was recently asked a question which, as it later turned out, did not really have the kind of a simple answer I thought it would have (meaning, “just google it” did not work).

How do you refresh a form once a grid on the form has been updated?

For example, imagine there is a subgrid on the form, and, every time a new record is added to the subgrid, there is a real-time process that updates the “counter” field. By default, unless there are further customizations, I will have to hit “refresh” button to see updated value of my counter field. Otherwise, I will keep seeing 0:

image

Which is not correct, since, if I clicked “Refresh” there, I would see “2”:

image

Apparently, some customization is in order, and, it seems, what we need is an event that will trigger on update of the subgrid. If there were such an event, I could just refresh the form to update the data.

This seems to be a no-brainer. For the form refresh, there is formContext.data.refresh method:

https://docs.microsoft.com/en-us/powerapps/developer/model-driven-apps/clientapi/reference/formcontext-data/refresh

For the sugrid, there is addOnLoad method for adding event handlers:

https://docs.microsoft.com/en-us/powerapps/developer/model-driven-apps/clientapi/reference/grids/gridcontrol/addonload

So, it seems, I just need to use addOnLoad to add a listener function, and, from that function, I need to refresh the form.

Except that, as it turned out, there are a few caveats:

  • When you open a record in your app, form onLoad will fire first. This is a good place to call addOnLoad for the grid
  • Gird’s onLoad event will follow shortly. But only if there is some data in the grid. Otherwise, it won’t happen for an empty grid
  • Every time a linked record is added to the grid or removed from it, grid’s onLoad event will fire. Even one the last record has been removed from the grid and the grid is empty after that
  • Once formContext.data.refresh is called, form data, including all grids on the form, will be refreshed. Form onLoad won’t fire, but onLoad event for the grid will fire at that time (Although, see note above about empty grids). This may lead to an infinite recursion if another formContext.data.refresh is called at that time

 

Strangely, I could not find a simple solution for that recursion problem above. At some point, I figured I could just add a variable and use it as a switch. So, once in the grid’s “onload” event, I would check if it’s set to true, and, if yes, would reset it and do nothing. Otherwise, I would set it to true, and, then, would call formContext.data.refresh

This was supposed to take care of the recursion, since I would be calling “refresh” every second time, and, therefore, the recursion wouldn’t be happening. And this was all working great until I realized that, when the form opens up initially, there is no way of telling if grid’s onload will happen or not (since that depends on whether there are any linked records – see the list above). Which means I can’t be sure which is the “first” time and which is the “second” when it comes to the grid events.

Eventually, I got a solution, but this now involves an API call to check modifiedon date. Along the way, it turned out that “modifiedon” date that we can get from the attributes on the form does not include seconds. You can try it yourself – I was quite surprised.

On the other hand, if we use Xrm.WebApi.retrieveRecord, we can get modifiedon date with the seconds included there.

What I got in the end is javascript code below.

  • gridName should be updated with the name of your grid control
  • onFormLoad should be added as an onLoad event handler for the form
  • onFormSave should be added as an onSave event handler for the form

 

Basically, this script will call refresh whenever modifiedon date changes after a grid control has been reloaded. Keeping in mind that I’d need to compare seconds as well, I am using Xrm.WebApi.retrieveRecord to initialize lastModifiedOn variable in the form onLoad.

And, then, I’m just using the same API call to verify if modifiedon has changed (and, then, to call “refresh”) in the grid onLoad event.

Finally, I need onFormSave to reset lastModifiedOn whenever some other data on the form is saved. Otherwise, once the form comes back after “save”, all grids will be reloaded, and, since modifiedon will be updated by then, an additional refresh will follow right away. Which is not ideal, of course.

 

var formContext = null;
var lastModifiedOn = null;
var gridName = "Details";

function onFormLoad(executionContext)
{
  formContext = executionContext.getFormContext();
  //Can't use
  //lastModifiedOn = formContext.getAttribute("modifiedon").getValue();
  //Since that value does not include "Seconds"
  //Also, this needs to be done for "updates" only
  if(formContext.ui.getFormType() == 2){
    Xrm.WebApi.retrieveRecord(formContext.data.entity.getEntityName(), formContext.data.entity.getId(), "?$select=modifiedon").then(onRetrieveModifiedOn);
  }
}

function onFormSave()
{
	//Not to refresh on save
	lastModifiedOn = null;
}

function onSubgridLoad(executionContext)
{
   Xrm.WebApi.retrieveRecord(formContext.data.entity.getEntityName(), formContext.data.entity.getId(), "?$select=modifiedon").then(onRetrieveModifiedOn);
}

function onRetrieveModifiedOn(result)
{
	if(lastModifiedOn != result.modifiedon)
	{
		debugger;
		var doRefresh = false;
		if(lastModifiedOn == null){
			formContext.getControl(gridName).addOnLoad(onSubgridLoad);
		}
		else{
			doRefresh = true;
		}
		lastModifiedOn = result.modifiedon;
		if(doRefresh) formContext.data.refresh();
	}
}

 

Have fun with the Power!

Leave a Reply

Your email address will not be published. Required fields are marked *