SharePoint Framework with Promise in extension
-
08-02-2021 - |
Question
I am trying to render header placeholder, but I am very new to the promise of SPFx.
Below line in my code runs before it returns any value. How can I await until it returns a string value?
this._topPlaceholder.domElement.innerHTML = this._getPlaceholderHtml();
My Code:
private _renderPlaceholder(): void {
if (!this._topPlaceholder) {
this._topPlaceholder = this.context.placeholderProvider.tryCreateContent(
PlaceholderName.Top,
{ onDispose: this._onDispose }
);
}
// The extension should not assume that the expected placeholder is available.
if (!this._topPlaceholder) {
console.error('The expected placeholder (Top) was not found.');
return;
}
// if it is available, and access to domElement, update contents
if (this._topPlaceholder.domElement) {
this._topPlaceholder.domElement.innerHTML = this._getPlaceholderHtml();
}
}
private _getPlaceholderHtml(parentId?: number): string {
let placeholderBody: string = '';
this.service.getMenus(parentId).then((menuItems: IMenuListItem[]) => {
if (menuItems && menuItems.length && menuItems.length > 0) {
placeholderBody += '<ul>'
menuItems.forEach((menutItem: IMenuListItem) => {
placeholderBody += `<li><a href='${menutItem.Link}' target='${menutItem.Target}'>${menutItem.Title}</a>`
if (menutItem.ParentId) {
placeholderBody += this._getPlaceholderHtml(menutItem.ParentId) + '</li>'
}
})
placeholderBody += '</ul>'
}
})
return placeholderBody;
}
Solution
I didn't test this code but it should be mostly right. It's enough to give you the idea of what you need to do.
There is an asynchronous call to getMenus
inside the body of the _getPlaceholderHtml
method so you need to update _getPlaceholderHtml
to return a promise rather than a string. Once the call to getMenus
has completed you can calculate the value of the placeholderBody
variable and then you can use the resolve method to send the placeholderBody
value to the caller of _getPlaceholderHtml
.
private _getPlaceholderHtml(parentId?: number): Promise<string> {
let promise = new Promise<string>((resolve, reject) => {
this.service.getMenus(parentId).then((menuItems: IMenuListItem[]) => {
let placeholderBody: string = '';
if (menuItems && menuItems.length && menuItems.length > 0) {
placeholderBody += '<ul>'
menuItems.forEach((menutItem: IMenuListItem) => {
placeholderBody += `<li><a href='${menutItem.Link}' target='${menutItem.Target}'>${menutItem.Title}</a>`
if (menutItem.ParentId) {
placeholderBody += this._getPlaceholderHtml(menutItem.ParentId) + '</li>'
}
});
placeholderBody += '</ul>'
}
resolve(placeholderBody);
});
});
return promise;
}
You then need to update the if statement that calls _getPlaceholderHtml
to kick off the asynchronous process, wait for it to return the computed placeholderBody
, and then update the user interface.
if (this._topPlaceholder.domElement) {
this._getPlaceholderHtml().then((placeholderBody) => {
this._topPlaceholder.domElement.innerHTML = placeholderBody;
});
}