Before I proceed further some disclaimers here. If you are thinking from the title that I have been able to resolve the problem completely, unfortunately it’s not. My approach works for certain scenarios and not for all scenarios and I will explain where it works and where it doesn’t. And this is of the time I am writing this blog. Very soon Microsoft may release something much simpler.
Ever since I wrote the article on in-depth walkthrough of Power Apps custom control, I have been flooded with questions and one of them was – “How do I get the formContext inside my custom control code so that I can interact with other attributes on the form?”. And in the series of blogs, I have used Xrm.Page construct to access some attributes on the form and as we all know, it has been deprecated. Honestly when I used Xrm.Page I used it for brevity as I didn’t find much time to explore all the variables that the custom framework has to offer and I thought it would be easy one.
But when I went back and started searching I could not find anything the context provides to get the formContext.
I would be happy to know here that I have missed and there is a simple way to do this, I would take this blog post off and would be relieved as personally I am stuck because of this.
But if you are in the same boat as me, you can keep reading.
So here I was baffled. After-all I took it for granted and then it was not there or may be I could not find it. Searched the heck out of Google and could not find it as well.
So falls back to find it myself. I started digging and after hours of effort came up with a way to do this. May not be ingenious or best way but works for certain scenarios (doesn’t work for all. that’s the sad part.)
The first thing that struck me is that I need to keep the formContext somewhere globally so that I can see it in my control code as well. And the only way seemed logical is to do that in the onload form event.
So one part is sorted out. But how about accessing global variables in different context. After all the custom component code and the form on-load script works in different frames.
Just to illustrate, the form scripts execute in one of the frames below “top” where custom component code executes.
Form Script frame Custom component code frame
So if I can maintain a global variable holding the form context in the “top” frame, it would be available in custom component code.
I took Account Entity as an example and registered a form load event handler. Below is the script code.
if (typeof (Demo) == “undefined”) {
Demo = { __namespace: true };
}
Demo.Account = {
_parent: parent,
formContext: null,
OnLoad: function (e) {
debugger;
this.formContext = e.getFormContext();
this._parent.pageContext = this.formContext;
}
};
Demo.Account.OnLoad is called on Account form load. When the function is called, it sets a variable called “pageContext” in the parent scope with the value of “formContext”. So far so good!
Here is my custom component embedded on the same account form.
I have registered an event handler for on click of submit button. What will I do now is try to access the formContext which is now stored a variable called “pageContext”.
When I click the submit button, the click event fires as expected.
If I go to console now and evaluate “pageContext” variable, it is nothing but “formContext”.
Whoooo! So everything just works fine. You can keep on using the this variable now in your control. Wasn’t it simple? Yes it was. Is it a complete solution. The answer is “NO”.
Before I go into the problem, lets go back to our basics here. If you are from the world of ASP.NET web programming and is aware of page lifecycle, you would be aware that page_init method will call control_init method of all the controls on page and then page_load will fire post page_init. The page_load method would only complete when all the controls on the page has been initialized and load completed.
If we bring this knowledge to our custom component, the init method custom component would fire before my form on-load script fires. So when the user opens up a record, the page load begins and the init method of your control fires. “Please note that by the time the init method of your control fires, the form load of the page has not been complete yet and hence the form load script won’t fire by that time.”
So what this mean? If you are trying to access the “pageContext” variable that we have used earlier you won’t find it in there. Even worse is if you are opening are moving from one record to another and try to access the pageContext inside your init method, you may end up getting the pageContext value
from previous record.
So what are the other alternatives?
- There might be simpler ways coming up in next releases or I have missed a simple way or something is already there which didn’t catch my eye (believe me I would be happy is someone points that out).
- Keep using Xrm.Page until MS releases a simpler like we still do for web-resources embedded in CRM form.
Hope this helps!
Debajit Dutta
(Dynamics MVP)
For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com
Our product offerings:
Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)
CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)
Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)
Multiselect picklist for Dynamics 365 (http://www.xrmforyou.com/multi-select-picklist.html)
Discover more from Debajit's Power Apps & Dynamics 365 Blog
Subscribe to get the latest posts sent to your email.
Reblogged this on Nishant Rana's Weblog.
Hi Debajit,
I believe you can use the context to make webapi calls,
I’ve used the context which you get from from init/updateview functions.
I’ve tried something line this,
context.parameters.textFormatted.attributes.EntityLogicalName
context.webAPI.createRecord(“account”, data).then(
function success(result) {
console.log(“Account created with ID: ” + result.id);
// perform operations on record creation
},
function (error) {
console.log(error.message);
// handle error conditions
}
);
textFormatted is the propertyname in ControlManifest.Input.xml file.
absolutely. you can query webapi using context. However that still is one extra call to server. it would have been nice if there would have been something like formContext where we can get values from form without querying.
Again as I understand, PCF controls are made to be generic. Hence I believe querying is the best option here.
Cheers!
Debajit