Closed. This question needs to be more focused。它当前不接受答案。
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            想改善这个问题吗?更新问题,使其仅通过editing this post专注于一个问题。
                        
                        3年前关闭。
                                                                                            
                
        
这是学习ASP.NET MVC 5的人的一个新手问题。我想创建一个Web应用程序屏幕,用户可以在其中编辑树状数据结构。我不确定如何遵循最佳做法。

为了简单起见,我们假设它是一个两级数据结构,每个驾驶员可以驾驶多辆汽车的驾驶员列表:

public class Car
{
  public string Name; { get; set; }
  public string Code; { get; set; }
}

public class Driver
{
  public string FullName { get; set; }
  public List<Cars> { get; set; }
}

public class Model
{
  public List<Driver> Drivers { get; set; }
}


我需要能够在同一屏幕上添加/删除/编辑驾驶员(及其汽车)。这是否意味着用户进行的每项更改(例如,添加一辆新车,然后更改其Code)都需要提交表单(即,对我的控制器进行点击)并将新视图发送回客户端?

如果是这样,如果我想在客户端上允许对多个模型进行更改,然后再将所有更新提交回我的控制器,是否需要使用MVVM框架(例如Angular或Knockout)?还是可以使用裸ASP.NET MVC来完成某些工作?

请注意,我不需要单页Web应用程序,我只想在执行HTTP发布操作之前在客户端缓存用户更新。

UPDATE,因此该Web应用程序当前可以经典的MVC方式工作:首先是具有驾驶员列表的视图,然后是具有汽车列表的视图,然后是一辆汽车。有一个单独的HTTP请求来呈现每个视图。

我想要的是让用户在同一视图内编辑整个结构,然后单击“更新”按钮。那时,我希望整个数据结构的更新的POCO模型可以提交并在MVC控制器中使用。我想要一个客户端JavaScript框架(Knockout,Angular,Aurelia等)来为我处理生成和更新DefaultModelBinder索引器,因此我不必手动管理索引器(即<input name="Drivers[2].Cars[1].Name" ... />等) ,有关更多详细信息,请参见this q/a)。

赏金将奖励给您一个答案,并给出相关示例代码,说明如何做到这一点。

最佳答案

因此,您想要我们帮助您的事情需要花费很多时间来构建,但实际上却非常简单(为公平起见,大多数基本的KO教程中都有足够的信息来完成所有这些工作。)

因此,我用3种方法构建了一个页面和一个MVC控制器:一个用于页面本身,另外两个用于GETTING或POSTING数据。

这是控制器的代码:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public JsonResult PostDriversModel(DriversModel model)
    {
        return Json(new { Success = true }, JsonRequestBehavior.AllowGet);
    }

    [HttpGet]
    public JsonResult GetDriversModel()
    {
        var model = new DriversModel
        {
            Drivers = new List<Driver>
            {
                new Driver
                {
                    FullName = "John Doe",
                    Cars = new List<Car>
                    {
                        new Car {Code = "car0", Name = "Amazing car"},
                        new Car {Code = "car1", Name = "Cool car"}
                    },
                },
                new Driver
                {
                    FullName = "Johnny Dough",
                    Cars = new List<Car>
                    {
                        new Car {Code = "car2", Name = "Another Amazing car"},                      new Car {Code = "car3", Name = "Another Cool car"}
                    }
                },
            }
        };

        return Json(model, JsonRequestBehavior.AllowGet);
    }
}


如您所见,该控制器是一个非常准系统,它们中最大的方法是GetDriversModel(),该方法优先处理带有示例数据的页面。

在这里,您可能会执行类似查询长期存储中要在客户端渲染的树的操作。很可能会用某种形式的ID进行标记,但是由于这些细节不在您的问题中,因此我将其省略。您可以通过此示例轻松地弄清楚。

最有趣的部分实际上是在页面上,我使用敲除为DriversModel数据结构构建了一个渲染器。首先,让我们检查一下JavaScript:

KO视图的核心是ViewModel:

    function DriversViewModel() {
        var self = this;

        self.Drivers = ko.observableArray([]);

        self.addDriver = function() {
            self.Drivers.push(new DriverModel({ FullName: 'Mr. Noname', Cars: [] }));
        };

        self.removeDriver = function(driver) {
            self.Drivers.remove(driver);
        };

        self.update = function() {
            $.ajax("/Home/PostDriversModel", {
                data: ko.toJSON({ Drivers: self.Drivers }),
                type: "post", contentType: "application/json",
                success: function () { alert('Success!'); }
            });
        }

        $.getJSON('/Home/GetDriversModel', function (data) {
            var drivers = data.Drivers.map(function (driver) { return new DriverModel(driver); });
            drivers.push(new DriverModel({ Cars: [], FullName: 'Mr Nocars' }));
            self.Drivers(drivers);
        });
    }


在其中,我们定义了两种从树中添加/删除驱动程序的方法,以及将内容发布回服务器的方法。 ViewModel非常简单(本示例中几乎所有内容)。请注意,在JS完成查询服务器数据之后,我已经在列表中添加了另一个随机驱动程序。这样做只是为了好玩(我也在做实验)。

以下是其余实体的ViewModel:

    function CarModel(data) {
        var self = this;

        self.Code = ko.observable(data.Code);
        self.Name = ko.observable(data.Name);
    }

    function DriverModel(data) {
        var self = this;

        self.addCar = function () {
            self.Cars.push(new CarModel({ Name: 'Tank', Code: '__' }));
        };

        self.removeCar = function (car) {
            self.Cars.remove(car);
        };

        self.Cars = ko.observableArray(data.Cars.map(function(car) { return new CarModel(car); }));
        self.FullName = ko.observable(data.FullName);
    }


如您所见,所有这些对象内部都有一些初始化逻辑,在这些逻辑中,我们将从服务器获取的JSON对象映射到客户端抽象。您还可以注意到,它们不遵循JavaScript对象的命名约定。我故意这样做,以便在客户端上完成与MVC的映射时,MVC在将它们映射到C#对象时不会出现问题。
从长远来看,这可能不是一件好事,但是为了简单起见,它会做到。

因此,基本上发生的是,当我们的DriversViewModel从服务器请求项目时,它将所有数据映射到Knockout友好的抽象中,随着Knockout的更改而对其进行跟踪。剩下的就是实际上告诉Knockout使用此ViewModel:

    ko.applyBindings(new DriversViewModel());


现在敲除已准备就绪,可以使用这些对象了,现在是时候构建UI部件了。

使用这些KO绑定的页面如下所示:

<div>
    <a href="#" data-bind="click: $root.addDriver">Add Driver</a>
    <a href="#" data-bind="click: $root.update">Update</a>
</div>

<ul data-bind="foreach: Drivers, visible: Drivers().length > 0">
    <ul>
        <div>
            <input data-bind="value: FullName"/>
            <a href="#" data-bind="click: $parent.removeDriver">Delete</a>
            <a href="#" data-bind="click: addCar">Add Car</a>
        </div>
        <ul class="no-cars" data-bind="visible: Cars().length == 0">No cars D:</ul>
        <ul data-bind="foreach: Cars, visible: Cars().length > 0">
            <li>
                <div>
                    <a href="#" data-bind="click: $parent.removeCar">Delete</a>
                    <label>Car Name:</label> <input data-bind="value: Name"/>
                    <label>Car Code:</label> <input data-bind="value: Code"/>
                </div>
            </li>
        </ul>
    </ul>
</ul>


如您所见,没有什么棘手的问题。关于这一切的最棘手的部分是知道要使用哪个数据绑定指令,并跟踪在哪个上下文中实际使用哪个ViewModel。 (当您使用在不同ViewModel上定义的不同添加/删除功能时,这一点很重要。)

就是这样。这是一个很好的解决方案屏幕截图:
c# - 使用ASP.MVC编辑树状结构-LMLPHP

虽然看起来很凌乱,但可以完成工作。
单击相应的“添加”按钮时,会添加汽车或驾驶员。
单击“更新”按钮将组装整个树并将其重新发布到服务器,在此通过ASP.NET MVC magic的功能将其转换为POCO。

这是代码的binsbin,因此您可以复制粘贴并自己查看。您将不得不花一些时间在MVC项目上,但是我相信您可以解决这个问题。

页:
http://pastebin.com/2aGkEHEN

控制器:
http://pastebin.com/nZaufcpw

重要说明:

如果您确实想执行类似的操作,则可能需要跟踪用户如何更改数据,而不是仅获取整个更改后的结构并覆盖旧数据。
而不是在示例中向您展示的简单方法,我将尝试跟踪用户对树所做的更改,然后将某种更改集(而不是整个结构)发送到服务器。这样,您可以在事务中应用变更集并获得相同的结果,但带宽更少,一致性更高。

关于c# - 使用ASP.MVC编辑树状结构,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/35283132/

10-12 00:01
查看更多