GH DevOps
GH DevOps

Reputation: 426

Bootstrap 5 Popover-Set Content Dynamically from Async Function

I'm trying to load a bootstrap 5 popover with dynamic content from an asynchronous function in which I have to wait for. I'm setting up my popover as so:

document.querySelectorAll('[data-bs-toggle="popover"]').forEach(function (popover) {

    new bootstrap.Popover(popover, {
        content: function () {
            (async () => {

                var x = await SetPopoverContent();
                return x;

            })()
        }
    });   
});

I'm then going back to the database and retrieving my data inside SetPopoverContent():

async function SetPopoverContent(popOver) {
    
    let contentText = '';
    let data = await dotNetReference.invokeMethodAsync('GetChatToInfo', messageId);
    if (data != null && data != undefined) {
        var outerDiv = '<div></div>';
        ...
        
        contentText = outerDiv.innerHTML;
    }
    else {

        contentText = '<p>Loading</p>';
    }
    
    return contentText;
}

I can see my html string inside my popover content function but the content never appears in the popover. Am I doing something wrong with the async callback method?

Upvotes: 1

Views: 111

Answers (2)

traynor
traynor

Reputation: 8717

It won't work with async calls, you need to use .setContent method instead on popover instance (alternatively, you could create popover after async call, or set content to loading... and then use .setContent).

So, get popover instance, then run your async method, and then assign new content:

// assign popover instance for later use
const pop = new bootstrap.Popover(popover);

// or set 'loading' right away
//const pop = new bootstrap.Popover(popover, {
//  content: '<p>Loading</p>'
//});


(async() => {

    var x = await SetPopoverContent();

    // set new content
    pop.setContent({
        '.popover-body': x
    })

})()

demo:

<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
  <title>Bootstrap Example</title>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
</head>

<body class="p-3 m-0 border-0 bd-example m-0 border-0">



  <button type="button" class="btn btn-lg btn-danger" data-bs-toggle="popover">Click to toggle popover</button>



  <script>
  
    function dbCall() {
      return new Promise(r => {
        setTimeout(() => r('new data'), 1000);
      })
    }

    async function SetPopoverContent() {

      let data = await dbCall();

      return data;
    }


    document.querySelectorAll('[data-bs-toggle="popover"]').forEach(function(popover) {

      const pop = new bootstrap.Popover(popover, {
        content: 'Loading'
      });

      (async() => {

        var x = await SetPopoverContent();

        pop.setContent({
          '.popover-body': x
        })
      })();

    });
  </script>

</body>

</html>

see example for setting content: Popovers - Methods - setContent example

The setContent method accepts an object argument, where each property-key is a valid string selector within the popover template, and each related property-value can be string | element | function | null

Upvotes: 1

Naeem Akhtar
Naeem Akhtar

Reputation: 1270

What I think this should work.

if (data != null && data != undefined) {
    var outerDiv = '<div></div>';
    ...
    
    contentText.innerHTML = outerDiv;
}
else {

    contentText = '<p>Loading</p>';
}

Upvotes: 0

Related Questions