See code (am using Knockout.js over ASP MVC 3):
@model List<Person>
@{
ViewBag.Title = "Home Page";
}
<table>
<thead><tr>
<th>First Name</th><th>Last Name</th><th>Age</th><th>Department</th> <th></th>
</tr></thead>
<tbody data-bind="foreach: people">
<tr>
<td><input data-bind="value: FirstName" /></td>
<td><input data-bind="value: LastName"></select></td>
<td><input data-bind="value: Age"></select></td>
<td><select></select></td>
<td><a href="#" data-bind="click: $root.removePerson">Remove</a></td>
</tr>
</tbody>
<tfoot>
<tr> <th align=left colspan=4>Total number of people:</th> <th><span data-bind="text: $root.people().length"></span></th> </tr>
</tfoot>
</table>
<button data-bind="click: addPerson, enable: people().length < 5">Add another person</button>
<script type="text/javascript">
function ViewModel() {
var self = this;
self.people = ko.observableArray(ko.mapping.fromJS(@Html.Raw(new JavaScriptSerializer().Serialize(Model))));
self.addPerson = function() {
self.people.push(@Html.Raw(new JavaScriptSerializer().Serialize(new Person() { FirstName = "I am", LastName = "a new Person", Age = "0" })));
}
self.removePerson = function(person)
{
self.people.remove(person)
}
}
ko.applyBindings(new ViewModel());
</script>
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Age { get; set; }
public string FullName
{
get { return FirstName + " " + LastName; }
}
}
My questions is why does people().length return 0?
(Why can’t I remove an item from the array using my function removePerson?) ->solved
EDIT:
Here’s the generated HTML
Home Page
<script src="/Scripts/knockout.js" type="text/javascript"></script>
<script src="/Scripts/knockout.mapping-latest.js" type="text/javascript"></script>
</head>
<body>
<div class="page">
<div id="header">
<div id="title">
<h1>
My MVC Application</h1>
</div>
<div id="logindisplay">
[ <a href="/Account/LogOn">Log On</a> ]
</div>
<div id="menucontainer">
<ul id="menu">
<li><a href="/">Home</a></li>
<li><a href="/Home/About">About</a></li>
</ul>
</div>
</div>
<div id="main">
<table>
<thead><tr>
<th>First Name</th><th>Last Name</th><th>Age</th><th>Department</th> <th></th>
</tr></thead>
<tbody data-bind="foreach: people">
<tr>
<td><input data-bind="value: FirstName" /></td>
<td><input data-bind="value: LastName"></select></td>
<td><input data-bind="value: Age"></select></td>
<td><select></select></td>
<td><a href="#" data-bind="click: $root.removePerson">Remove</a></td>
</tr>
</tbody>
<tfoot>
<tr> <th align=left colspan=4>Total number of people:</th> <th><span data-bind="text: $root.people().length"></span></th> </tr>
</tfoot>
</table>
<button data-bind="click: addPerson, enable: people().length < 5">Add another person</button>
<script type="text/javascript">
function ViewModel() {
var self = this;
self.people = ko.observableArray(ko.mapping.fromJS([{"FirstName":"Basti","LastName":"Wuf","Age":"28","FullName":"Basti Wuf"},{"FirstName":"Mawie","LastName":"Mew","Age":"25","FullName":"Mawie Mew"}]));
self.addPerson = function() {
self.people.push({"FirstName":"I am","LastName":"a new Person","Age":"0","FullName":"I am a new Person"});
}
self.removePerson = function(person)
{
//alert(person.FirstName);
self.people.remove(person)
}
}
ko.applyBindings(new ViewModel());
</script>
</div>
<div id="footer">
</div>
</div>
</body>
</html>
ko.mapping.fromJSwhen given an array turns it into an observableArray.So, when you do:
self.people = ko.observableArray(ko.mapping.fromJS([{"FirstName":"Basti","LastName":"Wuf","Age":"28","FullName":"Basti Wuf"},{"FirstName":"Mawie","LastName":"Mew","Age":"25","FullName":"Mawie Mew"}]));self.people will wrapped twice (observableArray contains an observableArray).
So,
self.people()would return the inner observableArray. Doing alengthon it (a function) will return the number of arguments defined for the function (0 in this case).Basically, you can just remove the outer
ko.observableArrayin the view model initialization code and let the mapping plugin do it for you.