Use of async-await in virtual PCF control

By | December 9, 2022

Recently while working on the virtual PCF control we noticed that the updateView() in the virtual PCF works in a different manner as compared to standard PCF control with async functions. In standard PCF control, the return type of the updateView() is void while in the virtual PCF the return type is react-element. So when we return the promise to the updateView() by calling the callback function in a virtual PCF control, we do not get the desired result.

Let’s take an example where we will create a virtual drop-down PCF control. We will be showing the entity attributes as options in our drop-down control. In the example below, we have created an async function   GetEntityAttribute which will be responsible for getting the entity attributes to list out in our PCF drop-down control.

If we call our async function GetEntityAttribute in the updateView() as follows, we will not get the options with dropdown as shown in the below image.

UpdateView() Then:

public updateView(context: ComponentFramework.Context<IInputs>): React.ReactElement {

//Local variables

let isDisable: boolean = false;

let isMultiple: boolean = false;

let dropDownProps: any;

//Calling function to get the attributes

this.GetEntityAttribute(context);

 

//Calling function to get the selected values

this.selectedItem = this.getSelecetdItems(context)

dropDownProps = {

entityMetadata: this.entityAttributes,

defaultSelectedOptions: this.selectedItem,

isMultiple: isMultiple,

isDisable: isDisable,

notifyChange: this.notifyChange.bind(this),

context: context

};

return React.createElement(

DynamicDropdown, dropDownProps

);

}

Field Interface then:

virtual PCF control

It happens because when an async function is called and executed the program control directly jumps to the next line of the updateView() and executes it first and renders a dropdown control with no options. It then goes again to the async function to execute the code after await where we had our array of all attributes. Since the control was rendered already, we will not get a drop-down with options.

To solve this issue we will use the useEffect() function of the react and call async functions inside it rather than calling the async function in the updateView(). Thus, the updateView() will now look as follows.

UpdateView() Now:

public updateView(context: ComponentFramework.Context<IInputs>): React.ReactElement {

//Local variables

let isDisable: boolean = false;

let isMultiple: boolean = false;

let dropDownProps: IDynamicDropdownProps = {};

let entityName: string | null = "";

entityName = context.parameters.entityName.raw ?            context.parameters.entityName.raw : context.parameters.fieldName.raw;

//Reading the parameters

dropDownProps = {

isDisable: isDisable,

isMultiple: isMultiple,

entityName: entityName,

context: context,

notifyChange: this.notifyOnSelect.bind(this)

}

return React.createElement(

DynamicDropdown, dropDownProps

);

}

We will create a Helper.ts file containing the functions and then create the object of the class containing all the functions in App.tsx file which consists of a functional component. We have also passed a dependency to ensure that the useEffect() gets called every time there’s a change in the entity name.

Helper.ts

public GetEntityAttribute = async (entityName: any) => {

//Local variables

let functionName: string = "GetEntityAttribute()=>";

let res: any;

let attributesLogicalname: any = [];

let result: any;

let entityAttributes: any = [];

try {

if (!this.isValid(entityName)) {

return { options: entityAttributes }

}

//Making request for entity metadata to get logical names of the attributes

res = await this._context.utils.getEntityMetadata(entityName);

//Getting all logical name of the attributes and storing it in the array

attributesLogicalname = res._entityDescriptor.AttributeNames;

//Making request to get logical name as well as display name of the attributes

result = await this._context.utils.getEntityMetadata(entityName,                      attributesLogicalname);

//Converting the object keys to an array for iteration

let response = Object.keys(result.Attributes._collection);

//Pushing the attributes into the array

for (let j = 0; j < response.length; j++) {

if (result.Attributes._collection[response[j]].DisplayName != null) {

entityAttributes.push({

key: result.Attributes._collection[response[j]].LogicalName,

text: result.Attributes._collection[response[j]].DisplayName

})

}

}

entityAttributes.sort(function (a: any, b: any) {

if (a.text < b.text) { return -1 }

if (a.text > b.text) { return 1 }

return 0;

});

}

catch (error: any) {

//Handling error

this._context.navigation.openErrorDialog({ message: functionName + ": Kindly check the entity name and it must be in lowercase" })

}

return { options: entityAttributes }

}
 

App.tsx:
 

useEffect(() => {

_CrmHelper.GetEntityAttribute(props.entityName).then((success) => {

//Setting the dropdown options

setDropDownOptions(success.options)

});

}, [props.entityName])

Field Interface Now:

virtual PCF control

Conclusion

Thus, we saw how to use Async-await on a load of virtual PCF control.