Рендеринг компонента React внутри бутстрапа popover
У меня есть набор изображений, которые имеют popovers с использованием компонента bootstrap popover ui в атрибуте/параметре контента, который я хотел бы добавить в компонент ReactJS MusicList, но я не мог понять синтаксис или это возможно или нет.
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} />
);
}
});
Ответы
Ответ 1
Bootstrap не позволяет легко отображать динамический компонент внутри popover. Если popover, который вы хотите представить, является статическим, вы можете просто использовать React renderComponentToString
, который берет компонент и возвращает строку HTML через обратный вызов:
var html = React.renderComponentToString(<MusicList />);
$(this.getDOMNode()).popover({
html: true,
content: html
});
Однако, если ваш компонент обладает какой-либо интерактивностью, эта стратегия не будет работать, потому что у React никогда не будет возможности присоединить обработчики событий (или запустить любой из ваших собственных методов жизненного цикла). В самом деле, Bootstrap не предоставляет правильные крючки, чтобы сделать ваш popover-контент динамичным.
Тем не менее, можно сделать эту работу, исправив Bootstrap. Я создал демо-версию с динамическим содержимым popover:
http://jsfiddle.net/spicyj/q6hj7/
Обратите внимание, что текущее время отображается внутри popover компонентом React, который обновляется каждую секунду.
Как был создан этот popover?
Я заплата Bootstrap поповер setContent
метод взять React компонент в дополнение к строке HTML или текст. Вместо использования методов jQuery html
или text
я использую 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 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]);
}
};
Теперь вы можете написать
$(this.getDOMNode()).popover({
react: true,
content: <MusicList />
});
в вашем методе componentDidMount
и правильно его обработать. Если вы посмотрите в связанном JSFiddle, вы увидите общего назначения <BsPopover />
обертка я сделал, что берет на себя все Bootstrap звонки для вас, в том числе должным образом очистки компонентов поповер когда компонент обертка удален из DOM.
Ответ 2
Появилась новая библиотека под названием React-Bootstrap, которая быстро развивается.
https://react-bootstrap.github.io/components.html#popovers
Если вы включите этот модуль, у вас есть возможность сделать popover своим собственным компонентом, который вы можете сохранить в переменной, а затем использовать в своей функции рендеринга.
Пример:
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);
мой код:
showMoreDetails: function (obj, columns) {
var popOver = (
<Popover>
<table>
{
columns.map(col =>
<tr>
<td style={{textAlign:'right', padding:5}}>
{col.label}:
</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>
);
},
Ответ 3
После того, как вы отправили этот ответ и проверили и немного поработали с этим обходом, я понимаю, что это не оптимально.
Выпадающие меню Bootstrap закрываются, как только пользователь взаимодействует с ним. Это может быть не желаемое поведение (в моем случае, я вставляю datepicker, и пользователь не может изменить месяц без необходимости повторного открытия меню, чтобы выбрать новую дату).
У меня была та же проблема, и я придумал легкое и альтернативное решение.
Я не хотел использовать action-bootstrap (другие части моего приложения не подвергаются реактированию и не используют bootstrap, поэтому я не хочу, чтобы две библиотеки делали то же самое).
Выпадающий компонент, предоставленный Bootstrap из коробки, почти обеспечивает те же функции, что и popover, за исключением того, что он более гибкий.
Это стандартное раскрывающееся окно Bootstrap:
<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>
Вот минимальный размер, который вам нужно сделать, чтобы он работал, не разбивая его, и вы можете включить в него любой элемент 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>
Все, что вам нужно сохранить, это (i) класс "раскрывающийся список" на обертке div, (ii) атрибут "перетаскивание" данных в триггер (вы можете изменить триггер на любой элемент html, который вы хотите) и (iii) в раскрывающемся меню класса "выпадающее меню" (вы также можете изменить его на любой элемент html, который вам нужен, и вам не нужно вставлять элемент "ul", предложенный Bootstrap).
Вы теряете несколько функций по сравнению с popover (вы не можете определить свои собственные переходы, вы не можете отображать их сверху, влево или вправо, но только ниже - это выпадающее меню), но вы получаете полный контроль над содержимым вашего раскрывающегося списка/popover.
Ответ 4
Другой способ решить эту проблему - включить компонент реагирования, который вы хотите использовать в качестве содержимого всплывающего окна, в функцию рендеринга компонента, содержащего всплывающее окно (т.е. "Страницу"), но поместить его в элемент div со стилем "display: none;" так что он не отображается и не занимает места, также присвойте ему идентификатор, например, "popOverContent". Затем при инициализации содержимого набора всплывающих окон: document.getElementById("popOverContent"). Вам не нужно изменять код начальной загрузки таким образом, что упрощает обслуживание/обновление.