Inner computed properties

Description

The sample demonstrates that computed properties can be used not only for the main model, but also for its sub-models.

Live example

FirstName LastName FullName

Model

public class InnerComputedItemModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    [Computed]
    [ScriptIgnore]
    [JsonIgnore]
    public string FullName
    {
        get { return FirstName + " " + LastName; }
    }
}

public class InnerComputedSubModel
{
    public string Caption { get; set; }
    public int Value { get; set; }

    [Computed]
    [ScriptIgnore]
    [JsonIgnore]
    public string Message
    {
        get { return Caption + " = " + Value; }
    }
}

public class InnerComputedModel
{
    public List<InnerComputedItemModel> Items { get; set; }
    public InnerComputedSubModel SubModel { get; set; }
}

Razor

@using PerpetuumSoft.Knockout
@model KnockoutMvcDemo.Models.InnerComputedModel
@{
  var ko = Html.CreateKnockoutContext();
}
<table>
  <tr>
    <th>FirstName</th>
    <th>LastName</th>
    <th>FullName</th>
  </tr>
  @using (var items = ko.Foreach(m => m.Items))
  {
    <tr>
      <td>
        @items.Html.Span(m => m.FirstName)
      </td>
      <td>
        @items.Html.Span(m => m.LastName)
      </td>
      <td>
        @items.Html.Span(m => m.FullName)
      </td>
    </tr>
  }
</table>

@using (var subModel = ko.With(m => m.SubModel))
{
  @ko.Html.Span(m => subModel.Model.Message)
}

@ko.Apply(Model)

Controller

public class InnerComputedController : BaseController
{
    public ActionResult Index()
    {
        InitializeViewBag("Inner computed properties");
        var model = new InnerComputedModel
          {
              Items = new List<InnerComputedItemModel>
        {
          new InnerComputedItemModel {FirstName = "Annabelle", LastName = "Arnie"},
          new InnerComputedItemModel {FirstName = "Bertie", LastName = "Brianna"},
          new InnerComputedItemModel {FirstName = "Charles", LastName = "Cayenne"},
        },
              SubModel = new InnerComputedSubModel
                {
                    Caption = "Count",
                    Value = 3
                }
          };
        return View(model);
    }
}

Html (autogenerated)

<table>
  <tr>
    <th>FirstName</th>
    <th>LastName</th>
    <th>FullName</th>
  </tr>
<!-- ko foreach: Items -->
    <tr>
      <td>
        <span data-bind="text : $data.FirstName"></span>
      </td>
      <td>
        <span data-bind="text : $data.LastName"></span>
      </td>
      <td>
        <span data-bind="text : $data.FullName"></span>
      </td>
    </tr>
<!-- /ko -->
</table>

<!-- ko with: SubModel -->
<span data-bind="text : $data.Message"></span><!-- /ko -->

<script type="text/javascript"> 
var viewModelJs = {"Items":[{"FirstName":"Annabelle","LastName":"Arnie"},{"FirstName":"Bertie","LastName":"Brianna"},{"FirstName":"Charles","LastName":"Cayenne"}],"SubModel":{"Caption":"Count","Value":3}};
var viewModelMappingData = {
'Items': { create: function(options) {
var data = ko.mapping.fromJS(options.data);
data.FullName = ko.computed(function() { try { return ((this.FirstName() + ' ') + this.LastName())} catch(e) { return null; }  ;}, data);
return data;
}}
};
var viewModel = ko.mapping.fromJS(viewModelJs, viewModelMappingData); 
viewModel.SubModel.Message = ko.computed(function() { try { return ((this.Caption() + ' = ') + this.Value())} catch(e) { return null; }  ;}, viewModel.SubModel);
ko.applyBindings(viewModel);
</script>