Knockout Nested List Post Нуль
Я пытаюсь интегрировать ко на одном из моих взглядов бритвы. Вчера я немного исследовал, но не смог найти аналогичного решения для моей проблемы.
У меня есть модель записи:
public class Record
{
public Int Request Id {get;set;}
public string RecordName {get;set;}
public Person Person {get;set;}
public IList<Person> People { get; set; }
}
И модель человека:
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public IList<Alias> Aliases { get; set; }
}
Мой взгляд:
@model Record
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
@using (Html.BeginForm("PostRequest", "Request", FormMethod.Post))
{
@Html.LabelFor(m => m.Person.FirstName)
@Html.TextBoxFor(m => m.Person.FirstName)
<h5>People</h5>
<table>
<tbody data-bind="foreach: People">
<tr>
<td>@Html.LabelFor(m => m.Person.FirstName)</td>
<td><input type="text" data-bind="value: FirstName, attr: {name: 'People[' + $index() + '].FirstName'}" /></td>
<td>@Html.LabelFor(m => m.Person.LastName)</td>
<td><input type="text" data-bind="value: LastName, attr: {name: 'People[' + $index() + '].LastName'}" /></td>
</tr>
<tr>
<td><button id="removePerson" data-bind="click: $root.removePerson">Remove Person</button></td>
</tr>
<tr>
<td>
<button id="addAlias" data-bind="click: addAlias">Add Alias</button>
</td>
</tr>
<!-- ko foreach: Aliases -->
<tr>
<td>@Html.LabelFor(m => m.Alias.FirstNameAlias)</td>
<td><input type="text" data-bind="value: FirstNameAlias, attr: {name: 'Aliases[' + $index() + '].FirstNameAlias'}" /></td>
<td>@Html.LabelFor(m => m.Alias.LastNameAlias)</td>
<td><input type="text" data-bind="value: LastNameAlias, attr: {name: 'Aliases[' + $index() + '].LastNameAlias'}" /></td>
<td><button id="removeAlias" data-bind="click: $root.removeAlias">Remove Alias</button></td>
</tr>
<!-- /ko -->
</tbody>
</table>
<button id="addPerson" data-bind="click: addPerson">Add Person</button>
<button>Submit</button>
<input id="clickMe" type="button" value="clickme" onclick="submit();" />
}
И сценарий:
@section scripts
{
<script type="text/javascript">
$(function () {
var personItem = function () {
var self = this;
self.LastName = ko.observable();
self.FirstName = ko.observable();
self.Aliases = ko.observableArray();
};
var model = ko.mapping.fromJS(@Html.Raw(Model.ToJson()));
alert('The length of the array is ' + model.People().length);
alert('The first element is ' + model.People()[0].Aliases().length);
alert(ko.toJSON(model));
model.addPerson = function () {
model.People.push(new personItem());
};
model.removePerson = function (person) {
model.People.remove(person);
};
ko.applyBindings(model);
})
function submit() {
$.ajax({
url: '/Request/PostRequest',
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: ko.toJSON(model),
success: function (status) {
alert(status);
}
});
};
</script>
}
Действие Controller, которое ПОЛУЧАЕТ это представление, предварительно инициализирует некоторые данные для модели записи, поэтому привязка, кажется, работает должным образом, однако, когда я публикую данные, данные для Alias List возвращаются к нулю.
контроллер:
public ActionResult CreateRequest(Record viewModel)
{
var list = new Record
{
People = new List<Person> {
new Person {FirstName = "My First Person", Aliases = new List<Alias> { new Alias{FirstNameAlias = "firstnamealias",LastNameAlias = "lastnamealias1"}},},
new Person {FirstName = "My Second Person", Aliases = new List<Alias> { new Alias{FirstNameAlias = "firstnamealias2", LastNameAlias ="lastnamealias2"}},}
},
Person = new Person()
{
FirstName = "me"
}
};
return View(list);
}
[HttpPost]
public JsonResult PostRequest(Record viewModel)
{
return Json(String.Format("'Success':'false','Error':'"));
}
Я чувствую, что мне чего-то не хватает в ko.mapping. Но так близко, поскольку данные заполняются от действия контроллера GET, но не в состоянии POST.
РЕШЕНИЕ
Я обновил свой KO-скрипт и то, как я рендерил KO-данные в виде. Это решило мою проблему.
Обновленный скрипт
<script>
var initialData = @Html.Raw(Serialize(Model.People));
var BackgroundModel = function(people) {
var self = this;
self.people = ko.mapping.fromJS(people);
self.addPerson = function() {
self.people.push({
FirstName: "",
LastName: "",
Aliases: ko.observableArray()
});
};
self.removePerson = function(person) {
self.people.remove(person);
};
self.addAlias = function(person) {
person.Aliases.push({
//todo
FirstNameAlias: "",
LastNameAlias: ""
});
};
self.removeAlias = function(Alias) {
$.each(self.people(), function() { this.Aliases.remove(Alias) })
};
self.save = function() {
self.lastSavedJson(JSON.stringify(ko.toJS(self.people), null, 2));
var subModel = JSON.stringify(ko.toJS(self));
$.ajax({
url: '/Request/PostRequest',
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: subModel,
success: function (status) {
alert(status);
}
});
};
self.lastSavedJson = ko.observable("")
};
ko.applyBindings(new BackgroundModel(initialData));
</script>
Обновленный вид
<div data-bind="foreach: people">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title"> <a data-bind="attr:{href:'#collapseOne'+$index()}, text:FirstName() +' '+ LastName()" @*href="#collapse_One"*@ class="accordion-toggle" data-toggle="collapse" data-parent="#accordion"></a> </h4>
</div>
<div data-bind="attr:{id:'collapseOne'+$index()}" class="panel-collapse collapse in" @*data-bind="attr:{id:_collapseId, class:_collapsedIn}"*@>
<div class="panel-body">
<table>
<tbody>
<tr>
<td>@Html.DisplayNameFor(x => x.Person.FirstName)</td>
<td>@Html.DisplayNameFor(x => x.Person.LastName)</td>
</tr>
<tr>
<td><input data-bind="value: FirstName, name: FirstName" /></td>
<td><input data-bind="value: LastName, name: LastName" /></td>
</tr>
<tr>
<td><a href='#' data-bind='click: $root.removePerson'>Remove Person</a></td>
</tr>
</tbody>
</table>
<!-- ko foreach: Aliases -->
<table>
<tbody>
<tr>
<td>@Html.DisplayNameFor(x => x.Alias.FirstNameAlias)</td>
<td>@Html.DisplayNameFor(x => x.Alias.LastNameAlias)</td>
</tr>
<tr>
<td><input data-bind="value: FirstNameAlias, name: FirstNameAlias" /></td>
<td><input data-bind="value: LastNameAlias, name: LastNameAlias" /></td>
</tr>
<tr>
<td><a href='#' data-bind='click: $root.removeAlias'>Remove Alias</a></td>
</tr>
</tbody>
</table>
<!-- /ko -->
<a href='#' data-bind='click: $root.addAlias'>Add Alias</a>
</div>
</div>
</div>
</div>
1 ответ
Переменная model
в вашем представлении функция будет undefined
это не та модель, которую вы ожидаете.
Переместите метод отправки в в вашей модели...
$(function () {
var personItem = function () {
var self = this;
self.LastName = ko.observable();
self.FirstName = ko.observable();
self.Aliases = ko.observableArray();
};
var model = ko.mapping.fromJS(@Html.Raw(Model.ToJson()));
alert('The length of the array is ' + model.People().length);
alert('The first element is ' + model.People()[0].Aliases().length);
alert(ko.toJSON(model));
model.addPerson = function () {
model.People().push(new personItem());
};
model.removePerson = function (person) {
model.People().remove(person);
};
model.submit = function() {
$.ajax({
url: '/Request/PostRequest',
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: ko.toJSON(model),
success: function (status) {
alert(status);
}
});
};
ko.applyBindings(model);
});
и обновите привязку на вашей кнопке, чтобы
<input id="clickMe" type="button" value="clickme" data-bind="click: submit" />
Также
Controller.Json
ожидает, что объект сериализуется, а не строка
[HttpPost]
public JsonResult PostRequest(Record viewModel)
{
return Json(String.Format("'Success':'false','Error':'"));
}
Должно быть:
[HttpPost]
public JsonResult PostRequest(Record viewModel)
{
return Json(new { success = false, error: String.Empty });
}
Обновить
Также заметил еще одну проблему с обновлением массива model.People
model.addPerson = function () {
model.People.push(new personItem());
};
model.removePerson = function (person) {
model.People.remove(person);
};
В этих функциях model.People
должно быть model.People()
т.е.
model.addPerson = function () {
model.People().push(new personItem());
};
model.removePerson = function (person) {
model.People().remove(person);
};
Обратите внимание на ()
после модели. Люди - включены в блок кода выше.