草庐IT

javascript - 将模态数据绑定(bind)到 knockout 模型

coder 2024-07-27 原文

我正在尝试将一个 Twitter Bootstrap 模式打开到一个窗口,该窗口中有一个可编辑的文本区域,然后在保存时,它会保存适当的数据。我当前的代码:

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

fiddle :http://jsfiddle.net/sL3HK/

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

有什么想法吗?

另外,我是 Knockout 的新手,所以如果其中有任何看起来不太正确的地方,请随时纠正我。

更新:

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

Updated fiddle.

最佳答案

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

ViewModels

首先,我喜欢将我的观点分成几个部分。对于每种类型的部分,我创建了一个 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);
} 

基本上,此构造函数将您的原始数据转换为您可以在 View 中操作的数据。根据您想做什么,您可以让事情变得可观察或不可观察。 comment 用于某些绑定(bind),预计会发生变化。我们希望页面对其变化做出动态 react ,所以让我们让它变得可观察。
由于此更改,我们将更改创建“上层”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);
}

结论

我不想更改您所有的绑定(bind)和代码,因此有很多小的更改,我认为您必须尝试使用​​这些代码来了解它们如何影响页面的行为,以及它是如何工作的。 我的解决方案当然不完美。您的 HTML 标记中仍有一些逻辑必须移至 JS,我不确定您是否真的需要所有自定义绑定(bind)内容。此外,我对模态不满意。模态内容应属于 EntryDataViewModel,因为编辑评论会作用于一个 EntryData,但正如我所说,我不想更改所有代码。如果您对我的解决方案有疑问,请告诉我 :)。

更新(一些进一步的提示)

当我说“将逻辑从 HTML 转移到 JS”时,这就是我的意思。以下绑定(bind)看起来很复杂,属于 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,然后你的点击绑定(bind)变成 click : $root .showModal。即使是“needs-comment”绑定(bind)也有逻辑,您可以将方法 needsComment 添加到包含此逻辑的 EntryDataViewModel
请记住,HTML 标记不应包含任何逻辑,它应该只是调用 JS 函数。如果一个函数作用于 View 的一部分(例如,一个 EntryData),那么这个函数属于部分 View 模型(这就是为什么我提示模态,它只作用于一个 EntryData 但这里位于TimesEntriesModel)。如果一个函数操作一组元素(例如,如果您创建一个“添加”按钮),则该函数属于容器 ViewModel。

这是一个非常长且具体的答案。对此表示歉意。您应该能够在网络上找到有关 Model View ViewModel (MVVM) 的大量资源,这将对您的旅程有所帮助:)

关于javascript - 将模态数据绑定(bind)到 knockout 模型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20751587/

有关javascript - 将模态数据绑定(bind)到 knockout 模型的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  4. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  5. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  6. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  7. ruby - ruby 中的 TOPLEVEL_BINDING 是什么? - 2

    它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput

  8. ruby-on-rails - 如何将验证与模型分开 - 2

    我有一些非常大的模型,我必须将它们迁移到最新版本的Rails。这些模型有相当多的验证(User有大约50个验证)。是否可以将所有这些验证移动到另一个文件中?说app/models/validations/user_validations.rb。如果可以,有人可以提供示例吗? 最佳答案 您可以为此使用关注点:#app/models/validations/user_validations.rbrequire'active_support/concern'moduleUserValidationsextendActiveSupport:

  9. ruby-on-rails - Rails 模型——非持久类成员或属性? - 2

    对于Rails模型,是否可以/建议让一个类的成员不持久保存到数据库中?我想将用户最后选择的类型存储在session变量中。由于我无法从我的模型中设置session变量,我想将值存储在一个“虚拟”类成员中,该成员只是将值传递回Controller。你能有这样的类(class)成员吗? 最佳答案 将非持久属性添加到Rails模型就像任何其他Ruby类一样:classUser扩展解释:在Ruby中,所有实例变量都是私有(private)的,不需要在赋值前定义。attr_accessor创建一个setter和getter方法:classUs

  10. ruby-on-rails - Rails - 从另一个模型中创建一个模型的实例 - 2

    我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案

随机推荐