
При создании любого приложения, рано или поздно возникает необходимость запросить подтверждение от пользователя (особенно полезно, если вы пишете веб-интерфейс управления ядерным реактором).
И хотя в арсенале любого фронтенд-разработчика есть простая как топор функция confirm(), будем честны: она страшна как смертный грех, по-разному выглядит в разных браузерах и запросто может разрушить весь эффект от вашего крутого дизайна. Выход один — использовать собственное модальное окно. Всё крайне просто, если вы используете какой-нибудь jQuery и императивно командуете DOM-элементом, в котором оно описано, тем более что и готовых решений существует множество. А вот если вы используете React JS с его парадигмой реактивного программирования, решение может быть достаточно хитрым из-за особенностей связи между родительскими и дочерними компонентами.
Для начала создадим компонент React для модального окна:
# app/ui-components/modal.jsx
var React = require('react');
var $ = require('jquery');
var Modal = React.createClass({
getInitialState: function () {
return {
visible: false,
cancel_title: this.props.cancel_title ? this.props.cancel_title : 'Отмена',
action_title: this.props.action_title ? this.props.action_title : 'ОК',
title: '',
text: ''
};
},
// Обработчик закрытия модального окна, вызовет обработчик отказа
close: function () {
this.setState({
visible: false
}, function () {
return this.promise.reject();
});
},
// Обработчик действия модального окна, вызовет обработчик действия
action: function () {
this.setState({
visible: false
}, function () {
return this.promise.resolve();
});
},
// Обработчик открытия модального окна. Возвращает promise
// ( при желании, можно передавать также названия кнопок )
open: function (text, title = '') {
this.setState({
visible: true,
title: title,
text: text
});
// promise необходимо обновлять при каждом новом запуске окна
this.promise = new $.Deferred();
return this.promise;
},
render: function () {
var modalClass = this.state.visible ? "modal fade in" : "modal fade";
var modalStyles = this.state.visible ? {display: "block"} : {};
var backdrop = this.state.visible ? (
<div className="modal-backdrop fade in" onClick={this.close} />
) : null;
var title = this.state.title ? (
<div className="modal-header">
<h4 className="modal-title">{this.state.title}</h4>
</div>
) : null;
return (
<div className={modalClass} style={modalStyles}>
{backdrop}
<div className="modal-dialog">
<div className="modal-content">
{title}
<div className="modal-body">
<p>{this.state.text}</p>
</div>
<div className="modal-footer">
<button
type="button"
className="btn btn-default"
onClick={this.close}
>
{this.state.cancel_title}
</button>
<button
type="button"
className="btn btn-primary"
onClick={this.action}
>
{this.state.action_title}
</button>
</div>
</div>
</div>
</div>
);
}
});
module.exports = Modal;
Встраиваем наш новый компонент на родительский:
# app/screens/sample.js
var React = require('react');
var Layout = require('ui-components/layouts/layout');
var Modal = require('ui-components/modal');
var SampleScreen = React.createClass({
modal: function () {
return this.refs.modal;
},
popup: function () {
this.refs.modal().open("Вы уверены?")
.then(function() {
// Действие
})
.fail(function() {
// Отмена
});
},
render: function() {
return (
<Layout>
<div className="row">
<a onClick={this.popup} className="btn btn-primary">Popup</a>
</div>
<Modal ref="modal"/>
</Layout>
);
}
});
module.exports = SampleScreen;
В принципе, можно использовать вместо refs передачу метода SampleScreen в Modal посредством props, но отладчик React выдает на такие действия warning (хотя всё работает).