Так что вы хотели, чтобы помочь вам с необходимой совсем немного времени, чтобы построить, но на самом деле очень просто (Чтобы быть справедливым, не было достаточно информации, в большинстве основных учебников KO, чтобы сделать все это.)
Итак, я построил одну страницу и один контроллер MVC с тремя способами: один для самой страницы и два для GET ting или POST Данные.
Вот код контроллера:
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(), который priovides страницы с образцами данных для работы с.
Здесь вы, вероятно, будете делать что-то вроде запроса к вашему долгосрочному хранилищу для дерева, которое будет отображаться на стороне клиента. Скорее всего, он будет отмечен каким-то идентификатором, но поскольку эти детали не были в вашем вопросе, я их пропустил. Вы можете легко понять это с помощью этого примера.
Самая интересная часть на самом деле находится на той странице, где я использовал нокаут для создания рендерера для структуры данных 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 ahs выполнил запрос сервера для данных. Это делается только ради удовольствия (я тоже экспериментировал).
Вот ViewModels на оставшуюся часть лиц:
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 запрашивает элементы с сервера, он отображает все данные в ночные развлекательные абстракции, которые отслеживаются нокаутом по мере их изменения. Все, что остается на самом деле сказать Нокаут использовать этот ViewModel:
ko.applyBindings(new DriversViewModel());
Теперь Нокаут готов работать с этими объектами, и настало время для нас, чтобы построить часть пользовательского интерфейса.
страница, которая использует эти привязки 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 вы используете в этом контексте. (Это важно, когда вы используете разные функции добавления/удаления, определенные на разных моделях ViewModels.)
И все. Вот скриншот полученного решения для хорошей меры:
И хотя он выглядит довольно грязным, он выполняет свою работу. При нажатии кнопки Добавить кнопки, автомобили или драйверы. Щелчок по кнопке Обновление приведет к сборке всего дерева и отправке его обратно на сервер, где он будет преобразован в POCO с помощью магии ASP.NET MVC.
Здесь представлены пастихины кода, чтобы вы могли просто скопировать-вставить и посмотреть его сами. Вам придется немного поиграть с проектом MVC, но я считаю, что вы можете справиться с этим.
Страница: http://pastebin.com/2aGkEHEN
Контроллер: http://pastebin.com/nZaufcpw
Одно важное замечание:
Если вы действительно хотите сделать что-то вроде этого, вы можете захотеть, чтобы отслеживать пользовательские изменения данные вместо этого просто получают всю измененную структуру и перезаписывают старые данные. Вместо того, чтобы найти нужный подход, я показал вам в этом примере, я бы попытался отслеживать изменения, которые пользователь делает с деревом, а затем отправляет на сервер вместо целой структуры какой-то набор изменений. Таким образом, вы можете применить набор изменений в транзакции и добиться того же результата, но с меньшей пропускной способностью и большей согласованностью.
Некоторые варианты динамического добавления (и удаления) элементов коллекции в ответах [здесь] (http://stackoverflow.com/questions/29161481/post-a-form-array-without-successful/29161796#29161796) и [здесь] (http://stackoverflow.com/questions/28019793/submit-same-partial-view-called-multiple-times-data-to-controller/28081308#28081308) –
@StephenMuecke, правильно ли я понимаю, что Мне нужно генерировать правильные скрытые индексы в HTML (и обновлять их, поскольку пользователь редактирует данные в форме)? Не могли бы вы указать мне некоторые документы, объясняющие, как данные модели POCO собираются обратно из HTML, прежде чем они будут отправлены обратно контроллеру и как это можно настроить? Спасибо. – avo
Да, это правильно. Индексаторы коллекции должны начинаться с нуля и быть последовательными, если вы не добавите скрытое поле для свойства «Index», которое позволяет «DefaultModelBinder» соответствовать нескольким индексаторам. –