Knockout.js 排序后KnockoutJS UI未使用丰富的可观察数组更新

Knockout.js 排序后KnockoutJS UI未使用丰富的可观察数组更新,knockout.js,Knockout.js,当我添加新教师时,它会对教师和包含的助手进行排序,但UI不会刷新。查看textarea控件,该控件显示添加新教师后,教师可观察数组会对其中的asstcol进行排序,但UI不会刷新。我尝试在self.teachers()可观察数组和包含的asstCols()上添加valueHasMutated(),但没有成功。我不知道我所做的是否过于复杂,但更大的应用程序也能够添加新的列/助手,并且需要能够在添加时按字母顺序对它们进行排序。因此,我故意以错误的顺序添加了助手列,以演示UI列刷新的问题。有趣的是,当

当我添加新教师时,它会对教师和包含的助手进行排序,但UI不会刷新。查看textarea控件,该控件显示添加新教师后,教师可观察数组会对其中的asstcol进行排序,但UI不会刷新。我尝试在self.teachers()可观察数组和包含的asstCols()上添加valueHasMutated(),但没有成功。我不知道我所做的是否过于复杂,但更大的应用程序也能够添加新的列/助手,并且需要能够在添加时按字母顺序对它们进行排序。因此,我故意以错误的顺序添加了助手列,以演示UI列刷新的问题。有趣的是,当您将Doug添加为教师时,它确实会将他添加到教师可观察数组的顶部,UI将他置于顶部,因此这是可行的,但是列需要注意。如果单击Doug第二列中的复选框,可以看到它正确地更新了百分比,但没有更新原始教师的百分比,因为UI不同步

视图:

两个问题:

  • teacher.asstCols()
    :在执行排序之前读取可观察对象
  • 您的数据不匹配。您在
    id
    属性值上输入了一个错误

    var initialAssistants = [
      new Assistant(20, "Bobby"),
      new Assistant(21, "Susie")
    ];
    
    new Teacher(1, "Jeff", [
     new AsstCol(20, "Susie", true, 50),
     new AsstCol(21, "Bobby", true, 33)
    ])
    

看起来你提供的小提琴不能正常工作。下拉列表中没有教师,Bobby也没有复选标记。我看到
可用的教师
,UI似乎正在正确更新。我认为在FF、IE和Chrome(最新版本)中,所有教师对给定助手的%更新都没有问题。您是否在旧版/特定浏览器上看到同步问题?啊,我现在看到了。我有一个Chrome插件阻止了一些脚本。明白了。。。为了便于查看,我只将相关数据放在另一个文件中。我在过去的jQuery sortable中也遇到过类似的问题。我在很久以前就发现了可观察的问题,但它仍然不太正确。我花了几分钟才注意到
id
不匹配。你的评论和我的回答几乎同时发布。但是,由于您强制刷新,
值发生了变化。从
assitCols
中删除
()
,您可以删除强制更新。太棒了!为了确认,将teacher.asstCols().sort(函数(a,b){更改为teacher.asstCols.sort(函数(a,b){是排序的问题。我没有意识到teacher.asstCols()第二,在InitialAssistants中,我为Bobby使用的ID为20,但在InitialTeachers中,我使用的ID为21。再次感谢!根据Origineil的答案更新了JSFIDLE at。
// methods
var Teacher = function (id, name, asstCols) {
    this.id = id;
    this.name = name;
    this.asstCols = ko.observableArray(asstCols).extend({ rateLimit: 0 });  // trigger just one re-evaluation of computed observable
};

var AsstCol = function (id, asstName, isChecked, percentage) {
    this.id = id;
    this.asstName = ko.observable(asstName);
    this.isChecked = ko.observable(isChecked);
    this.percentage = ko.observable(percentage);
};

var Assistant = function (id, name) {
    this.id = id;
    this.name = name;
};

var viewModel = function (teachers, assistants, columnList) {
    var self = this;
    self.teachers = ko.observableArray(teachers).extend({ rateLimit: 0 });
    self.columns = ko.observableArray();

    // set initial columns
    for (var index in columnList) {
    self.columns.push(columnList[index].asstName());
    };

    self.assistants = ko.observableArray(assistants).extend({ rateLimit: 0 });
    self.selectedTeacher = ko.observable("0");
    self.availableTeachers = ko.observableArray([
    new Teacher(5, "Doug", [{}]),
    new Teacher(6, "Kevin", [{}])
    ]);

    // methods
    // passes current item as the first parameter
    self.updatePercentage = function (asstCol) {
    var totalChecked = 0, percentage = 0;

    ko.utils.arrayForEach(self.teachers(), function (teacher) {
        for (var i = 0; i < self.columns().length; i++) {
        if (teacher.asstCols()[i].id == asstCol.id) {
            if (teacher.asstCols()[i].isChecked()) {
            totalChecked++;
            break;
            }
        }
        }
    });

    percentage = 100 / totalChecked;

    ko.utils.arrayForEach(self.teachers(), function (teacher) {
        for (var i = 0; i < self.columns().length; i++) {
        if (teacher.asstCols()[i].id == asstCol.id) {
            if (teacher.asstCols()[i].isChecked())
            teacher.asstCols()[i].percentage(percentage);
            else
            teacher.asstCols()[i].percentage(0);
        }
        }
    });

    return true;    // return default browser behavior to allow check/uncheck
    };

    // operations
    self.addTeacher = function () {
    self.teachers.push(new Teacher(ko.unwrap(self.selectedTeacher().id), ko.unwrap(self.selectedTeacher().name), undefined));

    // add each of the asst columns
    ko.utils.arrayForEach(self.assistants(), function (assistant) {
        self.teachers()[self.teachers().length - 1].asstCols.push(new AsstCol(assistant.id, assistant.name, false, 0));
    });
    self.teachers.sort(function(a, b) {
        return a.name.toLowerCase() == b.name.toLowerCase() ? 0 : (a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1);
    });
    self.columns.sort();
    ko.utils.arrayForEach(self.teachers(), function (teacher) {
        teacher.asstCols().sort(function(a, b) {
        return a.asstName().toLowerCase() == b.asstName().toLowerCase() ? 0 : 
        (a.asstName().toLowerCase() < b.asstName().toLowerCase() ? -1 : 1);
        });
    });

    // remove teacher from dropdown and reset the selection
    self.availableTeachers.remove(function (item) { return item.id == ko.unwrap(self.selectedTeacher().id); });
    self.selectedTeacher("0");
    };
};

var initialTeachers = [
    new Teacher(1, "Jeff", [
    new AsstCol(20, "Susie", true, 50),
    new AsstCol(21, "Bobby", true, 33)
    ]),
    new Teacher(2, "Joe", [
    new AsstCol(20, "Susie", false, 0),
    new AsstCol(21, "Bobby", true, 33)
    ]),
    new Teacher(3, "Josie", [
    new AsstCol(20, "Susie", true, 50),
    new AsstCol(21, "Bobby", true, 33)
    ])
];

var initialAssistants = [
    new Assistant(20, "Bobby"),
    new Assistant(21, "Susie")
];

var vm = new viewModel(initialTeachers, initialAssistants, initialTeachers[0].asstCols());
ko.applyBindings(vm);
table {
    border-spacing: 0px;
    border-collapse: collapse;
}
td, th {
    border: solid 1px black;
    padding: 2px;
}
.reset td, th {
    border: 0;
    padding: 0;
}
.cellLeft {
    border: 0;
    border-left:solid 1px black;
    border-bottom:solid 1px black;
    padding:1px 1px 1px 4px;
}
.cellRight {
    border: 0;
    border-right:solid 1px black;
    border-bottom:solid 1px black;
    padding:1px; padding:1px 4px 1px 1px;
}
var initialAssistants = [
  new Assistant(20, "Bobby"),
  new Assistant(21, "Susie")
];

new Teacher(1, "Jeff", [
 new AsstCol(20, "Susie", true, 50),
 new AsstCol(21, "Bobby", true, 33)
])