Я написал функцию (на основе некоторых других, которые я нашел в Интернете), которая помогает отображать динамически генерируемые модалы без необходимости создавать их в HTML страницы.
Чтобы упростить анализ кода, кратко:
- Я добавляю новый обработчик привязки в Knockout, чтобы разрешить отключение привязки для определенного элемента
- Модал генерируется из шаблона с использованием усов
- Усы заполняют соответствующие части модальных(заголовок, тело, кнопки)
- Модал заключен в div, что останавливает привязку данных (
modalWrapper
) - ..., чтобы я мог применить пользовательскую модель представления к модалу с помощью
ko.applyBindings
Кнопки генерируются автоматически на основе описания, например:
{
label: "OK",
cssClass: "default",
handler: "handleClick", // sets data-bind="click: handleClick"
autoClose: true // adds data-dismiss="modal"
}
Соответствующие части кода следующие:
ko.bindingHandlers.stopBinding = {
init: function () {
return { controlsDescendantBindings: true };
}
};
var modalTemplate = '<div class="modal fade">\
<div class="modal-dialog {{size}}">\
<div class="modal-content">\
<div class="modal-header">\
<h5 class="modal-title">{{title}}</h5>\
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>\
</div>\
<div class="modal-body">{{&body}}</div>\
<div class="modal-footer">{{&buttons}}</div>\
</div>\
</div>\
</div>';
/**
* Displays modal on the screen.
* @param {Object} options Options
* @param {string} options.title Title of the modal
* @param {html} options.body Body of the modal
* @param {string} options.size Size of the modal. Can be small, default, large or xlarge.
* @param {Object} options.actions Describes buttons to display on the modal. For each, specify label, cssClass, handler and optionally autoClose.
*/
var showModal = function(options) {
options = options || {};
options = $.extend({
title: '',
body: '',
size: false,
actions: false,
viewModel: {}
}, options);
var modalClass = {
small: "modal-sm",
default: "",
large: "modal-lg",
xlarge: "modal-xl"
};
var modalWrapper = $('<div data-bind="stopBinding: true"></div>').appendTo('body');
var buttons;
if (options.actions === false) {
buttons = '<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>';
} else {
buttons = "";
for (var i = 0, len = options.actions.length; i < len; i++) {
var currentButton = $.extend({
label: '<No label>',
cssClass: 'default',
handler: '',
autoClose: true
}, options.actions[i]);
var btn = '<button type="button" class="btn ' +
options.actions[i].cssClass +
'" data-bind="click: ' +
options.actions[i].handler +
(options.actions[i].autoClose === true ? '" data-dismiss="modal"' : '')
+ '>'
+ options.actions[i].label
+ '</button>';
buttons += btn;
}
}
var templateData = {
title: options.title,
body: options.body,
size: modalClass[options.size],
buttons: buttons
};
var modalHtml = Mustache.render(modalTemplate, templateData);
var $modal = $(modalHtml).appendTo(modalWrapper);
$modal.on('hidden.bs.modal', function (e) {
modalWrapper.remove();
});
ko.applyBindings(options.viewModel, $modal.get()[0]);
$modal.modal(options);
};
У меня есть проблема с этой функцией автоматического закрытия. Если он включен, viewmodel обрабатывает щелчок, модал закрывается механизмами Bootstrap и затем удаляется из DOM после скрытия.
Но когда я хочу, чтобы кнопка не была автоматически закрывающейся, у меня нет средств для закрытиямодальный от viewmodel. Решение, о котором я подумал, заключалось в том, чтобы внедрить метод для представления модели, например:
viewmodel['close'] = function() { $modal.modal('hide'); };
Однако это кажется хакерским решением (даже для Javascript;)). Точно так же я могу внедрить саму $modal
в модель представления, но это было бы еще более уродливо.
Что было бы тогда лучшим способом закрыть модал из модели представления модала?