Sam*_*nch 6 javascript jquery twitter-bootstrap knockout.js twitter-bootstrap-3
我正在尝试将一个twitter引导模式打开到一个窗口,该窗口中有一个可编辑的文本区域,然后在保存时,它会保存相应的数据.我目前的代码:
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>
Run Code Online (Sandbox Code Playgroud)
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));
Run Code Online (Sandbox Code Playgroud)
小提琴:http://jsfiddle.net/sL3HK/
正如您在小提琴中看到的那样,模态打开时会显示文本框,但是我无法弄清楚如何将"注释"文本放入模态中,或者在按下"保存"按钮时更新注释.
有任何想法吗?
另外,我对Knockout很新,所以如果那里看起来不太合适,请随时纠正我.
更新:
我一直在摆弄代码,并且能够将"评论"纳入模态,但到目前为止我还没有能够成功更新它.我最终遇到的另一个问题是,我只希望在点击"保存"时更新评论,而不是模糊的正常更新.我真的以为我会以错误的方式解决这个问题,但我不确定正确的方法是什么.非常感谢任何更多的帮助.
Pau*_* D. 16
这是一个JsFiddle,您可以在其中编辑每个条目的注释.以下是我如何获得这一点.
首先,我喜欢把我的观点分成几部分.对于每种类型的partial,我创建一个ViewModel.并且"上层"ViewModel用作所有部分ViewModel的容器.在这里你需要一个我用这种方式定义的EntryDataViewModel:
var EntryDataViewModel = function (rawEntryData) {
var self = this;
self.entry_data_id = rawEntryData.entry_data_id;
self.entry_id = rawEntryData.entry_id;
self.entry_hours = rawEntryData.entry_hours;
self.entry_date = rawEntryData.entry_date;
self.comment = ko.observable(rawEntryData.comment);
}
Run Code Online (Sandbox Code Playgroud)
基本上,这个构造函数会将您的原始数据转换为您可以在视图中操作的内容.根据您的想法,您可以观察或不观察.comment用于某些绑定,预计会发生变化.我们希望页面动态地对其更改做出反应,所以让它让它可观察.
由于这种变化,我们将改变我们创建"上层"ViewModel(此处TimeEntriesModel)的方式,特别是:
self.entries = ko.observableArray(ko.utils.arrayMap(entries, function (entry) {
return {
entry_id: entry.entry_id, //same as before
project_id: entry.project_id, // same as before
user_id: entry.user_id, // same as before
entry_data: ko.observableArray(entry.entry_data.map(function (entry_data) {
return new EntryDataViewModel(entry_data); // here we use the new constructor
}))
}
}));
Run Code Online (Sandbox Code Playgroud)
现在我们的ViewModel已准备好进行更新.所以让我们改变模态.
同样,在模态中,comment将会发生变化,我们想要检索它的值(更新我们的EntryData).所以这是一个可观察的.
现在我们必须通知我们正在修改哪个EntryData的模式(我认为这是你的代码缺少的主要部分).我们可以通过保留用于打开模态的EntryData的引用来完成此操作:
self.modal = {
...
comment:ko.observable(""),
entryData : undefined,
...
}
Run Code Online (Sandbox Code Playgroud)
最后要做的是在打开模态时更新所有这些变量:
self.showModal = function (entryDataViewModel) {
// modal.comment is already updated in your bindings, but logic can be moved here.
self.modal.entryData = entryDataViewModel; // keep track of who opened the modal
self.modal.show(true);
}
Run Code Online (Sandbox Code Playgroud)
当你保存:
self.onModalAction = function () {
self.modal.entryData.comment(self.modal.comment()); //save the modal's comment into the entryData.
self.modal.show(false);
}
Run Code Online (Sandbox Code Playgroud)
我不想更改所有绑定和代码,因此有很多小的更改,我认为你必须使用代码来查看它们如何影响页面的行为,它是如何工作的.我的解决方案当然不完美.您的HTML标记中仍然存在一些必须移动到JS的逻辑,我不确定您是否真的需要所有自定义绑定的东西.而且,我对这种模式并不满意.模态的东西应该属于一个EntryDataViewModel自编辑注释作用于一个EntryData,但正如我所说,我不想改变你的所有代码.如果你的解决方案有问题,请告诉我:).
当我说"将逻辑从HTML移动到JS"时,这就是我的意思.以下绑定看起来很复杂,属于HTML标记.
<a class="comment" data-bind="click: function() { $root.modal.comment(comment()); $root.showModal($data); }, css: { 'has-comment': comment().length > 0, 'needs-comment': comment().length == 0 }, attr: { title: comment() }">
Run Code Online (Sandbox Code Playgroud)
你可以做的一些事情:移动$root.modal.comment(comment())到showModal,然后你的点击绑定变为click : $root.showModal.即使是"需求注释"结合具有逻辑,你可以添加一个方法needsComment到您的EntryDataViewModel包含此逻辑.
请记住,HTML标记不应包含任何逻辑,它应该只调用JS函数.如果一个函数作用于视图的一部分(例如,一个EntryData),那么这个函数属于局部视图模型(这就是为什么我抱怨模态,它只作用于一个EntryData但是这里位于TimesEntriesModel).如果函数操作一组元素(例如,如果创建"添加"按钮),则此函数属于容器ViewModel.
这是一个非常漫长而具体的答案.为此道歉.您应该能够在Web上的Model View ViewModel(MVVM)上找到大量资源,这将有助于您的旅程:)
| 归档时间: |
|
| 查看次数: |
19284 次 |
| 最近记录: |