I'm trying to have a twitter bootstrap modal open to a window that has a text area in it which is editable, then on save, it saves the appropriate data. My current code:
HTML:
<table class="display table table-striped">
<tbody data-bind="foreach: entries">
<tr>
<td>
Placeholder
</td>
<!-- ko foreach: entry_data -->
<td>
<div class="input-group">
<input type="text" class="form-control col-sm-2" data-bind="value: entry_hours">
<span class="input-group-addon"><a class="comment" data-bind="click: function() { $root.modal.comment($data); $root.showModal(); }, css: { 'has-comment': comment.length > 0, 'needs-comment': comment.length == 0 }, attr: { title: comment }"><span class="glyphicon glyphicon-comment"></span></a></span>
</div>
</td>
<!-- /ko -->
</tr>
</tbody>
</table>
<!-- Modal template -->
<script id="commentsModal" class="modal-dialog" type="text/html">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-bind="click:close" aria-hidden="true">×</button>
<h4 data-bind="html:header" class="modal-title"></h4>
</div>
<div class="modal-body">
<textarea class="form-control" rows="3" data-bind="value: $root.modal.comment.comment"></textarea>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-bind="click:close,html:closeLabel">Close</button>
<button type="button" class="btn btn-primary" data-bind="click:action,html:primaryLabel" id="save-changes">Save changes</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</script>
<!-- Create a modal via custom binding -->
<div data-bind="bootstrapModal:modal" class="modal fade" id="commentsModal" tabindex="-1" role="dialog" data-keyboard="false" data-backdrop="static"></div>
JS:
/* Custom binding for making modals */
ko.bindingHandlers.bootstrapModal = {
init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var props = valueAccessor(),
vm = bindingContext.createChildContext(viewModel);
ko.utils.extend(vm, props);
vm.close = function() {
vm.show(false);
vm.onClose();
};
vm.action = function() {
vm.onAction();
}
ko.utils.toggleDomNodeCssClass(element, "modal fade", true);
ko.renderTemplate("commentsModal", vm, null, element);
var showHide = ko.computed(function() {
$(element).modal(vm.show() ? 'show' : 'hide');
});
return {
controlsDescendantBindings: true
};
}
}
var entriesdata = [{"entry_id":"51794","project_id":"2571","user_id":"89","entry_data":[{"entry_data_id":"359192","entry_id":"51794","entry_hours":"0.00","entry_date":"2013-12-22","comment":""},{"entry_data_id":"359193","entry_id":"51794","entry_hours":"8.00","entry_date":"2013-12-23","comment":"Test comment"},{"entry_data_id":"359194","entry_id":"51794","entry_hours":"8.00","entry_date":"2013-12-24","comment":"Test comment"},{"entry_data_id":"359195","entry_id":"51794","entry_hours":"0.00","entry_date":"2013-12-25","comment":""},{"entry_data_id":"359196","entry_id":"51794","entry_hours":"8.00","entry_date":"2013-12-26","comment":"Test comment"},{"entry_data_id":"359197","entry_id":"51794","entry_hours":"8.00","entry_date":"2013-12-27","comment":"Test comment"},{"entry_data_id":"359198","entry_id":"51794","entry_hours":"0.00","entry_date":"2013-12-28","comment":""}]}];
var projectsdata = [{"project_txt":"Test Project","project_id":12345}];
var TimeEntriesModel = function(entries, projects) {
var self = this;
self.projects = ko.observableArray(projects);
self.entries = ko.observableArray(ko.utils.arrayMap(entries, function(entry) {
return {
entry_id : entry.entry_id,
project_id : entry.project_id,
user_id : entry.user_id,
entry_data : ko.observableArray(entry.entry_data)
}
}));
self.save = function () {
ko.utils.stringifyJson(self.entries);
}
self.modal = {
header: "Add/Edit Comment",
comment: ko.observableArray([{comment: "test"}]),
closeLabel: "Cancel",
primaryLabel: "Save",
show: ko.observable(false), /* Set to true to show initially */
onClose: function() {
self.onModalClose();
},
onAction: function() {
self.onModalAction();
}
}
console.log(ko.isObservable(self.modal.comment));
self.showModal = function() {
self.modal.show(true);
}
self.onModalClose = function() {
// alert("CLOSE!");
}
self.onModalAction = function() {
// alert("ACTION!");
self.modal.show(false);
}
}
ko.applyBindings(new TimeEntriesModel(entriesdata, projectsdata));
Fiddle: http://jsfiddle.net/sL3HK/
As you can see in the fiddle, the modal opens with the text box, but I'm unable to figure out how to get the 'comment' text into the modal or update the comment when the 'save' button is pressed.
Any ideas?
Also, I'm very new to Knockout, so if there's anything in there that doesn't look quite right, please feel free to correct me on it.
UPDATE:
I've been fiddling with the code, and have been able to get the "comment" into the modal, but I've not been able to successfully update it up to this point. And another problem I will eventually run into is that I only want the comment to be updated when "Save" is clicked, rather than the normal update on blur. I really think I'm going about this the wrong way, but I'm not sure what the right way is. Any more help is greatly appreciated.