You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Knockout.js Foreach循环生成重复教室项问题求助

问题:合格教室类型重复显示

我们的UI中展示可参与评估的合格教室,创建并添加合格教室后,每种教室类型都出现了重复项,预期每种教室仅显示一项。

相关HTML

<div class="eligible-classrooms">
    <h5 class="semi-bold btm-gap-sm">Eligible Classrooms</h5>
    <div class="classrooms">
        <!-- ko foreach: model.sortedEligibleClassrooms -->
        <div class="classroom">
            <span data-bind="text: $data.ageRangeTypeName"></span><span class="count" data-bind="text: $data.numClassrooms"></span>
        </div>
        <!-- /ko -->
    </div>
</div>

相关TypeScript

Model定义

numberOfEligibleClassrooms: KnockoutObservableArray<{ ageRangeTypeId: number, ageRangeTypeName: string, numClassrooms: number }>,
sortedEligibleClassrooms: KnockoutComputed<Array<{ ageRangeTypeId: number, ageRangeTypeName: string, numClassrooms: number }>>

self.model实现

numberOfEligibleClassrooms: ko.observableArray([]),
sortedEligibleClassrooms: null,

constructor(orgCaseId: number, observationType: ObservationTypeModel, orgObservationSetTypeId: number) {
    var self = this;

    self.model.sortedEligibleClassrooms = ko.computed(function () {
        return self.model.numberOfEligibleClassrooms().sort((left: { ageRangeTypeId: number, ageRangeTypeName: string, numClassrooms: number }, right: { ageRangeTypeId: number, ageRangeTypeName: string, numClassrooms: number }): number => {
            return left.ageRangeTypeId > right.ageRangeTypeId ? 1 : -1;
        });
    }, self.model.numberOfEligibleClassrooms);
    
    self.load = (): void => {
        CommonFactory.AppSettings.get('OrgClassroomCollectMajorityAgeRange', function (setting: IApplicationSetting) {
            const majorityAgeRangeEnabled = setting ? setting.value.toString().toLowerCase() === 'true' : false;
            OrgFactory.ObservationTypeAgeRangeTypes.search({ "observationTypeId": self.model.observationType.id(), isCount: true }, function (ageRangeTypes) {
                for (var j = 0; j < ageRangeTypes.length; j++) {
                    // The following function preserves the scope of the variables 
                    (function (ageRangeTypeId, ageRangeTypeName) {
                        var minimumAgeRangeTypeId = self.settings.isERSObservation() ? ageRangeTypeId : null;
                        var search = {
                            minimumAgeRangeTypeId: minimumAgeRangeTypeId,
                            includesAgeRangeTypeId: majorityAgeRangeEnabled ? null : ageRangeTypeId,
                            majorityAgeRangeTypeId: majorityAgeRangeEnabled ? ageRangeTypeId : null,
                            isCount: true
                        };
                        OrgFactory.OrgCase.getEligibleObservationClassrooms(orgCaseId, search, function (data) {
                            self.model.numberOfEligibleClassrooms.push({ ageRangeTypeId: ageRangeTypeId, ageRangeTypeName: ageRangeTypeName, numClassrooms: data.length });
                        });
                    })(ageRangeTypes[j].ageRangeTypeId, ageRangeTypes[j].ageRangeTypeName);
                }
            });
        });
    }

实际输出

实际输出截图

解决方案

重复问题核心是加载数据时未处理旧数据残留或重复请求导致的重复插入,可通过以下方式修复:

1. 加载前清空数组

在load方法开头清空原有数组,避免旧数据与新数据叠加:

self.load = (): void => {
    // 清空原有数据
    self.model.numberOfEligibleClassrooms.removeAll();
    CommonFactory.AppSettings.get('OrgClassroomCollectMajorityAgeRange', function (setting: IApplicationSetting) {
        // 原有逻辑保持不变
    });
}

2. 插入前检查去重

如果存在同一类型被多次请求的情况,在push前检查是否已存在该教室类型,避免重复插入:

OrgFactory.OrgCase.getEligibleObservationClassrooms(orgCaseId, search, function (data) {
    // 根据ageRangeTypeId判断是否已存在
    const existingItem = self.model.numberOfEligibleClassrooms().find(item => item.ageRangeTypeId === ageRangeTypeId);
    if (!existingItem) {
        self.model.numberOfEligibleClassrooms.push({ ageRangeTypeId: ageRangeTypeId, ageRangeTypeName: ageRangeTypeName, numClassrooms: data.length });
    } else {
        // 如需更新数量,可在此修改并通知Knockout更新视图
        existingItem.numClassrooms = data.length;
        self.model.numberOfEligibleClassrooms.valueHasMutated();
    }
});

3. 计算属性中去重排序

修改sortedEligibleClassrooms的计算逻辑,先对数组去重再排序:

self.model.sortedEligibleClassrooms = ko.computed(function () {
    const classrooms = self.model.numberOfEligibleClassrooms();
    // 用ageRangeTypeId作为唯一标识去重
    const uniqueMap = new Map();
    classrooms.forEach(item => {
        if (!uniqueMap.has(item.ageRangeTypeId)) {
            uniqueMap.set(item.ageRangeTypeId, item);
        }
    });
    // 转换为数组并按id排序
    return Array.from(uniqueMap.values()).sort((left, right) => {
        return left.ageRangeTypeId > right.ageRangeTypeId ? 1 : -1;
    });
}, self.model.numberOfEligibleClassrooms);

4. 避免重复调用load

检查代码中是否存在多次触发load方法的场景(比如组件重复初始化、事件重复绑定),确保load仅在需要时执行一次。

内容的提问来源于stack exchange,提问作者Corey Sutton

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 19:20:53