Tag Archives: dynamics365

Work with Quick view forms in Dynamics 365/ CDS Client API

Quick view forms are wonderful features in Dynamics 365. And frequently you need to interact with quick view form from client side.

As you can see in the below screenshot, we have a quick view form of contact on the account form. Whenever we select the Primary Contact, the contact details like Email Address and Business phone are shown in the quick view form.

image
Advertisements

The name of the quick view form control on the account is – “contactquickform”. Below is the code to access the email address on the quick view control of contact form

var quickViewControl = formContext.ui.quickForms.get(“contactquickform”);

var emailAddressControl = quickViewControl.getControl(“emailaddress1”);

console.log(“Email address”, emailAddressControl().getAttribute().getValue());

There are host of methods that you can access for the quick view control. Refer to Microsoft Docs link to learn more.

Advertisements

Hope this works out as useful tip.

Debajit Dutta

(Business Solutions MVP)

Opening a quick create form in Dynamics 365–Use Xrm.Navigation.openForm instead of deprecated Xrm.Utility.openQuickCreate

Dynamics 365 and PowerApps are an ever changing world and in this ever changing world, keeping track of the changes while being busy at work is really hard. And I realized that hard while recently as I was using Xrm.Utility.openQuickCreate to launch a quick create form from client side API.

Xrm.Utility.openQuickCreate has been deprecated and you should be using Xrm.Navigation.openForm to actually open up a quick create form.

Sample code courtesy Microsoft Docs.

Advertisements

var entityFormOptions = {};
entityFormOptions["entityName"] = "contact";
entityFormOptions["useQuickCreateForm"] = true;

// Set default values for the Contact form
var formParameters = {};
formParameters["firstname"] = "Sample";
formParameters["lastname"] = "Contact";
formParameters["fullname"] = "Sample Contact";
formParameters["emailaddress1"] = "contact@adventure-works.com";
formParameters["jobtitle"] = "Sr. Marketing Manager";
formParameters["donotemail"] = "1";
formParameters["description"] = "Default values for this record were set programmatically.";

// Open the form.
Xrm.Navigation.openForm(entityFormOptions, formParameters).then(
     function (success) {
         console.log(success);
     },
     function (error) {
         console.log(error);
     });

Advertisements

More details about openForm is mentioned here in Microsoft docs.

Hope this helps!

Debajit Dutta

(Business Solutions MVP)

Stop users for performing Abandon/ Finish operations on a business process flow in Dynamics 365 using Client API.

This is a requirement I have recently received and the requirement was – “Users should not be able to abandon or finish a business process flow”. You may be thinking this has to be done using plugins. Off-course this can be done using plugins. Off course you can do it. But then you have to throw an InvalidPluginExecutionException from your plugins. And the end-user shall get the message in Business process flow dialog which have been perennially disliked by users.

But is there some other way. Off course there is. And surprisingly many are now aware of it.

Now in Business Process flow API’s of Dynamics 365, we have a special event where we can register our custom function – onPreProcessStatusChange. The event is fired when the user tries to change the status of Business process flow record.

Below is the code to register an event handler for this event. In the code all I am doing is preventing the user from changing the status of Business process flow.

formContext.data.process.addOnPreProcessStatusChange(function (e) {
    e.getEventArgs().preventDefault();
    var alertStrings = { confirmButtonLabel: “OK”, text: “This operation is not allowed.”, title: “Status Change!” };
    var alertOptions = { height: 120, width: 260 };
    Xrm.Navigation.openAlertDialog(alertStrings, alertOptions)
});

And below is the experience

Hope this helps! Follow my blog for more interesting articles on Dynamics 365 and PowerApps

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Our product offerings:

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Notes Manager (https://debajmecrm.com/2019/02/28/add-metadata-to-your-notes-and-attachments-in-dynamics-notes-metadata-manager-from-xrmforyou-com/)

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

prevent save event of a record in Dynamics 365 forms with custom message – use the setIsValid method

We all know that in UCI, when we try to save a record and if one or more of the form fields are does not contain valid data, the system stops the record from being saved and appropriate validation notifications are shown on the top of the form.

For example I am on the contact form and I haven’t entered the last name which is mandatory and entered an email address in incorrect format.

When I try to save the record, I get validation notifications on top of the form as you can see from the screenshot.

image

Well this is good. But the problem with this is, the system will show this only if you leave some mandatory field blank or you enter data in incorrect format which is not supported by the field by design. What if you have some custom validation logic and based on that you want to show a validation notification for the attribute in the same format and style?

You can use business rules but that would show notification beside the field and not on the top of the form. Also complex validation logic cannot be achieved by using business rules. Also form notifications wont stop save from happening. So how can we achieve this?

Well this is quite easy. There is infact a method in the API which does the same. The method name is setIsValid()

formContext.getAttribute(“lastname”).setIsValid(false, “Validation failed for lastname field.”);

Now when you try to save the record, you get the field validation notification just where you need

image

And your save event is stopped as well. No fancy code to stop the save event.

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:

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Notes Manager (https://debajmecrm.com/2019/02/28/add-metadata-to-your-notes-and-attachments-in-dynamics-notes-metadata-manager-from-xrmforyou-com/)

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

Control Next and Previous stage movement in Dynamics 365/ CDS Business Process flows using Client API

Honestly I was not aware of this function sometime back and this was purely an accidental discovery. And guess what when I queried around, I found that it actually missed the cognizance of so many consultants out there. And while this function have been introduced, we still keep telling the customer that it is kind of not possible to show something like a user confirmation before stage movement.

You may argue that “OnStageChange” event has been there from the beginning. However the issue with it is, it fires when the stage have already changed. Hence does not make any sense.

In the below example I am going to show you couple of scenarios. And trust me if you are not aware of this and seeing for the first time, it will be a huge sense of relief for you since this is a kind of requirement which pops up every now and then.

  • Getting user confirmation before the stage movement happens
  • Stop back stage movement.

Below is the sample code that I have set up for account entity form


var Acc = {};
Acc.formEvents = {
    stageMovementConfirmed: false,





   form_load: function (e) {
       var fc = e.getFormContext();                    
       fc.data.process.removeOnPreStageChange(Acc.formEvents.handlePreStage);
       fc.data.process.addOnPreStageChange(Acc.formEvents.handlePreStage);
    },





   handlePreStage: function (e) {
       debugger;





     // get the event arguments
       var bpfArgs = e.getEventArgs();





      if (bpfArgs.getDirection() === "Previous") // back stage movement is not allowed;
       {
          bpfArgs.preventDefault();
          var alertStrings = { confirmButtonLabel: "OK", text: "Back stage movement is not allowed", title: "Sample title" };
          var alertOptions = { height: 120, width: 260 };
          Xrm.Navigation.openAlertDialog(alertStrings, alertOptions);
          return;
       }





      // this will fire again when move next is called.
       // so if stage movement has been allowed earlier, please proceed. else ask for user confirmation.

       if (Acc.formEvents.stageMovementConfirmed === true) {
          Acc.formEvents.stageMovementConfirmed = false;
          return;
       }





      if (bpfArgs.getDirection() === "Next") { // only next stage movement is allowed.




         // stop the stage movement
          bpfArgs.preventDefault();





         var confirmStrings = { text: "Are you sure you want to move to the next stage?", title: "Stage movement Confirmation!" };
          var confirmOptions = { height: 150, width: 300 };
          Xrm.Navigation.openConfirmDialog(confirmStrings, confirmOptions).then(
             function (success) {
                if (success.confirmed) {
                   Acc.formEvents.stageMovementConfirmed = true;
                   e.getFormContext().data.process.moveNext();
                }
             });
       }





      // you can also play with the other properties of eventargs
       // get the stage - bpfArgs.getStage();
       // get the steps - bpfArgs.getStage().getSteps();





   }
};

Well, it’s a very simple code. The form_load event is fired on account form load and registers the function handlePreStage on Business process “onPreStageChange” event.

The remaining part is self explanatory and I have basically put all the code with appropriate comments to highlight what all you can do with this function.

All set and done, below is the experience I get when I try this out.

Hope you find this quite interesting!

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Our product offerings:

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Notes Manager (https://debajmecrm.com/2019/02/28/add-metadata-to-your-notes-and-attachments-in-dynamics-notes-metadata-manager-from-xrmforyou-com/)

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

Need to open up a webresource as modal in Dynamics 365 on-premise V9.0. Wondering how can you do since Xrm.Navigate.navigateTo is not available still on-premise? Dialog boxes with a little tweak can help you do that even in On-premise. Check this out!

When I came across this post by Bob Guidinger, I was like WOW! What an article and what a discovery. I could vouch that the most experienced of CRM consultants would envy to write a post like this.

And guess what, no sooner than I wrote this post came the requirement where I needed to use it as well.

So the customer is on-premise V9+ and they want modal dialogs to show their webresources. This functionality is absolutely imperative for them and on top of that, they are already in UCI. So even the unsupported Xrm.Internal.openDialog won’t bail us out here.

And then when nothing seemed to work out, I remembered this great post once I read. I would strongly recommend to read the post I referred above even before continuing with this since I am going to take forward the same concepts here.

To show the HTML webresources in modal dialog, the first thing I did was create a Dialog box called – Popup Dialog with the below XML

<Dialogs>
     <Dialog>
       <LocalizedNames>
         <LocalizedName description=”Popup Dialog” languagecode=”1033″ />
       </LocalizedNames>
       <Descriptions>
         <Description description=”Shows the custom dialog process.” languagecode=”1033″ />
       </Descriptions>
       <FormId>{FBEBB959-E1D6-4952-B26D-4E684F3A5FF9}</FormId>
       <UniqueName>PopupDialog</UniqueName>
       <IsCustomizable>1</IsCustomizable>
       <IntroducedVersion>1.0.0.0</IntroducedVersion>
       <IsTabletEnabled>1</IsTabletEnabled>
       <CanBeDeleted>1</CanBeDeleted>
       <FormXml>
         <forms type=”dialog”>
           <form>
          
  <formparameters>
               <querystringparameter name=”webresource_name” type=”SafeString” />
             </formparameters>

             <tabs>
               <tab name=”global” verticallayout=”true” id=”{267E2B6F-7FF7-4657-8B1B-EFD9EEE309DA}”>
                 <labels>
                   <label description=”” languagecode=”1033″ />
                 </labels>
                 <columns>
                   <column width=”100%”>
                     <sections>
                       <section showlabel=”false” showbar=”false” IsUserDefined=”0″ id=”B3024CD2-8073-4DC3-9AAE-52F4563C7DB2″>
                         <labels>
                           <label description=”General” languagecode=”1033″ />
                         </labels>
                         <rows>
                           <row>
                             <cell id=”{d7913827-57c6-bbb0-651e-063baaaa60c3}” showlabel=”false” colspan=”1″ auto=”false” rowspan=”5″>
                               <labels>
                                 <label description=”ModalDialog” languagecode=”1033″ />
                               </labels>
                        
      <control id=”IFRAME_ModalDialog” classid=”{FD2A7985-3187-444e-908D-6624B21F69C0}” isunbound=”true”>
                                 <parameters>
                                   <Url>
https://blank</Url>
                                   <PassParameters>true</PassParameters>
                                   <Security>false</Security>
                                   <Scrolling>never</Scrolling>
                                   <Border>false</Border>
                                   <ShowOnMobileClient>false</ShowOnMobileClient>
                                   <Height>600</Height>
                                 </parameters>

                               </control>

                     <events>
                                <event name=”onload” application=”false”>
                                  <dependencies />
                                </event>
                             
  <event name=”onreadystatecomplete” application=”false” active=”false”>
                                  <Handlers>
                                    <Handler functionName=”onSuccess” libraryName=”new_/Scripts/PopupDialog.js” handlerUniqueId=”{d9c01110-cd86-a3a9-27dc-9138d8e9d90c}” enabled=”true” parameters=”” passExecutionContext=”true” />
                                  </Handlers>
                                </event>

                              </events>
                             </cell>
                           </row>
                         </rows>
                       </section>
                     </sections>
                   </column>
                 </columns>
               </tab>
             </tabs>

            <events>
               <event name=”onload” application=”false” active=”false”>
                 <Handlers>
                   <Handler functionName=”isNaN” libraryName=”new_/Scripts/JQuery-3.4.1.min.js” handlerUniqueId=”58E1B8E6-EE99-40B4-AA5A-FB64D9D4A3F1″ enabled=”true” parameters=”” passExecutionContext=”false” />
                   <Handler functionName=”onLoad” libraryName=”new_/Scripts/PopupDialog.js” handlerUniqueId=”{124A0213-CE38-4F33-93C9-7D2B2BD83006}” enabled=”true” parameters=”” passExecutionContext=”true” />
                 </Handlers>
               </event>
             </events>

          
  <clientresources>
               <isvresources>
                 <clientincludes>
                   <webresource type=”jscript” path=”$webresource:new_/Scripts/JQuery-3.4.1.min.js” />
                   <webresource type=”jscript” path=”$webresource:new_/Scripts/PopupDialog.js” />
                 </clientincludes>
               </isvresources>
             </clientresources>

           </form>
         </forms>
       </FormXml>
     </Dialog>

It’s a pretty lengthy XML but I have highlighted all the important areas. The unique name of the dialog is PopupDialog (highlighted in green)

In my system I already uploaded couple of webresources – new_/scripts/jquery-3.4.1.min.js and new_/scripts/PopupDialog.js. On load of the Dialog, the onLoad function defined in the PopupDialog.js shall fire (highlighted in yellow)

Also observe carefully that I have inserted a iFrame (highlighted in gray). And on readystatecomplete event of the iFrame, I have registered function (highlighted in purple).

The iframe shall act as a container for the webresources. The webresource name shall be passed as QueryString parameter to the form (highlighted in pale blue).

All set and done, just import the customizations.xml back and you should see your dialog box.

Now the next step is to use this Dialog Box to show your HTML webresource as popup. Below is the code to show the popup.

var windowOptions = { “height”: 250, “width”: 450 };

                var input = { webresource_name: “new_/Pages/testfile.html” };

                Xrm.Navigation.openDialog(“PopupDialog”, windowOptions, input).then(
                  function success() {

                      // handle success call back
                  },
                  function error() { });

Observer I am using the Unique name of the dialog to invoke the Dialog box. Also I am passing the name of the webresource in input parameter. And I use the method Xrm.Navigation.openDialog to open the dialog box. It’s worthwhile to mention that the method is undocumented and hence I say it is unsupported. But again as I mentioned, if this is the need of the hour you can go ahead with it.

Now when the dialog is loading, the onLoad event which I registered earlier fires.

function onload(e) {
    debugger;

var fc = e.getFormContext();
    var webresource = fc.data.attributes.get(“webresource_name”).getValue();
    $(“#IFRAME_Modaldialog”).ready(function () {
        $(“#IFRAME_Modaldialog”).css(‘min-height’, ‘300px’);
    });
    fc.ui.controls.get(“IFRAME_Modaldialog”).setSrc(Xrm.Utility.getGlobalContext().getClientUrl() + “/webresources/” + webresource);

}

And voila. Your dialog box just opens up!

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:

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Notes Manager (https://debajmecrm.com/2019/02/28/add-metadata-to-your-notes-and-attachments-in-dynamics-notes-metadata-manager-from-xrmforyou-com/)

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

Are you still including unnecessary fields on your Dynamics 365 forms so that your scripts can access it? May be it’s time to use Attribute dependency feature of webresources.

Sometimes we are so much busy in our day to day consulting stuffs that we frequently miss those small little gems in Dynamics 365 which are scattered throughout but we tend to ignore them more often than not.

And one of them is the Attribute dependency feature in webresources.

Advertisements

How many of you faced a situation where you needed to just put an attribute on the form so that the code inside your form events and field events can access the field to get its value and take logical decisions depending on the field value? And attribute dependencies just helps you to get around it. But trust me, still in 95% of the cases where a requirement like this arises, we go ahead and put the field on the form and hide it so that our scripts can access it. Now that you understand, let’s see how it is done.

We will use account entity attribute “industrycode” of type optionset as dependent attribute to the web resource which is being referred in the form load of account entity.

Create a web resource and go to Dependencies tab. Click Add to choose your entity and fields

Advertisements
clip_image002[4]

Select Account entity and choose your dependent fields from the available fields

clip_image004[4]

Now save and publish the web resource. I have added the lookup field, parentaccountid as well as dependency

Advertisements

Note: Make sure you save & publish your web resource to establish the configured dependencies. It’s very important to first click on Save and then publish to reflect the changes.

Open the Account entity form make sure the “Industry” field is not there on the form. All our customizations are in place and let’s see what magic we can see now

Open any existing account record. I will use Developer Tools window to test this.

P.S – I am using the deprecated Xrm.Page here since I am demoing it in developer tools window. For real scenarios, use the formContext to access your controls and attributes.

Let us now verify whether the industry control is available on the form. To do that just type this command “Xrm.Page.getControl(“industrycode”)”.

clip_image006[4]

The control is unavailable which makes sense but the great thing is that we can still get the value of the industry field. To get the value of field just type following commands

Advertisements

Value : Xrm.Page.getAttribute(“industrycode”).getValue()

Text: Xrm.Page.getAttribute(“industrycode”).getText()

clip_image008[4]

Not only this, you can even set the value and save the record.

In this way we can set value of fields which are not placed on the form. And also use their value to arrive at business decisions. Before this feature we would need to add the field on the form and hide the control. The dependent attributes are not limited to single entity, we can even select attributes from multiple entities and attach to web resource and the same webresource can be used in multiple entity form and field events.

Advertisements

So unless you need to work with the control, use Attribute dependency and don’t unnecessarily drag and drop the controls on the form and increase the form clutter.

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:

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Notes Manager (https://debajmecrm.com/2019/02/28/add-metadata-to-your-notes-and-attachments-in-dynamics-notes-metadata-manager-from-xrmforyou-com/)

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

Want to show a global notification which appears anywhere on your Dynamics 365/ CDS model driven app? Xrm.App.addGlobalNotification does just that. Check this out!

This is one ask which customers had asked for sometime. We have form notifications and field notifications for quite sometime now and this API has also been there for quite sometime. However this is not much used or I shall say rather unknown and through my blog I want to spread the awareness of this wonderful API’s. Trust me, I have even seen experienced CRM consultants coming up and telling this kind of requirement is not possible. But hell yeah! It’s possible.

Advertisements

So let’s see this in action. You can basically use this code from anywhere in Dynamics 365 and it shall work. Below is the code to show a notification where the user have an option to close the notification as well. Not only that, we will also explore to show additional information about the notification. The API has also got a wonderful documentation in Microsoft docs with all the options.

var popupAction =
{
    actionLabel: “Know more”,
    eventHandler: function () {
       // perform other operations as required on clicking
       // you can open up a webresource as well.
       Xrm.Navigation.openWebResource(“<webresourceurl>”);
    }
};

 

// define notification object
var notification =
{
    type: 2,
    level: 4, // information. You can also display warnings/ errors etc.
    message: “This is a sample Notification”,
    action: popupAction
};

 

Xrm.App.addGlobalNotification(notification).then(
    function success(result) {
       sessionStorage.setItem(“GlobalNotificationId”, result);
       // storing it in the session storage will allow you to programmatically close the notifcation at a later point of time
       // using Xrm.App.clearGlobalNotification method.
    },
    function (error) {
       console.log(error.message);
       // handle error conditions
    }
);

Advertisements
image

You can show this up anywhere and this shall be persisted even if you move across various sections in your model driven app. The message won’t however persist if you duplicate tabs or open in new window or refresh the browser altogether. And that’s why I recommend to store the global notification id in sessionSotrage variable so that if it is still not cleared, we can run the code one more to show the notification.

There is very good documentation out there in Microsoft docs as I suggested earlier and I strong recommend going through the options for holistic understanding.

Advertisements

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:

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Notes Manager (https://debajmecrm.com/2019/02/28/add-metadata-to-your-notes-and-attachments-in-dynamics-notes-metadata-manager-from-xrmforyou-com/)

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)