1

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;
  }
2

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;
    });
}
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.