Gift list

Live example

You have asked for gift(s)

Gift name: Price: $

Saved at

Model

public class GiftListModel
{
    public List<GiftModel> Gifts { get; set; }
    public string ServerTime { get; set; }

    public void AddGift()
    {
        Gifts.Add(new GiftModel());
    }

    public void RemoveGift(int index)
    {
        Gifts.RemoveAt(index);
    }

    public void Save()
    {
        ServerTime = DateTime.Now.ToString();
    }
}

public class GiftModel
{
    public string Title { get; set; }
    public double Price { get; set; }
}

Razor

@using PerpetuumSoft.Knockout
@model KnockoutMvcDemo.Models.GiftListModel
@{
  var ko = Html.CreateKnockoutContext();
}
<p>You have asked for @ko.Html.Span(m => m.Gifts.Count) gift(s)</p>
<form id="myform">
  <table>
    <tbody>
      @using (var items = ko.Foreach(m => m.Gifts))
      {
        <tr>
          <td>Gift name:@items.Html.TextBox(item => item.Title, new { @class = "required" }).UniqueName()</td>
          <td>Price: $@items.Html.TextBox(item => item.Price, new { @class = "required number" }).UniqueName()</td>
          <td>@ko.Html.Button("Delete", "RemoveGift", "GiftList", new { index = items.GetIndex() })</td>
        </tr>
      }
    </tbody>
  </table>

  @ko.Html.Button("Add", "AddGift", "GiftList")
  <button @ko.Bind.Enable(m => m.Gifts.Count > 0) type="submit">Save</button>
</form>
@using (ko.If(m => m.ServerTime.Length > 0))
{
  <p>Saved at @ko.Html.Span(m => m.ServerTime)</p>
}
<script type="text/javascript">
  $("#myform").ajaxForm();
  $("#myform").validate({ submitHandler: function () { @ko.ServerAction("Save", "GiftList"); } });
</script>
<style scoped="scoped">
  input.error {
    border: 1px solid red;
    background-color: #FDC;
  }

  label.error {
    display: block;
    color: Red;
    font-size: 0.8em;
  }
</style>

@ko.Apply(Model)

Controller

public class GiftListController : BaseController
{
    public ActionResult Index()
    {
        InitializeViewBag("Gift list");
        var model = new GiftListModel
        {
            Gifts = new List<GiftModel>
        {
          new GiftModel {Title = "Tall Hat", Price = 49.95},
          new GiftModel {Title = "Long Cloak", Price = 78.25}
        }
        };
        return View(model);
    }

    public ActionResult AddGift(GiftListModel model)
    {
        model.AddGift();
        return Json(model);
    }

    public ActionResult RemoveGift(GiftListModel model, int index)
    {
        model.RemoveGift(index);
        return Json(model);
    }

    public ActionResult Save(GiftListModel model)
    {
        model.Save();
        return Json(model);
    }
}

Html (autogenerated)

<p>You have asked for <span data-bind="text : Gifts().length"></span> gift(s)</p>
<form id="myform">
  <table>
    <tbody>
<!-- ko foreach: Gifts -->
        <tr>
          <td>Gift name:<input class="required" data-bind="value : $data.Title,uniqueName : true" name="Title" type="text" /></td>
          <td>Price: $<input class="required number" data-bind="value : $data.Price,uniqueName : true" type="text" /></td>
          <td><button data-bind="click : function() {executeOnServer(viewModel, &#39;/GiftList/RemoveGift?index=&#39;+$index()+&#39;&#39;);}">Delete</button></td>
        </tr>
<!-- /ko -->
    </tbody>
  </table>

  <button data-bind="click : function() {executeOnServer(viewModel, &#39;/GiftList/AddGift&#39;);}">Add</button>
  <button data-bind="enable : (Gifts().length > 0)" type="submit">Save</button>
</form>
<!-- ko if: (ServerTime().length > 0) -->
  <p>Saved at <span data-bind="text : ServerTime"></span></p>
<!-- /ko -->
<script type="text/javascript">
  $("#myform").ajaxForm();
  $("#myform").validate({ submitHandler: function () { executeOnServer(viewModel, '/GiftList/Save'); } });
</script>
<style scoped="scoped">
  input.error {
    border: 1px solid red;
    background-color: #FDC;
  }

  label.error {
    display: block;
    color: Red;
    font-size: 0.8em;
  }
</style>

<script type="text/javascript"> 
var viewModelJs = {"Gifts":[{"Title":"Tall Hat","Price":49.95},{"Title":"Long Cloak","Price":78.25}],"ServerTime":""};
var viewModel = ko.mapping.fromJS(viewModelJs); 
ko.applyBindings(viewModel);
</script>