简体   繁体   English

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

[英]Bind modal data to knockout model

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. 我正在尝试将一个twitter引导模式打开到一个窗口,该窗口中有一个可编辑的文本区域,然后在保存时,它会保存相应的数据。 My current code: 我目前的代码:

HTML: 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: 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/ 小提琴: 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. 另外,我对Knockout很新,所以如果那里看起来不太合适,请随时纠正我。

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. 非常感谢任何更多的帮助。

Updated fiddle. 更新了小提琴。

Here is a JsFiddle in which you should be able to edit comment for each entry. 这是一个JsFiddle ,您可以在其中编辑每个条目的注释。 Here is how I proceeded to obtain this. 以下是我如何获得这一点。

The ViewModels ViewModels

First, I like to divide my views into partials. 首先,我喜欢把我的观点分成几部分。 For each type of partial, I create a ViewModel. 对于每种类型的partial,我创建一个ViewModel。 And an "upper level" ViewModel is used as a container for all the partial ViewModels. 并且“上层”ViewModel用作所有部分ViewModel的容器。 Here you'll need a EntryDataViewModel which I defined this way : 在这里你需要一个我用这种方式定义的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);
} 

Basically, this constructor does the conversion from your raw data to something you will be able to manipulate in your views. 基本上,这个构造函数会将您的原始数据转换为您可以在视图中操作的内容。 Depending on what you want to do, you can make things observable or not. 根据您的想法,您可以观察或不观察。 comment is used in some bindings and is expected to change. comment用于某些绑定,预计会发生变化。 We want the page to react dynamically to its changes, so let's make it observable. 我们希望页面动态地对其更改做出反应,所以让它让它可观察。
Because of this change, we will change the way we create the "upper level" ViewModel (here TimeEntriesModel ), and in particular : 由于这种变化,我们将改变我们创建“上层”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
        }))
    }
}));

Now our ViewModels are ready to be updated. 现在我们的ViewModel已准备好进行更新。 So let's change the modal . 所以让我们改变模态

The Modal 模态

Again, in the modal, the comment will be subject to change, and we want to retrieve its value (to update our EntryData). 同样,在模态中, comment可能会发生变化,我们想要检索其值(更新我们的EntryData)。 So it's an observable. 所以这是一个可观察的。
Now we have to inform the modal of which EntryData we are modifying (and I think this is the main part your code was lacking). 现在我们必须通知我们正在修改哪个EntryData的模式(我认为这是你的代码缺少的主要部分)。 We can do this by keeping a reference of the EntryData that was used to open the modal : 我们可以通过保留用于打开模态的EntryData的引用来完成此操作:

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

Last thing to do is to update all these variables when you open the modal : 最后要做的是在打开模态时更新所有这些变量:

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

And when you save : 当你保存:

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

Conclusion 结论

I did not want to change all your bindings and code, thus there were a lot of little changes and I think you'll have to play with the code to see how they affect the behavior of the page, how it works. 我不想更改所有绑定和代码,因此有很多小的更改,我认为你必须使用代码来查看它们如何影响页面的行为,它是如何工作的。 My solution is not perfect of course . 我的解决方案当然不完美 There remains some logic in your HTML markup that must be moved to the JS and I'm not sure you really need all the custom binding stuff. 您的HTML标记中仍然存在一些必须移动到JS的逻辑,我不确定您是否真的需要所有自定义绑定的东西。 Moreover, I'm not happy about the modal. 而且,我对这种模式并不满意。 The modal stuff should belong to a EntryDataViewModel since editing the comment acts on one EntryData, but as I said, I did not want to change all your code. 模态内容应属于EntryDataViewModel因为编辑注释会对一个EntryData起作用,但正如我所说,我不想更改所有代码。 Tell me if you have problems with my solution :). 如果你的解决方案有问题,请告诉我:)。

Update (some hints for going further) 更新(进一步的一些提示)

When I said "moving logic from HTML to JS", here is what I meant. 当我说“将逻辑从HTML移动到JS”时,这就是我的意思。 The following binding looks to complicated to belong to HTML markup. 以下绑定看起来很复杂,属于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() }">

Some things you could do : move $root.modal.comment(comment()) to showModal , then your click binding becomes click : $root.showModal . 你可以做的一些事情:将$root.modal.comment(comment())showModal ,然后点击绑定变为click : $root.showModal Even the "needs-comment" binding has a logic, you could add a method needsComment to your EntryDataViewModel that contains this logic. 即使是“needs-comment”绑定也有逻辑,你可以在包含这个逻辑的EntryDataViewModel中添加一个方法needsComment
Keep in mind that HTML markup should not contain any logic, it should just make calls to JS functions. 请记住,HTML标记不应包含任何逻辑,它应该只调用JS函数。 If a function acts on an partial of the view (for example, an EntryData), then this function belongs to the partial view model (this is why I was complaining about the modal, that acts on only one EntryData but here is located in the TimesEntriesModel ). 如果一个函数作用于视图的一部分(例如,一个EntryData),那么这个函数属于局部视图模型(这就是为什么我抱怨模态,它只作用于一个EntryData但是这里位于TimesEntriesModel )。 If a function manipulates a set of elements (for example, if you create an "add" button), this function belongs in the container ViewModel. 如果函数操作一组元素(例如,如果创建“添加”按钮),则此函数属于容器ViewModel。

This was a VERY long and specific answer. 这是一个非常漫长而具体的答案。 Apologies for that . 为此道歉 You should be able to find a lot of resources on Model View ViewModel (MVVM) on the web, that will help you in your journey :) 您应该能够在Web上的Model View ViewModel(MVVM)上找到大量资源,这将有助于您的旅程:)

For what it's worth, I wrote the knockout-modal project to make modals easier to work with when using Knockout. 为了它的价值,我编写了knockout-modal项目,以便在使用Knockout时更容易使用模态。

Would welcome any feedback on it, and in any case I hope it is helpful to look at. 欢迎任何有关它的反馈,无论如何,我希望看看有帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM