Dynamic Bootstrap Modal Content with Knockout
This is a demonstration of how to use Knockout.js with Twitter Bootstrap to edit a collection of items.
HTML
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.0/css/bootstrap.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-modal/2.2.6/css/bootstrap-modal.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-modal/2.2.6/css/bootstrap-modal-bs3patch.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-migrate/1.4.0/jquery-migrate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.0/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-modal/2.2.6/js/bootstrap-modalmanager.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-modal/2.2.6/js/bootstrap-modal.min.js"></script>
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
<div class="page-header">
<h1 class="text-center">
Dynamic Bootstrap Modal
Content with Knockout
</h1>
</div>
</div>
</div>
<div class="row">
<div class="well col-sm-6 col-sm-offset-3">
<p>
This is a demonstration of how to use
<a href="http://knockoutjs.com/" target="_blank">
Knockout.js
</a>
with
<a href="http://getbootstrap.com/" target="_blank">
Twitter Bootstrap
</a>
to edit a collection of items. This code also uses the
<a href="https://github.com/jschr/bootstrap-modal" target="_blank">
Bootstrap Modal
</a> plugin to assist with creating modal windows.
</p>
<p>
This code demonstrates the following:
</p>
<ul>
<li>
Basic <abbr title="Create, Read, Update, and Delete">CRUD</abbr> operations on a collection of data
</li>
<li>
Using modal dialogs for adding and editing...
SCSS
/**** These styles are here to override the styles in 'bootstrap-modal-bs3patch.css' ****/
$screen-sm-min = 768px; //<== Standard Bootstrap width
.modal {
left: 25%;
width: 50% !important;
@media screen and (-webkit-min-device-pixel-ratio:0) {
/* Safari and Chrome */
left: 50%;
width: 500px;
}
@media (max-width: $screen-sm-min) {
left: 0;
width: 100% !important;
}
}
JavaScript
ko.bindingHandlers['modal'] = {
init: function(element, valueAccessor, allBindingsAccessor) {
var allBindings = allBindingsAccessor();
var $element = $(element);
$element.addClass('hide modal');
if (allBindings.modalOptions && allBindings.modalOptions.beforeClose) {
$element.on('hide', function() {
var value = ko.utils.unwrapObservable(valueAccessor());
return allBindings.modalOptions.beforeClose(value);
});
}
},
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
if (value) {
$(element).removeClass('hide').modal('show');
} else {
$(element).modal('hide');
}
}
};
/* ViewModel for the individual records in our collection. */
var User = function(name, age) {
var self = this;
self.Name = ko.observable(ko.utils.unwrapObservable(name));
self.Age = ko.observable(ko.utils.unwrapObservable(age));
}
/* The page's main ViewModel. */
var ViewModel = function() {
var self = this;
self.Users = ko.observableArray();
self.ValidationErrors = ko.observableArray([]);
// Logic to ensure that user being edited is in a valid state
self.ValidateUser = function(user) {
if (!user) {
return false;
}
var currentUser = ko.utils.unwrapObservable(user);
var currentName = ko.utils.unwrapObservable(currentUser.Name);
var currentAge = ko.utils.unwrapObservable(currentUser.Age);
self.ValidationErrors.removeAll(); // Clear out any previous errors
if (!currentName)
self.ValidationErrors.push("The user's name is required.");
if (!currentAge) {
self.ValidationErrors.push("Please enter the user's age.");
} else { // Just some arbitrary checks here...
if (Number(currentAge) == currentAge && currentAge % 1 === 0) { // is a whole number
if (currentAge < 2) {
self.ValidationErrors.push("The user's age must be 2 or greater.");
} else if...