转换到编辑模式时如何保持选择原始值?

时间:2012-02-22 23:41:37

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

选择正确的选项时,选择列表不会呈现。我已经尝试了很多不同的方法,包括计算选择的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);

1 个答案:

答案 0 :(得分:0)

您加载的初始值

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

这将是字符串值“3”。加密狗html选择元素实际上正在保存/期望检索对象 { "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中为每个客户添加了一个Name属性,名称为“Company Name 1”等。

让项目集合发挥作用

这里的问题与加密狗一样。使用selectedProject初始化$projectData.attr('data-id') 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需要被选为项目()。名称。没什么大不了的。

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

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

我已经实施了一个黑客来解决这个问题。

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,在新客户对象中查找项目对象并将其分配,如果未找到则默认为第一个。

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

您可能会发现在js变量而不是html dom元素中更容易从服务器中保存json。 e.g。

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

希望这有帮助。