Contacts editor
Description
Live example
Add a contact
Save to JSON
Model
public class ContactsEditorPhoneModel
{
public string Type { get; set; }
public string Number { get; set; }
}
public class ContactsEditorContactModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public List<ContactsEditorPhoneModel> Phones { get; set; }
public void AddPhone()
{
Phones.Add(new ContactsEditorPhoneModel());
}
public void DeletePhone(int phoneIndex)
{
if (phoneIndex >= 0 && phoneIndex < Phones.Count)
Phones.RemoveAt(phoneIndex);
}
}
public class ContactsEditorModel
{
public List<ContactsEditorContactModel> Contacts { get; set; }
public string LastSavedJson { get; set; }
public void AddContact()
{
Contacts.Add(new ContactsEditorContactModel());
}
public void DeleteContact(int contactIndex)
{
if (contactIndex >= 0 && contactIndex < Contacts.Count)
Contacts.RemoveAt(contactIndex);
}
public void AddPhone(int contactIndex)
{
if (contactIndex >= 0 && contactIndex < Contacts.Count)
Contacts[contactIndex].AddPhone();
}
public void DeletePhone(int personIndex, int phoneIndex)
{
if (personIndex >= 0 && personIndex < Contacts.Count)
Contacts[personIndex].DeletePhone(phoneIndex);
}
public void SaveJson()
{
LastSavedJson = new JavaScriptSerializer().Serialize(this);
}
}
Razor
@using PerpetuumSoft.Knockout
@model KnockoutMvcDemo.Models.ContactsEditorModel
@{
var ko = Html.CreateKnockoutContext();
}
<div>
<table>
<tr>
<th>First name</th>
<th>Last name</th>
<th>Phone numbers</th>
</tr>
<tbody>
@using (var contacts = ko.Foreach(m => m.Contacts))
{
<tr>
<td style="vertical-align: top">
@contacts.Html.TextBox(m => m.FirstName)
<div>@contacts.Html.HyperlinkButton("Delete", "DeleteContact", "ContactsEditor", new { contactIndex = contacts.GetIndex() })</div>
</td>
<td style="vertical-align: top">
@contacts.Html.TextBox(m => m.LastName)
</td>
<td>
<table>
<tbody>
@using (var phones = contacts.Foreach(m => m.Phones))
{
<tr>
<td>@phones.Html.TextBox(m => m.Type)
</td>
<td>@phones.Html.TextBox(m => m.Number)
</td>
<td>@ko.Html.HyperlinkButton("Delete", "DeletePhone", "ContactsEditor", new { contactIndex = contacts.GetIndex(), phoneIndex = phones.GetIndex() })
</td>
</tr>
}
</tbody>
</table>
@ko.Html.HyperlinkButton("Add number", "AddPhone", "ContactsEditor", new { contactIndex = contacts.GetIndex() })
</td>
</tr>
}
</tbody>
</table>
</div>
<p>
@ko.Html.Button("Add a contact", "AddContact", "ContactsEditor")
@ko.Html.Button("Save to JSON", "SaveJson", "ContactsEditor").Enable(m => m.Contacts.Count > 0)
</p>
@ko.Html.TextArea(m => m.LastSavedJson, new { rows = 5, cols = 50 })
<style scoped="scoped">
input {
width: 120px;
}
textarea {
width: 500px;
}
</style>
@ko.Apply(Model)
Controller
public class ContactsEditorController : BaseController
{
public ActionResult Index()
{
InitializeViewBag("Contacts editor");
var model = new ContactsEditorModel();
model.Contacts = new List<ContactsEditorContactModel>();
model.Contacts.Add(new ContactsEditorContactModel
{
FirstName = "Danny",
LastName = "LasRusso",
Phones = new List<ContactsEditorPhoneModel>
{
new ContactsEditorPhoneModel {Type = "Mobile", Number = "(555) 121-2121"},
new ContactsEditorPhoneModel {Type = "Home", Number = "(555) 123-4567"},
}
});
model.Contacts.Add(new ContactsEditorContactModel
{
FirstName = "Sensei",
LastName = "Miyagi",
Phones = new List<ContactsEditorPhoneModel>
{
new ContactsEditorPhoneModel {Type = "Mobile", Number = "(555) 444-2222"},
new ContactsEditorPhoneModel {Type = "Home", Number = "(555) 999-1212"},
}
});
return View(model);
}
public ActionResult AddContact(ContactsEditorModel model)
{
model.AddContact();
return Json(model);
}
public ActionResult DeleteContact(ContactsEditorModel model, int contactIndex)
{
model.DeleteContact(contactIndex);
return Json(model);
}
public ActionResult AddPhone(ContactsEditorModel model, int contactIndex)
{
model.AddPhone(contactIndex);
return Json(model);
}
public ActionResult DeletePhone(ContactsEditorModel model, int contactIndex, int phoneIndex)
{
model.DeletePhone(contactIndex, phoneIndex);
return Json(model);
}
public ActionResult SaveJson(ContactsEditorModel model)
{
model.SaveJson();
return Json(model);
}
}
Html (autogenerated)
<div>
<table>
<tr>
<th>First name</th>
<th>Last name</th>
<th>Phone numbers</th>
</tr>
<tbody>
<!-- ko foreach: Contacts -->
<tr>
<td style="vertical-align: top">
<input data-bind="value : FirstName" />
<div><a data-bind="click : function() {executeOnServer(viewModel, '/ContactsEditor/DeleteContact?contactIndex='+$index()+'');}" href="#">Delete</a></div>
</td>
<td style="vertical-align: top">
<input data-bind="value : LastName" />
</td>
<td>
<table>
<tbody>
<!-- ko foreach: Phones -->
<tr>
<td><input data-bind="value : Type" />
</td>
<td><input data-bind="value : Number" />
</td>
<td><a data-bind="click : function() {executeOnServer(viewModel, '/ContactsEditor/DeletePhone?contactIndex='+$parentContext.$index()+'&phoneIndex='+$index()+'');}" href="#">Delete</a>
</td>
</tr>
<!-- /ko -->
</tbody>
</table>
<a data-bind="click : function() {executeOnServer(viewModel, '/ContactsEditor/AddPhone?contactIndex='+$index()+'');}" href="#">Add number</a>
</td>
</tr>
<!-- /ko -->
</tbody>
</table>
</div>
<p>
<button data-bind="click : function() {executeOnServer(viewModel, '/ContactsEditor/AddContact');}">Add a contact</button>
<button data-bind="click : function() {executeOnServer(viewModel, '/ContactsEditor/SaveJson');},enable : (Contacts().length > 0)">Save to JSON</button>
</p>
<textarea cols="50" data-bind="value : LastSavedJson" rows="5"></textarea>
<style scoped="scoped">
input {
width: 120px;
}
textarea {
width: 500px;
}
</style>
<script type="text/javascript">
var viewModelJs = {"Contacts":[{"FirstName":"Danny","LastName":"LasRusso","Phones":[{"Type":"Mobile","Number":"(555) 121-2121"},{"Type":"Home","Number":"(555) 123-4567"}]},{"FirstName":"Sensei","LastName":"Miyagi","Phones":[{"Type":"Mobile","Number":"(555) 444-2222"},{"Type":"Home","Number":"(555) 999-1212"}]}],"LastSavedJson":""};
var viewModel = ko.mapping.fromJS(viewModelJs);
ko.applyBindings(viewModel);
</script>