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;
  }
Was it helpful?

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;
    });
}
Licensed under: CC-BY-SA with attribution
Not affiliated with sharepoint.stackexchange
scroll top