因此,您希望我们帮助您完成的事情需要相当多的时间来构建,但实际上非常简单(公平地说,大多数基本 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 在将它们映射到 C# 对象时就没有问题了。
从长远来看,这可能不是一件好事,但为了简单起见,它会这样做。
所以基本上发生的情况是,当我们的 DriversViewModel 从服务器请求项目时,它会将所有数据映射到对 Knockout 友好的抽象,这些抽象会在它们发生变化时被 Knockout 跟踪。剩下的就是告诉 Knockout 使用这个 ViewModel:
ko.applyBindings(new DriversViewModel());
现在 knout 已经准备好处理这些对象了,是时候构建 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 上定义的不同添加/删除功能时,这一点很重要。)
就是这样。这是一个很好的测量结果解决方案的屏幕截图:
虽然它看起来很乱,但它完成了工作。
当您单击相应的添加按钮时,会添加汽车或司机。
单击 更新 按钮将组装整个树并将其发送回服务器,并在服务器上借助 ASP.NET MVC 的魔力将其转换为 POCO。
这里是代码的粘贴箱,因此您可以复制粘贴并亲自查看。您将不得不稍微摆弄一下 MVC 项目,但我相信您可以处理。
页面:
http://pastebin.com/2aGkEHEN
控制器:
http://pastebin.com/nZaufcpw
一个重要提示:
如果您真的想做这样的事情,您可能想要跟踪用户如何更改数据,而不是只获取整个更改的结构并覆盖旧数据。
我将尝试跟踪用户对树所做的更改,然后将某种 changeset 发送到服务器而不是整个结构,而不是我在示例中向您展示的幼稚方法。通过这种方式,您可以在事务中应用变更集并获得相同的结果,但带宽更少,一致性更高。