将模态数据绑定到淘汰模型

时间:2013-12-23 21:41:58

标签: 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">&times;</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));

小提琴:http://jsfiddle.net/sL3HK/

正如您在小提琴中看到的那样,模态打开时带有文本框,但我无法弄清楚如何将“注释”文本放入模态中或在按下“保存”按钮时更新注释

有什么想法吗?

另外,我对Knockout很新,所以如果那里看起来不太合适,请随时纠正我。

更新:

我一直在摆弄代码,并且已经能够将“评论”纳入模态,但到目前为止我还没能成功更新它。我最终遇到的另一个问题是,我只希望在点击“保存”时更新评论,而不是模糊的正常更新。我真的认为我会以错误的方式解决这个问题,但我不确定正确的方式是什么。非常感谢任何帮助。

Updated fiddle.

2 个答案:

答案 0 :(得分:16)

以下是JsFiddle,您可以在其中编辑每个条目的评论。以下是我如何获得这一点。

ViewModels

首先,我喜欢将我的观点划分为部分。对于每种类型的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);
} 

基本上,这个构造函数会将您的原始数据转换为您可以在视图中操作的内容。根据您的想法,您可以观察或不观察。 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
        }))
    }
}));

现在我们的ViewModel已准备好进行更新。 所以让我们改变模态

模态

同样,在模态中,comment将会发生变化,我们想要检索其值(更新我们的EntryData)。所以这是一个可观察的 现在我们必须通知我们正在修改哪个EntryData的模式(我认为这是你的代码缺少的主要部分)。我们可以通过保留用于打开模态的EntryData的引用来完成此操作:

self.modal = {
   ...
   comment:ko.observable(""),
   entryData : undefined,
   ...
}

最后要做的是在打开模态时更新所有这些变量:

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);
}

当你保存时:

self.onModalAction = function () {
    self.modal.entryData.comment(self.modal.comment()); //save the modal's comment into the entryData.
    self.modal.show(false);
}

结论

我不想更改所有绑定和代码,因此会有很多小的更改,我认为您必须使用代码来查看它们如何影响页面的行为,它是如何工作的。 我的解决方案当然不完美。您的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() }">

您可以做的一些事情:将$root.modal.comment(comment())移至showModal,然后您的点击约束变为click : $root.showModal。即使是“需求 - 评论”绑定也有逻辑,您可以向包含此逻辑的needsComment添加方法EntryDataViewModel。 请记住,HTML标记不应包含任何逻辑,它应该只调用JS函数。如果一个函数作用于视图的一部分(例如,一个EntryData),那么这个函数属于局部视图模型(这就是为什么我抱怨模态,它只作用于一个EntryData但是这里位于TimesEntriesModel)。如果函数操作一组元素(例如,如果创建“添加”按钮),则此函数属于容器ViewModel。

这是一个非常长且具体的答案。为此道歉。您应该能够在网络上的模型视图ViewModel(MVVM)上找到大量资源,这将有助于您的旅程:)

答案 1 :(得分:2)

为了它的价值,我编写了knockout-modal项目,以便在使用Knockout时更容易使用模态。

欢迎任何有关它的反馈,无论如何我希望看看有帮助。