Introduction
While creating PCF control, we have mostly encountered difficulties to create popups and dialog boxes. We use external UI libraries like Fluent UI that enables us to manage popups in our PCF control.
Have you heard of PopupService in PowerApps Component Framework?
Let us find out what the PopupService is.
PopupService is a native PCF option that we can use to manage popups in the PCF control.
Sometimes you may design PCF control wherein your control does not require including Fluent UI or any other external library for popups. In this situation, you can go with native PopupService option to create popups.
PopupService includes seven methods to interact with the popups including createPopup, closePopup, deletePopup, openPopup, updatePopup, getPopupsId and setPopupsId. To know more about these methods, follow the link here.
Prerequisites
• Microsoft PowerApps CLI must be installed.
• Should be familiar in creating basic field control.
Below are the steps to create a PCF control, which includes a button. By clicking this button our custom popup will open.
1. In index.ts file, create three global variables in class which will include a container to append the button in it, PopUpService which will give us the methods discussed in the introduction of this blog and isUpdateViewCalled to restrict extra updateView() call with default value as false.
2. Initialize the container in init() method of PCF.
3. In updateView() method we are going to create a button on click of which a popup will appear. At first, we will check the condition of this._isUpdateViewCalled to call the updateView method only once and not multiple times. Further, we will create four variables for our html container, button element, popup content and a close button.
4. After this variable declaration in updateView() method, create and append button element to the htmlContainer. Let the name of button be ‘Quick View’, by clicking which it will call popUpClick() that will open the popup. As shown below we also added a class name ‘button’ to give some style to our button.
Css:
To add CSS in your PCF control, create a .css file and add your css in it. To use that file, add it in the resources part in ControlManifest.Input.xml file as shown below:
5. Now it is time to create the content for the popup. After creating and appending ‘Quick View’ button to htmlContainer, add logic to create the content, which can be either a HTML or a plain text. For the demo purpose, we will show primary contact record detail of the account on which our control is present. We will retrieve the record using webApi.retrieveMultipleRecords function and in success callback function, we will create the popup content and set true to isUpdateViewCalled for not calling it again.
In content, we will create a HTML table in which we will show three field values – Name, Email address and Phone number of primary contact. You can choose any field you want along with the table; we will add a button to close the popup on click of which we will call the close Popup method.
So, first we will create fetchXML query and retrieve the record.
Further will create the popup content in the success callback function.
We also added “container” css class to the popUpContent and “closeButton” class to the button on the popup. In addition, we added some styling to the table and its content.
Css:
6. Now it is time to create our popup Object. Before creating it we need to make an interface the above class. While creating the interface, extend it to ComponentFramework.FactoryApi.Popup.Popup. Now we can use this interface as a definition type for our popup object.
Will create the Popup Object below our html content, in which we will add popup properties like,
closeOnOutsideClick: Indicates whether popup will close with an outside mouse click. When set to false, the popup will not close with an outside mouse click.
content: Static DOM element to be inserted of type HTMLElement.
name: Unique name of Popup.
type: The type of popup, which is described in the enum PopupType. Root: 1 and Nested: 2
The createPopup(popUpOptions) method will create our popup.
7. After configuring our control on a field in the form, the button will look as shown in the below image:
On click of the above button, we will call our popUpClick() in which we are calling openPopup(popUpName) method, that will open the popup created in the above step.
Now you can see the popup as shown in the below image with details of Primary contact in it.
We can see the three fields with their values and a close button. On click of ‘Close’ button, we will call closeCustomPopUp() in which we will call closePopup(popUpName) to close our custom popup.
Below is the complete code for the above Popup.
CSS:
.container {
width: 400px;
height: 200px;
background-color: azure;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border: 1px solid slategray;
padding: 10px;
border-radius: 10px;
}
.button {
border: 1px solid #333;
border-radius: 5px;
background-color: #EFEFEF;
width: 100%;
padding: 10px;
}
.closeButton {
width: 100%;
border: 1px solid black;
margin-top: 10px;
background: azure;
padding: 10px;
}
table {
width: 100%;
background: #EFEFEF;
border: 1px solid #333333;
color: black;
}
td {
padding: 10px;
}
Code:
Index.ts (TypeScript)
import { IInputs, IOutputs } from “./generated/ManifestTypes”;
interface PopUpInterface extends ComponentFramework.FactoryApi.Popup.Popup {
popupProp: object;
}
export class CustomPopUp implements ComponentFramework.StandardControl<IInputs, IOutputs> {
private _container: HTMLDivElement;
private _popUpService: ComponentFramework.FactoryApi.Popup.PopupService;
private _isUpdateViewCalled: boolean = false;
/**
* Empty constructor.
*/
constructor() { }
/**
* Used to initialize the control instance. Controls can kick off remote server calls and other initialization actions here.
* Data-set values are not initialized here, use updateView.
* @param context The entire property bag available to control via Context Object; It contains values as set up by the customizer mapped to property names defined in the manifest, as well as utility functions.
* @param notifyOutputChanged A callback method to alert the framework that the control has new outputs ready to be retrieved asynchronously.
* @param state A piece of data that persists in one session for a single user. Can be set at any point in a controls life cycle by calling ‘setControlState’ in the Mode interface.
* @param container If a control is marked control-type=’standard’, it will receive an empty div element within which it can render its content.
*/
public init(context: ComponentFramework.Context<IInputs>, notifyOutputChanged: () => void, state: ComponentFramework.Dictionary, container: HTMLDivElement) {
this._container = container;
}
public popUpClick = () => {
this._popUpService.openPopup(“CustomPopUp”);
}
public closeCustomPopUp = () => {
this._popUpService.closePopup(“CustomPopUp”);
}
/**
* Called when any value in the property bag has changed. This includes field values, data-sets, global values such as container height and width, offline status, control metadata values such as label, visible, etc.
* @param context The entire property bag available to control via Context Object; It contains values as set up by the customizer mapped to names defined in the manifest, as well as utility functions
*/
public updateView(context: ComponentFramework.Context<IInputs>): void {
if (!this._isUpdateViewCalled) {
let htmlContainer: HTMLDivElement;
let quickViewButton: HTMLButtonElement;
let popUpContent: HTMLDivElement;
let closeButton: HTMLButtonElement;
//============ content of our popup =============
htmlContainer = document.createElement(‘div’);
//button to show our popup
quickViewButton = document.createElement(‘button’);
quickViewButton.innerHTML = “Quick View”;
quickViewButton.classList.add(‘button’);
quickViewButton.onclick = () => this.popUpClick();
htmlContainer.appendChild(quickViewButton);
//@ts-ignore
let page = context.page;
let entityId = page.entityId;
let fetchXml = “<fetch version=’1.0′ output-format=’xml-platform’ mapping=’logical’ distinct=’false’>” +
“<entity name=’account’>” +
“<attribute name=’name’ />” +
“<attribute name=’primarycontactid’ />” +
“<attribute name=’telephone1′ />” +
“<attribute name=’accountid’ />” +
“<order attribute=’name’ descending=’false’ />” +
“<filter type=’and’>” +
“<condition attribute=’accountid’ operator=’eq’ value='{” + entityId + “}’ />” +
“</filter>” +
“<link-entity name=’contact’ from=’contactid’ to=’primarycontactid’ visible=’false’ link-type=’outer’>” +
“<attribute name=’emailaddress1′ />” +
“<attribute name=’telephone1′ />” +
“</link-entity>” +
“</entity>” +
“</fetch>”;
let fetchQuery = “?fetchXml=” + encodeURIComponent(fetchXml);
context.webAPI.retrieveMultipleRecords(page.entityTypeName, fetchQuery).then((record: any) => {
//take the value in record variable
record = record.entities[0];
this._isUpdateViewCalled = true;
popUpContent = document.createElement(‘div’);
popUpContent.innerHTML = “<table>” +
“<tr>” +
“<td>Primary Contact: ” + record[“_primarycontactid_value@OData.Community.Display.V1.FormattedValue”] + ” </td>” +
“</tr>” +
“<tr>” +
“<td>Email: ” + record[“contact1.emailaddress1″] + ” </td>” +
“</tr>” +
“<tr>” +
“<td>Telephone: ” + record[“contact1.telephone1″] + ” </td>” +
“</tr>” +
“</table>”;
popUpContent.classList.add(“container”);
closeButton = document.createElement(‘button’);
closeButton.innerHTML = “Close”;
closeButton.classList.add(‘closeButton’);
closeButton.onclick = () => this.closeCustomPopUp();
popUpContent.appendChild(closeButton);
// Popup object
let popUpOptions: PopUpInterface = {
closeOnOutsideClick: false,
content: popUpContent,
name: ‘CustomPopUp’, // unique popup name
type: 1, // Root popup
popupProp: {}
};
this._popUpService = context.factory.getPopupService();
this._popUpService.createPopup(popUpOptions);
});
this._container.appendChild(htmlContainer);
}
}
/**
* It is called by the framework prior to a control receiving new data.
* @returns an object based on nomenclature defined in manifest, expecting object[s] for property marked as “bound” or “output”
*/
public getOutputs(): IOutputs {
return {};
}
/**
* Called when the control is to be removed from the DOM tree. Controls should use this call for cleanup.
* i.e. cancelling any pending remote calls, removing listeners, etc.
*/
public destroy(): void {
// Add code to cleanup control if necessary
}
}
Conclusion
As illustrated above, you can now add popups in PCF control using PopupService.