Oct 28 2011

Handling Big Amounts of Data with Client-Side Templates 2

Category: Asp.net | MVCFrancesco @ 23:42

Low BandWidth Transfers with The Client Side Templates of the Mvc Controls Toolkit

Handling Big Amounts of Data with Client-Side Templates

In my last post about Client-Side templates I showed how to show a big amount of products in a View and let the user add some of them to a chart with the help of Client-Side templates. The main issue there, was mainly, the optimization of the View organization.

Here, I will show how the addition of some parameters to the template binding may further improve performance, and how to improve the general look of the View with the help of other kind of bindings such as the CSS binding, and with the help of some extensions of the _D helper of the Mvc Controls Toolkit.

First of all we notice that the productViewTemplate used to show the list of all products doesn’t contain any input filed or any input server control. Moreover, it doesn’t contain any javascript or server control(that rely on javascript), but it just contains “holes” to be filled when the template is instantiated. We can say all this to the template engine so that it can avoid to do high computational cost operations when the template is instantiated. This can be done by simply specifying two optional parameters of our repeater:

new { id = "ProductsContainer" }, fastNoInput:true, fastNoJavaScript:true)

This way the template is not parsed looking for input validation or input bindings, and it is not scanned to collect JavaScript code to be executed. As default the value of this extra parameter is false. There is also an applyClientValidation parameter whose default value is true that can be used when there are input fields but we don’t want validate them on the client side.

As a further improvement we can show the price of each product, …but this is easy ..just adding another property to our model and another _D helper to show it in our template.

What if we want to show some promotional images saying a product has a discount?  Suppose we have 3 possible situations: no discount, 25% discount and 35% discount. We can define an enumeration with this three possible values:

public enum discount { none, d25, d35 };

This way we can take advantage of the possibility to render enumerations with images or strings introduced since the 1.1 RC version of the Mvc Controls Toolkit.

As a first step we have to declare two vectors: the first one containg all image urls, and the second one containing all strings to use as values for the alt attribute of the images:

Html.DeclareStringArray(new string[] { "", Url.Content("~/Content/discount25.png"), Url.Content("~/Content/discount35.png") }, "discountUrls");
Html.DeclareStringArray(new string[] {"", "25%", "35%"}, "discountAlts");

Once we have declared this two arrays and we have given them names, it is enough to render the enumeration property with  the _D helper passing it the array names as parameters. Below the full code of the clint side template definition:

<div id="ProductsToList">
    <h2>Products List</h2>
    <br /><br /><br />
    @using (Html.BeginForm("Index", "Shopping"))
    {
    
        <div id="automaticLoad" class="ToScroll">
        <div id="automaticLoadContent">
                  @Html.ValidationSummary(false)
                  <table >
                  <thead>
                      <tr>
                      <td><strong>Product</strong></td>
                      <td><strong>Price</strong></td>
                      <td><strong>Discount</strong></td>
                      <td><strong>Select Product</strong></td>
                  
                      </tr>
                  </thead>
                  @{  Html.DeclareStringArray(new string[] { "", Url.Content("~/Content/discount25.png"), Url.Content("~/Content/discount35.png") }, "discountUrls");
                      Html.DeclareStringArray(new string[] { "", "25%", "35%" }, "discountAlts");}
                   @Html.ClientBlockRepeater(m => m.Products,
                       _S.H<ProductViewBase>(
                            @<tr>
                                    <td>
                                    @item._D(m => m.Description)
                                    </td>
                                    <td>
                                    @item._D(m => m.Price)
                                    </td>
                                    <td>@item._D(m => m.Discount, null, "discountAlts", "discountUrls")</td>
                                    <td><a href="#" data-bind="click: function() { productsClientView.buyProduct($data) }">Buy</a> </td>
                             </tr>
                        ),
                        ExternalContainerType.tbody,
                        new { id = "ProductsContainer" })
                  </table>

Once we have the product price we can add to the chart the total cost of each product(number of products*unit price) and the overall price of the purchase.

Each product price can be computed by using a Text binding where we, first perform the needed multiplication and then we format properly the price with the help of the Mvc Controls Toolkit JavaScript formatting function:

function MvcControlsToolkit_FormatDisplay(value, format, dataType, prefix, postfix, nullString)

The first parameter is the value to format, the second one a format string, the third one the data type, then we can add also a prefix and postfix to the string obtained this way, and we can also provide a string to be used when the value is null. In our case the data type is float and we want just 2 decimals, so the ‘n’ format string is ok. Moreover we need to add a ‘$’ postfix since each number is a price:

@{var totalPriceBinding = itemBindings.Text(
                                  m => m.Quantity,
                                  "MvcControlsToolkit_FormatDisplay({0}*{1}, 'n', MvcControlsToolkit_DataType_Float, '', '$', '')",
                                  itemBindings.L<decimal>(m => m.Price)).Get();}
                            <td><span data-bind="@totalPriceBinding"></span></td>

Since the binding has more than one parameter(Quantity and Price) we have to use the additional optional parameters provided by most of bindings. Since they are not strongly typed but they are defined as Expressions (not as Expression<Func<…..) Visual Studio IntelliSense is not available. To overcome this problem we revert to strongly typed Expressions with the help of the itemBindilgs.L method.

In order to display the total price of the purchase we add a table footer and use a text binding that calls the grandTotal() method of the client side ViewModel:

<tfoot>
              <tr>
                <td><strong>Total:</strong></td>
                <td><strong></strong></td>
                <td><strong></strong></td>
                <td><strong><span data-bind="text: MvcControlsToolkit_FormatDisplay(grandTotal(), 'n', MvcControlsToolkit_DataType_Float, '', '$', '')"></span></strong></td>
                <td></td>
              </tr>
              <tr>
              <td colspan="3"></td>
              <td><input type="button" value="Submit" data-bind='click: function(){if(confirmModel.Products().length>0) confirmModel.saveAndSubmit();}'/></td>
              <td></td>
              </tr>
              </tfoot>

The table footer contains also the submit button that is enabled only when there is at least one product added to the chart. The definition of the grandTotal() method is quite immediate:

<script language='javascript' type='text/javascript'>
        confirmModel.grandTotal = ko.dependentObservable(function () {
            var total = 0;
            for (var i = 0; i < this.Products().length; i++) {
                total += this.Products()[i].Quantity() * this.Products()[i].Price();
            }
            return total;
        },
        confirmModel);
    </script>

As you can see the grandTotal member is defined as a dependent observable so that the total is automatically updated when something changes.

Finally, the whole chart might be invisible when empty. This can be easily achieved attaching a CSS binding to the table tag:

var aPurchase = confirmModel.CSS("hide", m => m.Products, "{0}.length==0").Get();

and then:

<table data-bind = "@aPurchase">

That’s All! Download all code of the whole client-template set of tutorials from the Mvc Controls Toolkit download page. The name of the file is RazorClientTemplating.

                                                                           Stay Tuned!

                                                                           Francesco

Tags: , , , ,

Comments