web-dev-qa-db-fra.com

Rendu d'un composant React dans un popover Bootstrap

J'ai un ensemble d'images qui ont des popovers en utilisant le composant bootstrap popover de l'interface utilisateur dans l'attribut/paramètre de contenu. J'aimerais ajouter le composant ReactJS MusicList, mais je ne pouvais pas comprendre la syntaxe ni si c'était possible ou non.

var MusicList = React.createClass({
    render : function(){
        return(<ul><li>Here</li></ul>);
    }
});

var PopoverImage = React.createClass({
    componentDidMount:function(){

        $("#"+this.getDOMNode().id).attr('data-component','<span>here</span>');
        $("#"+this.getDOMNode().id).popover({
            html:true,
            content: 
            });
    },

    render:function(){
        return(
            <img href="#" id={this.props.friend.uid+'_popover'} data-html={true} className="smallImage" src={this.props.friend.pic_small} rel="popover" data-original-title={this.props.friend.name} />

                );
    }
});
29
Fredrick Thornton

Bootstrap ne facilite pas le rendu d'un composant dynamique dans un popover. Si le popover que vous souhaitez présenter est statique, vous pouvez simplement utiliser la variable renderComponentToString de React qui prend un composant et renvoie une chaîne de code HTML via un rappel:

var html = React.renderComponentToString(<MusicList />);
$(this.getDOMNode()).popover({
    html: true,
    content: html
});

Toutefois, si votre composant présente une interactivité, cette stratégie ne fonctionnera pas car React n'a jamais la possibilité d'attacher des gestionnaires d'événements (ni d'exécuter aucune de vos méthodes de cycle de vie personnalisées). En effet, Bootstrap ne fournit pas les crochets appropriés pour rendre votre contenu popover dynamique.


Cela dit, il est possible de résoudre ce problème en appliquant des correctifs à Bootstrap. J'ai créé une démo en direct avec un contenu popover dynamique:

 popover demo screenshot
http://jsfiddle.net/spicyj/q6hj7/

Notez que l'heure actuelle est rendue dans la fenêtre pop-up par un composant React mis à jour toutes les secondes.


Comment ce popover a-t-il été créé?

J'ai corrigé la méthode setContent de Bootstrap popover pour prendre un composant React en plus d'une chaîne HTML ou de texte. Au lieu d'utiliser les méthodes html ou text de jQuery, j'utilise React.renderComponent:

// Patch Bootstrap popover to take a React component instead of a
// plain HTML string
$.extend($.fn.popover.Constructor.DEFAULTS, {react: false});
var oldSetContent = $.fn.popover.Constructor.prototype.setContent;
$.fn.popover.Constructor.prototype.setContent = function() {
    if (!this.options.react) {
        return oldSetContent.call(this);
    }

    var $tip = this.tip();
    var title = this.getTitle();
    var content = this.getContent();

    $tip.removeClass('fade top bottom left right in');

    // If we've already rendered, there's no need to render again
    if (!$tip.find('.popover-content').html()) {
        // Render title, if any
        var $title = $tip.find('.popover-title');
        if (title) {
            React.renderComponent(title, $title[0]);
        } else {
            $title.hide();
        }

        React.renderComponent(content,  $tip.find('.popover-content')[0]);
    }
};

Il est maintenant possible d'écrire

$(this.getDOMNode()).popover({
    react: true,
    content: <MusicList />
});

dans votre méthode componentDidMount et le rendre correctement. Si vous regardez dans le JSFiddle lié, vous verrez un wrapper <BsPopover /> à usage général que j'ai créé et qui s'occupe de tous les appels Bootstrap pour vous, y compris le nettoyage correct des composants popover lorsque le composant wrapper est supprimé du DOM.

39
Sophie Alpert

Il existe une nouvelle bibliothèque appelée React-Bootstrap en cours de développement rapide.

https://react-bootstrap.github.io/components.html#popovers

Si vous incluez ce module, vous avez la possibilité de faire en sorte que la popover soit son propre composant que vous pouvez enregistrer dans une variable, puis utiliser dans votre fonction de rendu.

Leur exemple:

const positionerInstance = ( <ButtonToolbar> <OverlayTrigger trigger="click" placement="bottom" overlay={<Popover title="Popover bottom"><strong>Holy guacamole!</strong> Check this info.</Popover>}> <Button bsStyle="default">Click</Button> </OverlayTrigger> <OverlayTrigger trigger="hover" placement="bottom" overlay={<Popover title="Popover bottom"><strong>Holy guacamole!</strong> Check this info.</Popover>}> <Button bsStyle="default">Hover</Button> </OverlayTrigger> <OverlayTrigger trigger="focus" placement="bottom" overlay={<Popover title="Popover bottom"><strong>Holy guacamole!</strong> Check this info.</Popover>}> <Button bsStyle="default">Focus</Button> </OverlayTrigger> <OverlayTrigger trigger="click" rootClose placement="bottom" overlay={<Popover title="Popover bottom"><strong>Holy guacamole!</strong> Check this info.</Popover>}> <Button bsStyle="default">Click + rootClose</Button> </OverlayTrigger> </ButtonToolbar> );

React.render (positionerInstance, mountNode);

mon code:

showMoreDetails: fonction (obj, colonnes) {

    var popOver = (
        <Popover>
            <table>
                {
                    columns.map(col =>
                            <tr>
                                <td style={{textAlign:'right', padding:5}}>
                                    {col.label}:&nbsp;
                                </td>
                                <td style={{padding:5}}>
                                    {obj[col.key]}
                                </td>
                            </tr>
                    )
                }
            </table>
        </Popover>
    );

    return(
        <OverlayTrigger trigger='click' rootClose placement='left' overlay={popOver}>
            <div className="popover-more">more...</div>
        </OverlayTrigger>
    );

},
2
ml242

Une autre façon de résoudre ce problème consiste à inclure le composant réactif que vous souhaitez utiliser comme contenu popover dans la fonction de rendu du composant contenant le popover (c'est-à-dire 'la page') mais en le plaçant dans un div de style "display: none;" afin que ne soit pas affiché et ne prend pas de place, donnez-lui également un identifiant, par ex. 'popOverContent'. Ensuite, lorsque vous initialisez le contenu de la liste déroulante: document.getElementById ("popOverContent"). Dans bootstrap 4 au moins, il faut un élément comme contenu.

Vous n'avez pas besoin de modifier le code d'amorçage de cette façon, ce qui facilite la maintenance/les mises à jour.

0
Skill M2

Après avoir posté cette réponse et après avoir testé et travaillé un peu avec cette solution de contournement, je me rends compte que ce n’est pas optimal.

Les menus déroulants Bootstrap se ferment dès que l'utilisateur a interagi avec eux. Ce n'est peut-être pas le comportement souhaité (dans mon cas, j'insère un sélecteur de date et l'utilisateur ne peut pas changer de mois sans avoir à rouvrir le menu pour choisir une nouvelle date).


J'avais le même problème et j'ai proposé une solution simple et alternative.

Je ne voulais pas utiliser react-bootstrap (les autres parties de mon application ne sont pas réactifiées et utilisent bootstrap, je ne souhaite donc pas que deux bibliothèques fassent la même chose).

Le composant déroulant fourni immédiatement par Bootstrap fournit presque les mêmes fonctionnalités que le popover, à l'exception du fait qu'il est beaucoup plus flexible . Il s'agit du menu déroulant Bootstrap standard:

<div class="dropdown">
  <button id="dLabel" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
    Dropdown trigger
    <span class="caret"></span>
  </button>
  <ul class="dropdown-menu" aria-labelledby="dLabel">
    ...
  </ul>
</div>

Voici le minimum dont vous avez besoin pour le faire fonctionner sans le casser et vous pouvez y inclure n'importe quel élément React:

<div className="dropdown">
  <button data-toggle="dropdown">
    Dropdown trigger
  </button>
  <div className="dropdown-menu">
    <myGreatReactIntervactiveComponent 
      value={this.props.value}
      onChange={this.handleChange}
      onClick={this.handleClick}
      children={this.greatChildrenForMyGreatREactInteractiveComponent}
    />
  </div>
</div>

Tout ce que vous devez conserver est (i) la classe "dropdown" sur le div wrapper, (ii) l'attribut data-toggle "dropdown" sur le déclencheur (vous pouvez changer votre déclencheur en l'élément HTML de votre choix) et (iii) la classe "menu déroulant" dans le menu déroulant (vous pouvez également le changer en n'importe quel élément html que vous voulez et vous n'avez pas à coller l'élément "ul" proposé par Bootstrap).

Vous perdez quelques fonctionnalités par rapport au popover (vous ne pouvez pas définir vos propres transitions, vous ne pouvez pas les afficher en haut, à gauche ou à droite, mais uniquement en dessous - il s'agit d'une liste déroulante), mais vous obtenez un contrôle total sur le contenu de votre liste déroulante./popover.

0
Cedric