如何处理淘汰视图模型中的多对多关系

Posted

技术标签:

【中文标题】如何处理淘汰视图模型中的多对多关系【英文标题】:How to handle many to many relationship in knockout viewmodels 【发布时间】:2014-04-30 15:25:36 【问题描述】:

我的架构中有 2 个表 ServiceEmployee 之间的多对多关系(一个服务可以由多个员工执行,一个员工可以执行多个服务) .我使用 ServiceEmployee 联结表来创建这种关系。

我在客户端使用淘汰赛。淘汰视图模型是通过来自服务器端视图模型的 knockout.mapping 插件创建的。在服务器端,我有 3 个视图模型,它们是:

    EmployeeModel(包含 ServiceEmployeeModel 列表

    ServiceModel(包含 ServiceEmployeeModel 列表

    ServiceEmployeeModel(包含 ServiceId、EmployeeId)[不能包括 Employee 和 服务对象以避免客户端的自引用循环]

现在在客户端我有 3 个模块:

员工模块

function Employee(data)

    var self = this;

    ko.mapping.fromJS(data, 
        "Services": 
            create: function (options) 
                return new serviceEmployee(options.data, options.parent);
            
        
    , self);

    ....    

服务模块

function Service(data)

    var self = this;

    ko.mapping.fromJS(data, 
        "Employees": 
            create: function (options) 
                return new serviceEmployee(options.data, options.parent);
            
        
    , self);

    ....    

ServiceEmployee 模块

function (data, parent) 

    var self = this;

    ko.mapping.fromJS(data, self);

    //If parent object has property EmployeeId it means the parent object is Employee 
    //object and we will add only related Service (not Employee) in order 
    //to avoid self reference loop.
    if (parent.EmployeeId) 
        self.Service = ko.computed(function () 
            if (self.ServiceId()) 
                var services = require("modules/tenant").services();
                if (services) 
                    var assignedService;
                    ko.utils.arrayFirst(services(), function (service) 
                        if (service.ServiceId() === self.ServiceId()) 
                            assignedService = service;
                            return true;
                        
                    );
                    return assignedService;
                
            
        );
    

    //If parent object has property ServiceId it means the parent object is Service  
    //object and we will add only related Employee (not Service) in order  
    //to avoid self reference loop.
    if (parent.ServiceId) 
        self.Employee = ko.computed(function () 
            if (self.EmployeeId()) 
                var staff = require("modules/tenant").staff();
                if (staff) 
                    var assignedEmployee;
                    ko.utils.arrayFirst(staff(), function (employee) 
                        if (employee.EmployeeId() === self.EmployeeId()) 
                            assignedEmployee = employee;
                            return true;
                        
                    );
                    return assignedEmployee;
                
            
        );
    

我使用的方法很有效,但我觉得应该有其他更好的方法来处理这种情况。因为在这种方法中如果我们将服务分配给员工或员工服务,那么我们必须手动更新员工和服务数组,我觉得应该有一些更好的方法,以便淘汰赛会为我更新这些数组。

Computed observable 可能是解决方案,但我不知道如何解决。谁能帮我解决这个问题?

【问题讨论】:

你说的自引用循环,是指你运行toJSON的时候吗? 【参考方案1】:

解决方案 A:

您可能想尝试微风:http://www.breezejs.com/ 他们不直接支持多对多关系,但是如果您像这样公开 navigaiton 对象:

Service.Employees // Array of ServiceEmployee
ServiceEmployee.Employee // Employee
ServiceEmployee.Service // Service
Employee.Services // Array of ServiceEmplyee

Breeze 提供了自动跟踪哪些员工和服务相关的机制,允许您执行以下操作:

var myEmployeesServices = ko.computed(function () 
    return myEmployee.Services().map(function (a) 
        return a.Service;
    ;
); // an always up to date array of services related to a specific employee
var newService = entityManager.createEntity("Service", )
    //entityManager is a class defined by breeze
    //createEntity is a function provided to create new breeze managed objects
var newServiceLink = entityManager.createEntity("ServiceEmployee", 
    EmployeeId: myEmployee.Id,
    ServiceId: newService.Id
); // creates a new ServiceEmployee object linking myEmployee and newService

// myEmployeeServices now also contains newService

如果你想使用微风,你必须阅读其他微风功能,例如加载和保存数据等。

解决方案 B:

您可能想查看 ko.mapping 插件的忽略属性,例如:

function Employee(data)

    var self = this;

    ko.mapping.fromJS(data, 
        "Services": 
            create: function (options) 
                return new serviceEmployee(options.data, options.parent);
            
        
    , self);

        self.ServiceObjects = ko.computed(function () 
            var staff = require("modules/tenant").services();
            return staff().filter(function (a) 
                return self.Services.filter(function (b) 
                    return b.ServiceId() == a.ServiceId();
                ).length;
            )
        );
    ....    


function serviceEmployee(data, parent) 
    this.EmployeeId = parent.EmployeeId || data.EmployeeId;
    this.ServiceId = parent.ServiceId || data.ServiceId;

如果我正确编写了映射部分(不确定是否正确,请在此处查看更多详细信息http://knockoutjs.com/documentation/plugins-mapping.html),那么当您取消映射员工模型时,映射插件应该忽略您计算的 ServiceObjects

您还可以通过在 Services 数组中添加和删除对象以响应 ServiceObjects 数组中的更改来添加写入函数以添加到您的数组中。

解决方案 C:

看看这个其他解决方案:Knockout JS ObservableArray with many-to-many relationships

【讨论】:

以上是关于如何处理淘汰视图模型中的多对多关系的主要内容,如果未能解决你的问题,请参考以下文章

如何处理MongoDB中的多对多关系?

如何处理 JSON 中的多对多关系?

使用 Spring Boot、Jackson 和 Hibernate 的多对多关系

MVC4 DTO 和多对多关系与 WebAPI 的扩展方法

如何在 django 中处理未保存的多对多关系?

从laravel中的多对多关系中获取单列