【问题标题】:How do I keep the original value selected when transitioning to edit mode?转换到编辑模式时如何保持选择的原始值?
【发布时间】:2012-03-13 09:13:50
【问题描述】:

选择列表未在选择正确选项的情况下呈现。我已经尝试了多种不同的方法,包括计算选择的 observable (this.selected = ko.computed(return parseInt(selected(), 10) == this.id; )) 并在数组函数中查找。

在生产中,dataArea 元素将填充服务器端数据。使用带有“data-”属性的 div 可以将服务器端和客户端脚本分开(我发现这有助于设计人员)。

记录将首先以非编辑模式显示,并带有通过单击编辑按钮进行编辑的选项。在编辑模式下,记录的初始值出现在输入控件中。您可以选择说,选择另一个客户并让表单加载新的关联项目。加载新客户将按预期重置项目列表。

因此,虽然加载新客户效果很好,但转换到编辑当前值会导致问题。所选项目需要出现在下拉列表中。如果选择了新客户,列表中会填充新选项并且不需要默认值。

http://jsfiddle.net/mathewvance/ZQLRx/

* 原样(请忽略)http://jsfiddle.net/mathewvance/wAGzh/ *

谢谢。

<p>
    issue: When the select options are read, the inital value gets reset to the first object in the options. How do I keep the original value selected when transitioning to edit mode?
</p>

<div>
    <h2>Edit Quote '1001'</h2>

    <div class="editor-row" data-bind="with: selectedCustomer">
        <label>Customer</label>
        <div data-bind="visible: !$root.isEditMode()">
            <span data-bind="text: CompanyName"></span>
        </div>
        <div data-bind="visible: $root.isEditMode()">
            <input type="radio" name="customerGroup" value="1" data-bind="value: id"> Company Name 1
            <input type="radio" name="customerGroup" value="2" data-bind="value: id"> Company Name 2
        </div>
    </div>

    <div class="editor-row">
        <label>Project</label>
        <div data-bind="visible: !isEditMode()">
            <span data-bind="text: selectedProject.Name"></span>
        </div>
        <div data-bind="visible: isEditMode()">
            <select data-bind="options: selectedCustomer().projects, optionsText: 'Name', value: selectedProject"></select>
        </div>
    </div>

    <div>
        <button data-bind="click: function() { turnOnEditMode() }">Edit</button>
        <button data-bind="click: function() { turnOffEditMode() }">Cancel</button>
    </div>
</div>

<hr/>

<div data-bind="text: ko.toJSON($root)"></div>

function ajaxCallGetProjectsByCustomer(customerId) {
    var database = '[{"CustomerId": 1, "Name":"Company Name 1", "Projects": [ { "ProjectId": "11", "Name": "project 11" }, { "ProjectId": "12", "Name": "project 12" }, { "ProjectId": "13", "Name": "project 13" }] }, {"CustomerId": 2, "Name": "Company Name 2", "Projects": [ { "ProjectId": "21", "Name": "project 21" }, { "ProjectId": "22", "Name": "project 22" }, { "ProjectId": "23", "Name": "project 23" }] }]';

    var json = ko.utils.parseJson(database);
    //console.log('parseJson(database) - ' + json);

    //ko.utils.arrayForEach(json, function(item) {
    //    console.log('CustomerId: ' + item.CustomerId);
    //});

    return ko.utils.arrayFirst(json, function(item){
        return item.CustomerId == customerId;
    });
}

var Customer = function(id, name, projects) {
    var self = this;

    this.id = ko.observable(id);
    this.CompanyName = ko.observable(name);

    this.projects = ko.observableArray(ko.utils.arrayMap(projects, function(item) {
        return new Project(item.ProjectId, item.Name);
    }));
};

Customer.load = function(id) {
    var data =  ajaxCallGetProjectsByCustomer(id);

    var customer = new Customer(
        data.CustomerId,
        data.Name,
        data.Projects);
 };

var Project= function(id, name) {
    this.id = id;
    this.Name = ko.observable(name);
 };

var QuoteViewModel = function () {
    var self = this;

    $customerData = $('#customerData'); // data from html elements
    $projectData = $('#projectData');

    // intial values to display from html data
    var customer = new Customer (
        $customerData .attr('data-id'),
        $customerData .attr('data-companyName'),
        [{"ProjectId": $projectData .attr('data-id'), "Name": $projectData .attr('data-name')}]
    )

    this.selectedCustomer = ko.observable(customer);
    this.selectedProject = ko.observable($projectData.attr('data-id'));

    this.isEditMode = ko.observable(false);

    this.selectedCustomer.subscribe(function(){
        // todo: load customer projects from database api when editing
    });

    this.turnOnEditMode = function() {
        var customerId = self.selectedCustomer().id();
        console.log('customerId: ' + customerId);
        Customer.load(customerId);
        self.isEditMode(true);
    };

    this.turnOffEditMode = function() {
        self.isEditMode(false);
    };
};

var viewModel = new QuoteViewModel();
ko.applyBindings(viewModel);

【问题讨论】:

  • 很难看出你在做什么。每次单击编辑按钮时都会重新加载thingamabob?我不明白你为什么需要 jquery dom 的东西,也许这与问题无关。你能准确解释你会发生什么吗?
  • 我添加了更多解释。目标是以只读模式显示记录,并可选择就地编辑,并在需要时将相关数据从服务器加载到控件中。选择不同的 thingamabob 是完整版中的一个选项,但我的问题只是编辑初始值,这就是演示的全部内容。

标签: javascript data-binding mvvm viewmodel knockout.js


【解决方案1】:

一个你加载的初始值

this.dongle = ko.observable($dongleData.attr('data-id'));

这将是字符串值“3”。加密狗 html 选择元素实际上正在保存/期望检索 object { "Id": "3", "Name": "dongle 3" }

这是一个可以获取正确初始值并允许编辑的工作版本。

http://jsfiddle.net/madcapnmckay/28FVr/5/

如果您需要保存特定值而不是整个加密狗/小部件对象,您可以使用optionsValue 属性仅存储 id。它以同样的方式工作。

http://jsfiddle.net/madcapnmckay/VnjyT/4/

编辑

好的,我为您准备了一个工作版本。我将尝试总结我所做的所有更改以及更改的原因。

http://jsfiddle.net/madcapnmckay/jXr8W/

让客户信息发挥作用

客户名称未存储在 ajaxCallGetProjectsByCustomer json 中,因此当您加载客户时,无法从收到的数据中确定新名称。我在 json 中为每个客户添加了一个名称属性,名称为“公司名称 1”等。

让项目集合工作

这里的问题与加密狗最初所说的一样。您使用$projectData.attr('data-id') 初始化selectedProject observable,它等于字符串值13。这是不正确的,因为选择列表的配置方式实际上是保存/期望接收项目对象本身。将此 id 分配更改为对象分配使项目的初始值正常工作。

var project = ko.utils.arrayFirst(customer.projects(), function(project){
    return project.id == Number($projectData.attr('data-id'));
});

this.selectedProject = ko.observable(project);

仅供参考,html 中有一个小错误,selectedProject.Name 需要是 selectedProject().Name。没什么大不了的。

我相信你可以很容易地弄清楚这些。下一点是真正的问题所在。每次单击编辑按钮时,您都会重新加载客户。这看起来很奇怪,您可能需要重新考虑这种方法。

但是发生的情况是您通过 id 从服务器加载客户对象。将它分配给 selectedCustomer 可观察对象,这实际上工作正常。但是因为下拉绑定到selectedCustomer().projectsviewModel.selectedProject,所以它期望 selectedProject 是 selectedCustomer().projects 的成员。对于对象,相等运算符正在评估引用是否匹配,而在您的情况下,它们不匹配,因为当您覆盖 selectedCustomer 值时,原始 selectedProject 与其关联的客户一起被破坏。 id 相同的事实是无关紧要的。

我已经设置了一个技巧来解决这个问题。

var oldProjectId = viewModel.selectedProject().id;
viewModel.selectedCustomer(customer);

var sameProjectDifferentInstance = ko.utils.arrayFirst(customer.projects(), function(project){
    return project.id == oldProjectId;
});

viewModel.selectedProject(sameProjectDifferentInstance || customer.projects()[0]);

这会在分配新客户之前保存旧的 projectId,在新客户对象中查找项目对象并分配它,如果没有找到则默认为第一个。

我建议重新考虑何时加载对象以及如何处理它们的生命周期。如果您将当前对象保存在内存中并包含完整的项目列表,则无需重新加载它们即可进行编辑,只需编辑然后将更新发送回服务器即可。

您可能会发现将来自服务器的 json 保存在 js 变量中而不是 html dom 元素中更容易。例如

<script>var projectInitialData = '@Model.ProjectInitialData.toJSON()';</script>

希望这会有所帮助。

【讨论】:

  • 感谢您查看此 madcapnmckay 并提出建议。我正在寻找一种与服务器端 MVC 方法相似的淘汰赛方法。选择对象而不仅仅是它们的 id,然后在 ui 的其他区域和状态中引用这些对象。与此示例类似:jsfiddle.net/mathewvance/Krc2L 这是先前在原地编辑动态填充的选择列表的尝试:jsfiddle.net/mathewvance/TZeBc
  • 镜像服务器端 MVC 方法是什么意思?在您的第二个示例中,从我的角度来看,令人困惑的是 this.isEditMode.subscribe 函数,它会在您每次单击编辑按钮时重新加载 thingamabob。您在开始时加载了 id 44,单击编辑,它重新加载它,您再次单击取消/编辑它重新加载它。如果您每次都重新加载,您如何看待这能够保存状态。如果我能更好地理解它,我可以为你解决这个问题:)
  • 顺便说一句,this.isEditMode.subscribe 调用设置 isEditMode 的编辑。目前不痛,但这没有意义。
  • 以我的第一个示例解决方案作为参考,也许您可​​以解释一下您需要哪些功能。
  • 我正在研究一个更简单且可能更好地说明设计目标的示例。虽然还没有完全完成,但它可能更容易参考。谢谢jsfiddle.net/mathewvance/ZQLRx
猜你喜欢
  • 2018-09-07
  • 1970-01-01
  • 2014-05-25
  • 1970-01-01
  • 2018-11-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-14
  • 1970-01-01
相关资源
最近更新 更多