Oct 4 2017

Building Web applications with Knockout.js and ASP.NET core

Category: Asp.net | Asp.net core | MVC | TypeScript | WebApi | JavaScriptFrancesco @ 05:40

Amongst all the client side frameworks backed by big companies, React.js and Angular.js appear to be the most popular. However, Knockout.js still maintains a good market share, thanks to its interesting peculiarities.

Knockout is based on an MVVM paradigm similar to Angular.js, but unlike React.js. While it is adequate for modular complex applications, at the same time, it is very simple to mix with server side templating, similar to React.js, but unlike Angular.js….Read full article

Contact us if you want to learn more

aspnet-core-running-spa

Tags: , ,

Mar 20 2013

Single Page Applications 3: TreeIterator

Category: Asp.net | MVCFrancesco @ 23:08

Data Moving Plugin Controls

Data Moving Plugin Styling

Data Moving Plugin Forms: Detail Views, Data Filtering UI, and Undo/Redo Stack

Single Page Applications 1: Manipulating the Client Side ViewModel

Single Page Applications 2: Validation Error Handling

Single Page Applications 3: TreeIterator

Client templates instantiation is one of the techniques used to implement SPA applications: The initial page contains just a minimum Html, and the remainder of the Html is created dynamically by instantiating client templates on data items. Typically, template instantiations are triggered by actions executed by javascript code. The Data Moving Plug-in offers a simple way to create dynamically all Html of a SPA, in a declarative data-driven fashion. The TreeIterator helper scans a hierarchical data structure and decides  which template to use for each data element it meets during this processing. The developer is required to provide just a list of templates, and a function that selects a template given a data item. After that, the provided templates are used as building blocks to produce the whole UI of the page (or of a part of the page) as soon as a new client ViewModel(or a new part of it, such as a Workspace) is sent to the client side.

Since the Mvc Controls Toolkit, and consequently the Data Moving Plug-in that is built on top of it, are able to compile any Razor template into a client side template, we can put everything in the templates provided to the TreeIterator: paged grids and/or complex controls such as Dual selects , or whole Data Moving Forms.

We will see how the TreeIterator works with a simple example: automatically building a multipage JQuery mobile menu based on nested lists, from the information contained in a hierarchy of objects. We will use the Mvc Controls Toolkit SimpleMenuItem class to define each menu item:

  1. public class SimpleMenuItem
  2. {
  3.     public SimpleMenuItem();
  4.  
  5.     public List<SimpleMenuItem> Children { get; set; }
  6.     public string Link { get; set; }
  7.     public string Target { get; set; }
  8.     public string Text { get; set; }
  9.     public string UniqueName { get; set; }
  10. }

 

As first step we declare all needed templates:

  1. var menuTemplates = h.DeclareClientTemplates("mobile_menu")
  2.                 .Add<SimpleMenuItem>(
  3.                 @<ul data-role="listview" id="menuRoot">
  4.                         @item.RecurIterationOn(m => m.Children)
  5.                               </ul>
  6.                 )
  7.                 .Add<SimpleMenuItem>(
  8.                 @<li >
  9.                         @item._D(m => m.Text)           
  10.                 </li>
  11.                 )
  12.                 .Add<SimpleMenuItem>(
  13.                 @<li >
  14.                     @item.LinkFor(m => m.Text, m => m.Link)            
  15.                 </li>
  16.                 )
  17.                 .Add<SimpleMenuItem>(
  18.                 @<li >
  19.                     @item.LinkFor(m => m.Text, m => m.Link, target: m => m.Target)          
  20.                 </li>
  21.                 )
  22.                 .Add<SimpleMenuItem>(
  23.                 @<li >
  24.                     @{var mitem = item.ViewData.Model;}
  25.                     @item._D(m => m.Text)
  26.                     <ul data-role="listview">
  27.                         @item.RecurIterationOn(m => m.Children)
  28.                                     </ul>
  29.                 </li>
  30.                 );

This task is easily accomplished with the fluent interface of the DeclareClientTemplates helper. The unique argument of this helper is the templates base name. All templates are named by adding an integer suffix to this base name: the firts template is named “mobile_menu0”, the second one “mobile_menu1”, and so on.

Our first template defines the startup template, that is, the initial template that starts the rendering. The startup template is not necessarily the first one, but the startup template is declared in the call to the TreeIterator helper.

Then we have the template to use for displaying pure text, then the template of a menu item that links to a target page, then then the template of a menu item that links to a target page to be opened in a new browser window, and finally the template to be used for a menu item that has sub-menu items.

As you can see both the startup template and the last template call RecurIteration on the children of the current SimpleMenuItem. This is the way the TreeIterator visits recursively, the whole data structure. In general, each, template may contains several calls to RecurIteration if the object has several children arrays.

Once we have defined all templates we need just to render them:

  1. @menuTemplates.Render("menuTemplateChoice")

The single argument of the Render method is the name of the javascript function that selects the template to use for each data item:

  1. function menuTemplateChoice(item) {
  2.     if ((!item.Children()) || item.Children().length == 0) {
  3.         if (item.Link()) {
  4.             return item.Target() ? 'mobile_menu3' : 'mobile_menu2';
  5.         }
  6.         else return 'mobile_menu1'
  7.     }
  8.     else return 'mobile_menu4';
  9. }

The above code is self-explanatory.

Finally, we may call the TreeIterator helper that "will do the job”:

  1. @h.ClientTreeIteratorFor(m => m, "mobile_menu0", "listviewAfterRender")

The first argument is the root of the objects hierarchy, the second argument is the name of the startup template, and finally the third argument is a javascript function to be called after the whole objects hierarchy has been rendered. In our case the listviewAfterRender javascript function enhances the <ul> <li> nested list created by the ClientTreeIteratorFor transforming them into a JQuery mobile nested listview:

  1. function listviewAfterRender() {
  2.         $('#menuRoot').hide();
  3.         setTimeout(function () { $('#menuRoot').listview(); $('#menuRoot').show(); });
  4.     }

The nice part, is that as soon as we substitute the root of the objects hierarchy with a different data item the UI of the menu is changed immediately to reflect the new settings.

In this example all templates were pre-rendered in the host Html page, however, in more complex multi-page applications templates may be organized in modules and loaded dynamically together with AMD javascript modules.

The video below shows the menu working:

 

That’ all for now!

Stay tuned and give a look also to all other Data Moving Plug-in introductory tutorials and videos

                      Francesco

Tags: , , , , , ,

Mar 19 2013

Single Page Applications 2: Validation Error Handling

Category: MVC | Asp.netFrancesco @ 22:30

Data Moving Plugin Controls

Data Moving Plugin Styling

Data Moving Plugin Forms: Detail Views, Data Filtering UI, and Undo/Redo Stack

Single Page Applications 1: Manipulating the Client Side ViewModel

Single Page Applications 2: Validation Error Handling

Single Page Applications 3: TreeIterator

This is the second half of the tutorial Single Page Applications 1: Manipulating the Client Side ViewModel that describes the enhancements added to the standard asp.net Mvc validation error engine by the Data Moving Plug-in. We advice to assist the video associated to this introductory tutorial before reading it:

In the Data Moving Plug-in validation rules, and error handling are coded with the usual tools of Asp.net Mvc, such as Data Annotations (or Fluent validation), by adding errors to the ModelState, etc. Also client side validation is based on the jquery validation plug-in. However, all this tools have been enhanced in several ways. First of all, server errors are returned also as a result of JSon updates, and dispatched to the right places in the UI (thanks to the updatesManagers that handle the updates). Moreover, also elements that are detached from the Dom, and rendered again by instantiating  client templates “remember” their error state thanks to parallel data structures that takes care of this “job”.

Probably, the more interesting enhancement is “error bubbling”. What is “error bubbling”? Suppose you need to report an error to the property AllProgrammers[1].EMail, that is to the Email of the second programmer of the programmers list. Well, since the grid has no visible Email column, the error cant be shown! In order to overcome this problem the error is bubbled up to the whole entity level:  AllProgrammers[1]. Now since we added an error column to the programmers grid the user can see the second programmer has an error:

  1. .AddErrorColumn(m => m, "*", width: 20)

Since we put m => m the error column is triggered by whole entity level errors. This way also errors in fields that are not shown in a grid row can be signalled to the user. Error bubbling is useful also in TreeViews/TreeGrids because an error happening in a child entity is signalled also in its father entity, so the user may open the right branch of the tree to find the children entity in error.

Error bubbling is automatically applied to both the errors returned by the server and to client side validation errors.

What if the user want to know exactly which among the fields that are not shown in the grid is in error in order to correct the error? Simple! He just shows the entity in error in a detail view, and thanks to the errors synchronization behavior with detail views provided by the data moving plug-in he will see the error in the detail view!

Another interesting feature is Error Filtering. All Data Moving plug-in controls that have been enhanced with a retrievalManager, have error filtering capabilities. This means, they respond to a “show just entity in error” filtering command that can be issued with a button in a toolbar. This way the user can review all errors by browsing the list of all entities in error.

Everything works properly because the Data Moving engine “remembers” the error state of a field also if the entity the field in error belongs to is removed from the Dom, for instance, because of paging.

In order to minimize the round-trips to the server the Data Moving Plug-in offers the possibility to add entity level validation rules, and whole Core Workspace validation rules directly on the client side.

Below the addition of an entity level validation rule to the programmers updatesManager:

 

  1. TeamBuilding.programmersUpdater.options({ itemErrors: function (x) {
  2.         if (x.CanBeTeamLeader() && (!x.Experience() || x.Experience() < 7))
  3.             return ["A 7 years experience is required to be a Team Leader"];
  4.         else return null;
  5.     }
  6.     });

Validation errors are specified through a function that receives an entity as argument and returns an array of errors. Error dispatching in the adequate place of the UI is done automatically by the Data Moving plug-in engine.

Below the definition of a Core Workspace level validation rule in the whole Workspace updatesManager:

  1. TeamBuilding.ClientModel, "ProposedTeam", TeamBuilding.DestinationModel, "ProposedTeam",
  2. { updatersIndices: [TeamBuilding.programmersUpdater, TeamBuilding.artistsUpdater],
  3.     {
  4.     ......
  5.     ......
  6.     Errors: function (model) {
  7.         if (model.LeaderProgrammer() && model.LeaderArtist()) return null;
  8.         else return ["The team is incomplete"];
  9.     }
  10. });

We can also specify an error callback that is invoked with a data structure containing information on the errors as argument whenever there are errors in the Workspace :

  1. onObjectErrors: function (errorObjects) {
  2.                  alert("Click the error button to show errors that are not visible on the screen");
  3.              }

That’ all for now!

Stay tuned and give a look also to all other Data Moving Plug-in introductory tutorials and videos

                      Francesco

Tags: , , , , , , ,

Mar 18 2013

Single Page Applications 1: Manipulating the Client Side ViewModel

Category: Asp.net | MVC | WebApiFrancesco @ 21:46

Data Moving Plugin Controls

Data Moving Plugin Styling

Data Moving Plugin Forms: Detail Views, Data Filtering UI, and Undo/Redo Stack

Single Page Applications 1: Manipulating the Client Side ViewModel

Single Page Applications 2: Validation Error Handling

Single Page Applications 3: TreeIterator

This is the first of 3 introductory tutorials about the features for handling Single Page Applications offered by The Data Moving Plugin. The Data Moving Plugin is in its RC stage and will be released to the market in about one month.

Firs of all just a little bit theory….then we will move to a practical example. If you want, you may give a look to the video that shows the example working before reading about the theory:

See this Video in High Resolution on the Data Moving Plug-in Site

 

Typically an SPA application allows the user to perform several tasks without leaving its physical Html page. This is result may be achieved by defining several “virtual pages” inside the same physical page. During the whole lifetime of the application just an active virtual page is visible, all other pages are either hidden or completely out of the Dom because they are created dynamically by instantiating client templates. The active page interacts with just a part of the Client Side ViewModel, and only that part is kept in synchrony with the server. 

One can use also different techniques to enable the user to perform several tasks without leaving the l Html physical page; in any case the client ViewModel may be partitioned into subparts that are the smallest “units” that may be sinchronized with the server independently of the remainder of the client side ViewModel. We call such elementary units Workspaces because they are the “data units”  manipulated by the user while he is performing one of the several tasks allowed by the SPA.

A workspace, in turn, is composed of two conceptually different sub-parts: a kind of short living data structure that is used just to carry out the current task and a set of long living data structures that are typically connected with a Database on the server side. Typically, on the client side, we don’t have all long living entities  of a “given type” but just a small window of the whole Entity Set. We call Entity Set Window, the set of all long-living entities of the same type stored in the Client Side ViewModel, and we call Core Workspace the part of the Workspace that remains after removing all Entity Set Windows.

Summing up, the Client side ViewModel is composed of partially overlapping Workspaces, that are in turn composed of a Core WorkSpace and several Entity Set Windows.

In general we can’t assume that all data of the Workspace are someway visible in the user interface. In fact the task being performed currently by the user may be composed of several steps (just think to the steps of a wizard), and substantially just the data “used” in the current step are visible to the user. Accordingly, each Workspace may be further split into partially overlapping UI Units, where each UI Unit is a part of the workspace that is “visible” in the user interface at a given time.

The concept of UI Unit is very important in error handling because, while all UI Units belonging to a Workspace must be submitted simultaneously to the server, only the errors that refer to the current UI Unit can be shown to the user.

The Data Moving Plug-in, offers tools to handle properly Entity Set Windows, Core Workspaces, and for handling properly UI Units during validation error processing:

  1. Retrieval Managers takes care of browsing Entity Sets in the Entity Set Windows, while updatesManagers take care of keeping the Entity Set Windows synchronized with the server by processing updates performed by the users to the Entity Set Windows, and by dispatching the principal key of newly created entities returned by the server.
  2. Whole WorkSpace updatesMangers take care of keeping a whole Workspace synchronized with the server, by automatically issuing commands to the updatesManagers of all Entity Set Windows contained in the Workspace and by taking care “personally” of the Core Workspace.
    The communication protocol between a whole Workspace updatesManager and the server includes the possibility for the server to issue “remote commands” that modify the Core Workspace on the client side. In fact, often, it is not possible for the server, to send a whole “updated” Core Workspace to the client that substitutes completely the old one, because the Core Workspace might have “links” with UI elements and with other Client Side ViewModel data, and a similar substitution would break them.
  3. The Data Moving Plug-in provides a powerful dom element-to-dom element data binding engine that enables the user to trigger “interactions” between dom elements,  and provides also a  Reference Knockout binding that maps UI elements to sub-parts of the Workspace, in such a way that the user “move” such parts of the Workspace by simply moving the UI elements that represent them in an intuitive way. The dom element-to-dom element data binding engine has been already described in a previous tutorial and in a previous video, so in this tutorial we will focus mainly on the Reference binding.
  4. Error Bubbling, Entities in Error Filtering, and other enhancements of the standard Asp.net Mvc validation engine help in associating errors to data that are not immediately visible on the screen. Error handling will be described in the second tutorial about SPA applications:  Single Page Applications 2: Validation Error Handling.

Let understand better how all this works with a simple example(the same shown in the video above).

Suppose we have a list of artists and a list of programmers, that are completely stored in the client side view model, and let suppose we would like to build a team to face a web project made of both artists and programmers. The team will have both a leader programmer and a leader artist, and not all people are entitled to cover the role of leader. Below a screen shot with an indication of the UI elements that represent data of the Core Workspace and data of the Entity Set Windows:

TeamBuilding

In the programmers tab there is another Entity Set Window containing Programmers Entities. Since we said all programmers and all artists are contained in the client ViewModel the Entity Set Windows  contain the whole Entity Sets. Moreover, since both the list of all programmers and the list of all artists are paged, not all all programmers and not artists belong to the current UI Unit; this means we will have difficulties in showing possible errors related to artists and programmers that are not in the current page.

As we can see in the video new people may be added to the Team being built by simply dragging them in the “Members” area. if a new Leader is selected, the old leader is automatically moved back in the original list. People that can cover the role of leader have a yellow border, and the two Leader areas accept only people entitled to cover the Leader role. Moreover, the artists area of the team accepts just artists while the programmers area of the team accepts just programmers.

The whole team building UI logics with the constraints listed above has been obtained without writing a single line of procedural code, but by just declaring reference bindings, Drag Sources, and Drop Targets.

For instance, below the definition of the leader programmer area:

  1.   <div id="leader_programmer"class='leader-container programmers ui-widget-content' data-bind="@bindings.Reference(m => m.ProposedTeam.LeaderProgrammer).Get()">
  2.     @ch._withEmpty(m => m.ProposedTeam.LeaderProgrammer, ExternalContainerType.div, existingTemplate: "ProgrammerTemplate0")
  3. </div>
  4. @Html.DropTarget("#leader_programmer", new List<string> { "LeaderProgrammer" }, rolesDropOptions)

The reference binding maps the div named “leader-programmer” with the property of the Core Workspace ProposedTeam.LeaderProgrammer, while the DropTarget declaration makes it accepts Drag Sources tagged as “LeaderProgrammer”. As a consequence of this two declarations when a UI element representing a programmer entitled to cover the role of leader (ie that has the “LeaderProgrammer” tag) is dragged over this are it is “accepted”, and the data item tied to the dragged UI element with another reference binding, is moved into the knockout observable of the ProposedTeam.LeaderProgrammer property. This in turn triggers the instantiation of the programmerTemplate0 client template because of the _withEmpty instruction that is an enhancement of the knockout with binding.

The ProgrammerTemplate0 templates is the client template automatically built by the grid on the left of the page that lists all programmers. As a consequence the chosen leader programmer is rendered in the “Leader Programmer” area with the same appearance he had in the grid. Each member area works in a similar way:

  1. <div class="members-container programmers ui-widget-content" id ="all_programmers" data-bind="@bindings.Reference(m => m.ProposedTeam.Programmers).Get()">
  2.     @ch._foreachEmpty(m => m.ProposedTeam.Programmers, ExternalContainerType.div, existingTemplate: "ProgrammerTemplate0")
  3. </div>
  4. @Html.DropTarget("#all_programmers", new List<string> { "Programmer" }, rolesDropOptions)

However in this case the ProposedTeam.Programmers property used in the reference binding is an observable array, so the dragged element is pushed into this array. Instead of the _withempty, we have a _foreachEmpty that is an enhancement of knockout foreach binding.

 

To makes everything works properly all programmers must be declared as Drag Sources tagged with “Programmer”. Moreover, all programmers entitled to cover the role of leader must have also the “LeaderProgrammer” tag:

  1. @Html.DragSourceItems(".programmers", ".simple-list-item", new List<string> { "Programmer" }, new DataDragOptions { DestroyOriginal = true, AppendTo = "body" })

The above declaration basically says: “define all elements marked with the class “simple-list-item”  that are descendants of the dom element with class “programmers” as Drag Sources with tag “Programmer”. Now since the whole grid containing all programmers is under a div with class “programmers” and since all rows of this grid have the class “simple-list-item” all programmers are all defined as Drag sources.

The request is extended also to future elements that will be added as descendants of the element with class “programmers“, thus if we insert new elements in the grid they will be automatically declared as Drag Sources.

The “simple-list-item” class is added to each row of the grid as a part of its row definition instructions with:

  1. .ItemRootHtmlAttributes(new Dictionary<string, object> { { "class", "simple-list-item" } })

About the “LeaderProgrammer” tag, it must be added to all data items with the property CanBeTeamLeader set to true. Since this property may change during processing we must add it with a knockout binding attached to the CanBeTeamLeader property:

  1. .KoBindingsGenerator(bb => bb.CSS("LeaderProgrammer", l => l.CanBeTeamLeader)
  2.     .Reference(m => m)
  3.     .Get().ToString())

The KoBindingsGenerator is a method of the fluent interface of the grid row definition. It accepts a function of the type

  1. Func<IBindingsBuilder<U>, string> knockoutBindings

and applies the knockout bindings defined in the body of the function to all rows of the grid, by adding them to the client row template being built by the grid. We use the IBindingsBuilder interface received as argument to build a standard Knockout Css binding that adds the css class “LeaderProgrammer” whenever the property CanBeTeamLeader is true, and a Reference binding that bind each row to its associated data item. The Reference binding enables the “Dragged” programmer to “release” its referred data to the data item referred by the “Drop Traget”.

Since in the options of the DragSourceItems declaration we set DestroyOriginal to true a dropped programmer is removed from the programmers list.

When we put a new Leader Programmer in the Leader Programmer area, the old Leader programmer returns back to the programmers list because we defined the Programmers list as mirroring pool for the programmers entities (this is done in javascript):

  1. ko.mirroring.pool = function (obj) {
  2.     var dis = obj["MainCompetence"];
  3.     dis = ko.utils.unwrapObservable(dis);
  4.     if (dis === "Artist") return TeamBuilding.ClientModel.AllArtsist.Content;
  5.     else if (dis === "Programmer") return TeamBuilding.ClientModel.AllProgrammers.Content;
  6.     else return null;
  7. };

All mirroring pools are defined by assigning a javascript function to the ko.mirroring.pool configuration variable. This function is passed all items that were removed from their places because of a Reference binding based interaction and that were put in no other place, so they “disappeared” from the client side ViewModel. This function is their last chance to find an “home”. This function analyze all properties of each item and possibly find a new “home” for it.

Moving either an artist or a programmer in the detail area assigns a reference to its associated data item to the knockout observable CurrentDetail in the client ViewModel without detaching the data item from its previous place, because in this case the DestroyOriginal option of the drop target is not set to true. This triggers the instantiation of a template that shows the data item in detail mode:

  1. @ch._with0(m => m.CurrentDetail,
  2.     @<text>
  3.         <p>
  4.         <span class='ui-widget-header'>@item.LabelFor(m => m.Name)</span>
  5.         :
  6.         @item.TypedEditDisplayFor(m => m.Name, simpleClick: true)
  7.         @item.ValidationMessageFor(m => m.Name, "*")
  8.         </p>
  9.         <p>
  10.         <span class='ui-widget-header'>@item.LabelFor(m => m.Surname)</span>
  11.         :
  12.         @item.TypedEditDisplayFor(m => m.Surname, simpleClick: true)
  13.         @item.ValidationMessageFor(m => m.Surname, "*")
  14.         </p>
  15.         <p>
  16.         <span class='ui-widget-header'>@item.LabelFor(m => m.EMail)</span>
  17.         :
  18.         @item.TypedEditDisplayFor(m => m.EMail, simpleClick: true)
  19.         @item.ValidationMessageFor(m => m.EMail, "*")
  20.         </p>
  21.         <p>
  22.         <span class='ui-widget-header'>@item.LabelFor(m => m.Address)</span>
  23.         :
  24.         @item.TypedEditDisplayFor(m => m.Address, simpleClick: true)
  25.         @item.ValidationMessageFor(m => m.Address, "*")
  26.         </p>
  27.         <p>
  28.         <span class='ui-widget-header'>@item.LabelFor(m => m.CanBeTeamLeader)</span>
  29.         :
  30.         @item.CheckBoxFor(m => m.CanBeTeamLeader)
  31.         </p>
  32.     </text>
  33. , ExternalContainerType.koComment, afterRender: "mvcct.ko.detailErrors", forceHtmlRefresh: true, isDetail: true)

The _with0 instruction is a different enhancement of the knockout with binding, that accepts an in-line razor helper as client template. Among its arguments there is one named isDetail that we set to true, to inform the Data Moving Plug-in engine that the template is the detail view of a data item. This declaration triggers a synchronization behavior between the original UI of the data item and its detail view.

 

Having finished describing how the user can manipulate the Workspace we can move to see how server-client interaction takes place. The two Entity Set Windows of the Workspace are implemented with two grids. For a detailed description about how to “code” grids you may refer to Data Moving Plugin Controls. Here we point out just that since all data items are already on the client side we must use a local retrievalManager to execute the paging, sorting and filtering queries:

  1. .StartLocalRetrievalManager(m => m.AllProgrammers.Content, true, "TeamBuilding.programmersRM").EndRetrievalManager()

The first argument is the source of all items to be queried(a property of the client side ViewModel), the second argument set to true requires the execution of an initial query as soon as the page is loaded (in order to show some initial data in the grid), and the third argument is where to put the newly created retrievalManager.

The updatesManager of the two grids are both root updatesManager since our items are not children of any one-to-many relation, as in all other examples we have seen in Data Moving Plugin Controls. However, in this case they don’t communicate directly with the server, because we will define a whole WorkSpace updatesManager that will take care of collecting data from the two grids updatesManagers, handling the updates of the Core WorkSpace, communicating with the server, and dispatching the responses of the server to the two grids updatesManagers.

The definition of the programmers updatesManager is:

 

  1. .CreateUpdatesManager<TeamBuildingDestinationViewModel>("TeamBuilding.programmersUpdater", true)
  2.     .BasicInfos(m => m.Id, m => m.ProgrammersChanges, "TeamBuilding.DestinationModel")
  3.     .IsRoot(Url.Action("UpdateTeam"))
  4. .EndUpdatesManager()

It appears more complex of the updates managers we have seen in Data Moving Plugin Controls. The first call to CreateUpdatesManager contains the whole path where to store the updatesManager on the client side instead of the name of the property of the ViewModel where to store it, that’s why the second optional parameter is set to true. Moreover, the method call contains a generic type instantiation, the viewmodel we will use to submit all changes to the server:

  1. public class TeamBuildingDestinationViewModel
  2. {
  3.     public Team ProposedTeam { get; set; }
  4.     public OpenUpdater<Employed, Guid?> ProgrammersChanges { get; set; }
  5.     public OpenUpdater<Employed, Guid?> ArtistsChanges { get; set; }
  6. }

The first property will be filled with the whole Core Workspace, while the other two properties will be filled with the programmers and artists change sets. That’s why the call to BasicInfos has two more parameters after the specification of the principal key: the first parameter is the property of the destination ViewModel where to store the programmers change set, and the third parameter is the property of the whole client ViewModel where to put the destination viewmodel before sending it to the server. The IsRoot method contains a fake url since the destination ViewModel will be posted to the server by the whole Workspace updatesManager.

The whole Workspace updatesManager must be defined in javascript since it is not tied to any specific Data Moving Plugin control:

  1. $('#outerContainer').attr('data-update'),
  2.  TeamBuilding.ClientModel, "ProposedTeam", TeamBuilding.DestinationModel, "ProposedTeam",
  3.  { updatersIndices: [TeamBuilding.programmersUpdater, TeamBuilding.artistsUpdater],
  4.      classifyEntity: function (x) {
  5.          if (x['Id'] && x['MainCompetence'])
  6.              return x.MainCompetence() == "Programmer" ? 0 : 1;
  7.          else
  8.              return null;
  9.      },
  10.      ........

The first argument is the url where to submit the destination ViewModel that is extracted from an Html5 attribute of a dom element. The second argument is the whole client ViewModel and the third argument is the path where to find the Core Workspace within the whole client ViewModel. The fourth argument is the destination ViewModel, and the fifth argument is the path to the place where to store the Core Workspace within the destination ViewModel. Then we have an option argument with several properies. Here we analyze just three of them, since all others are connected to error handling that will be discussed in Single Page Applications 2: Validation Error Handling.

UpdatersIndices is an array containing all updatersManager of the Entity Set Windows of the Workspace; in our case the updatesManagers of the two grids.

classifyEntities is a function that given an entity must return the index in the previous array of its Entity Set Windows updatesManager. This function enables the whole Workspace updatesManager to process adequately all entities it find inside the Core WorkSpace.

That’s enough for everything to work properly! When the user clicks submit the update method of TeamBuilding.updater is invoked, the destination ViewModel is filled, and submitted to the server. When the server send the response the parts of the response destined to the Entity Set Windows updatesManagers will be automatically dispatched to them, and processed automatically. As a consequence, modifications to the the Core Workspace returned as remote commands by the server are applied, keys created for newly inserted entities are dispatched each to its entity, and errors associated to various data elements are dispatched next to the adequate UI elements:

  1. $('#submit').click(function () {
  2.         var form = $(this).closest('form');
  3.         TeamBuilding.ClientModel.CurrentDetail(null);
  4.         if (form.validate().form()) {
  5.             TeamBuilding.updater.update(form);
  6.         }
  7.     });

Let give a look to the action method:

  1. public HttpResponseMessage Post(TeamBuildingDestinationViewModel model)
  2.         {
  3.             if (ModelState.IsValid)
  4.             {
  5.                 try
  6.                 {
  7.                     var builder = ApiResponseBuilder.NewResponseBuilder(model, ModelState, true, "Error in client request");
  8.                     builder.Process(m => m.ArtistsChanges, model.ProposedTeam, m => m.Id);
  9.                     builder.Process(m => m.ProgrammersChanges, model.ProposedTeam, m => m.Id);
  10.                     //business processing here
  11.  
  12.                     var response = builder.GetResponse();
  13.                     return response.Wrap(Request);
  14.                 }
  15.                 catch (Exception ex)
  16.                 {
  17.                     ModelState.AddModelError("", ex.Message);
  18.                     return
  19.                         Request.CreateResponse(System.Net.HttpStatusCode.InternalServerError, new ApiServerErrors(ModelState));
  20.                 }
  21.             }
  22.             return Request.CreateResponse(System.Net.HttpStatusCode.InternalServerError, new ApiServerErrors(ModelState));
  23.         }

This code is very similar to the code we have seen in the action methods that processes the grids updates in Data Moving Plugin Controls: we create an ApiResponseBuilder and then we call the Process method on each of the Entity Set change sets we received in the destination ViewModel. Since our principal key are Guids we dont need to specify a custom key generation function, so the indication of which property is the principal key suffices. However, now the Process methods has 3 arguments, instead of two. We used a different overload! An overload that accepts a Core WorkSpace as second argument. Why we need this further argument? Simple, because we have to process also the changes of the entities that are contained in the Core WorkSpace. In fact, a programmer or artist that we added to the Team might have been modified, or it might be a newly inserted item. The addition of the new argument enables the Process method to include also the entities contained in the Core WorkSpace  in the change sets in their appropriate places if this is necessary.

 

Now suppose we want to modify the client side Core WorkSpace by changing all programmers names with the suffix “Changed” and by adding two more programmers to the team. We need to add adequate remote commands in the response. How to built them? Quite easy! It is enough to create a changes builder object and then to mimic these operations on it:

  1. var changer = builder.NewChangesBuilder(model.ProposedTeam);
  2.  
  3. changer.Down(m => m.Programmers)
  4.     .UpdateModelIenumerable(m => m, m => m.Name, (m, i) => m.Name + "Changed");
  5. changer.UpdateModelField(m => m.LeaderProgrammer, m => m.Name, model.ProposedTeam.LeaderProgrammer.Name + "Changed");
  6. changer.Down(m => m.Programmers)
  7.     .AddToArray(m => m[0],
  8.         new Employed()
  9.         {
  10.             Id = Guid.NewGuid(),
  11.             MainCompetence = "Programmer",
  12.             Name = "John1",
  13.             Surname = "Smith1",
  14.             Address = "New York, USA",
  15.             EMail = "John@dummy.us",
  16.             CanBeTeamLeader = true
  17.         }, 0, true).AddToArray(m => m[0],
  18.             new Employed()
  19.             {
  20.                 Id = Guid.NewGuid(),
  21.                 MainCompetence = "Programmer",
  22.                 Name = "John2",
  23.                 Surname = "Smith2",
  24.                 Address = "New York, USA",
  25.                 EMail = "John@dummy.us"
  26.             }, 0, true);

The first instruction modifies the names of the programmers that are simple members of the team(actually it just creates the remote command to do this). We go down the Programmers properties of the Core Workspace and then call the UpdateModelIEnumerable that applies a modifications to all elements of an IEnumerable. The first argument specify the IEnumerable to be modified; since we already moved into the IEnumerable it is just m => m. The second argument specifies the property of each element that must me modified and the third argument specifies how to modify it.

The second instruction modifies the LeaderProgrammer name.  It is self-explanatory.

Finally, the third instruction adds two more programmers. We move down the Programmers property, and then we call AddToArray twice. The first argument of each call specifies the place in the javascript array where to place the newly added element: in our case we place it at index 0….but wait …wait wait, since the last argument of the call is set to true the enumeration starts from the bottom, so we are just queuing the new elements at the bottom of the array.

Now in order to include all “remote commands” in the response we must substitute:

  1. var response = builder.GetResponse();

with:

  1. var response = builder.GetResponse(changer.Get());

However, we have a problem, the two programmers that we added to the team might be already contained in the programmers list so we might have an entity duplication in the client side View Model. Luckily, the Data Moving Plug-in offers tools to enforce  uniqueness.we may trigger the processing that enforce uniqueness of entities in onUpdateComplete callback of the whole WorkSpace updatesManager (defined in its options):

  1. onUpdateComplete: function (e, result, status) {
  2.     if (!e.success) return;
  3.     var hash = {};
  4.     mvcct.updatesManager.utils.entitiesInWorkSpace(TeamBuilding.ClientModel.ProposedTeam, hash);
  5.     TeamBuilding.programmersUpdater.filterObservable(hash);
  6.     TeamBuilding.artistsUpdater.filterObservable(hash);
  7. },

The mvcct.updatesManager.utils.entitiesInWorkSpace method extracts all entities contained in the core workspace, and index them into an hash table. After that, each Entity Set updatesManager ensures they are not contained in the Entity Set Window it takes care of. The task is carried out efficiently because of the indexing performed by mvcct.updatesManager.utils.entitiesInWorkSpace .

That’ all for now!

Stay tuned and give a look also to all other Data Moving Plug-in introductory tutorials and videos

                      Francesco

Tags: , , , , , ,

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: , , , ,

Oct 28 2011

Handling Big Amounts of Data with Client-Side Templates

Category: Asp.net | MVCFrancesco @ 23:22

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

Handling Big Amounts of Data with Client-Side Templates 2

In my previous post here I introduced the client-side templates of the Mvc Controls Toolkit, and I discussed how they support both the use of server controls and validation.  I pointed out also that they allow the implementation of rich UI interfaces at a low BandWidth cost since the rich UI code is sent in templates and then replicated on the client-side on several data items by exploiting the capability of templates to be instantiated on on whole collection of items.

As already discussed, instantiation of the collections is done substantially, either by substituting “holes” in the template with the actual data contained in each item once for all when the template is transformed into html nodes, or by creating bindings between item properties and template html elements. Bindings grant that modifications of data items performed after the template has been instantiated propagate immediately to the bound html elements, and vice versa that input provided by the user into html elements are propagated immediately to the data items they are bound to. However, bindings have an higher cost in terms of computational resources, especially if the html elements involved in the binding are complex server controls, like for instance a DateTime input.

As a consequence, one should avoid using input fields bound to data items when dealing with big collections of thousands of items. This can be achieved, by allowing the user to select the collection items he would like to operate on, as a first step. Once the item has been selected a new template containing all desired input field is instantiated. Since the user will probably select just a few of all listed items, this way we avoid any performance issue. Moreover, if we group all selected items in a different area of the page we help the user to manage properly all items he decided to operate on without being forced to look for them among thousands of other items.

To show how all this can be implemented we modify adequately the example of my previous post. To download the full code of the example, please,  go to the download area of the Mvc Controls Toolkit  and download the file: RazorClientTemplating.

Now we need two ViewModel versions of the product class, the first one to be used for display only purposes, and the other one to be used once the user has selected  a product and needs to specify the quantity, and delivery date:

public class ProductViewBase
    {
        public int Code { get; set; }
        public string Description { get; set; }
    }
    public class ProductView : ProductViewBase
    {
        
        
        [Required, Range(1, 4), Display(Name="Quantity", Prompt="Insert Quantity")]
        public int? Quantity { get; set; }
        [DateRange(DynamicMinimum="MinDeliveryDate", DynamicMaximum="MaxDeliveryDate")]
        public DateTime DeliveryDate { get; set; }
        [MileStone, ScriptIgnore]
        public DateTime MaxDeliveryDate
        {
            get
            {
                return DateTime.Today.AddMonths(2);
            }
            
        }
        [MileStone, ScriptIgnore]
        public DateTime MinDeliveryDate
        {
            get
            {
                return DateTime.Today.AddDays(2);
            }
            
        }
    }

 

Since now the ProductView class is used only for the products that have been selected by the user, we modified  the RangeAttribute to require a minimum Quantity of 1: if the user changes his mind and he doesn’t want the product anymore he can just delete it from the list of selected products

We need two Client-Side ViewModel. The first one for handling the list of all products with some kind of paging:

public class ToBuyViewModel
    {
        public List<ProductViewBase> Products { get; set; }
        public int LastKey { get; set; }
        public DateTime DefaultDeliveryDate { get; set; }
    }

The LasteKey integer is used as in my previous post to enable the user to load more products pages in the View through Ajax, while DefaultDeliveryDate is the delivery date that is suggested to the user immediately after he selects a product.

The other model handles the selected products and is submitted to the Confirm action method that handles the purchase confirmation:

public class PurchaseConfirmationView
    {
        public List<ProductView> Products { get; set; }
        public bool PurchaseConfirmed { get; set; }
        public bool GoToList { get; set; }
    }

It contains the list of all selected products and two booleans that encode some user choices. Once the user has selected all products the above Client-Side ViewModel is submitted to the Confirm action method that will handle the purchase confirmation process allowing the user either to cancel the purchase and returns to the main purchase page or to confirm the purchase by means of the PurchaseConfirmed and GoToList booleans. Below the Confirm action method:

[HttpPost, System.Web.Mvc.OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
        public ActionResult Confirm(PurchaseConfirmationView purchase)
        {
            if (purchase.GoToList)
                return RedirectToAction("Index");
            if (ModelState.IsValid)
            {

                if (purchase.PurchaseConfirmed)
                {
                    //insert here actual purchase locgics
                    return View("PurchaseConfirmed", purchase);
                }
                else
                {
                    purchase.GoToList = false;
                    purchase.PurchaseConfirmed = false;
                    return this.ClientBlockView("PurchaseConfirmation", purchase, "confirmModel");
                }
            }
            else
            {
                purchase.GoToList = false;
                purchase.PurchaseConfirmed = false;
                return this.ClientBlockView("PurchaseConfirmation", purchase, "confirmModel");
            }
        }

As you can see the Confirm Action returns a ClientBlockViewResult, thus defining the whole Confirmation View as a Client Block. Infact the Confirmation View is completely handled on the Client-Side, since it contains just a ClientBlockRepeater showing all products selected by the user.

The PurchaseConfirmationView ViewModel is rendered in the main view in a separate form whose submit target is the Confirm action method, and it it is filled with the new products selected by the user. To handle it we define a different Client Block by means of the Template extension method:

<div id="ProductsToBuy">
    <h2>Products Chosen</h2>
    <br /><br /><br />
    @using (Html.BeginForm("Confirm", "Shopping"))
    {      
        @Html.Template(new PurchaseConfirmationView { Products = new List<ProductView>() },
        _S.H<PurchaseConfirmationView>(ToBuyInnerBlock),
        true,
        "confirmModel",
        "ProductsToBuy")
    }
    
    </div>

The third parameter of the Template extension method is set to true, to declare we are defining a new Client Block, the first parameter is the object we would like to use as Client ViewModel, “confirmModel” is the name of the global javascript variable where to put the Client ViewModel, “ProductsToBuy” is the id of the div acting as Root of he Client Block. Finally, ToBuyInnerBlock is the name of the template containing the whole code of the Client Block; we defined it with a Razor Helper:

@helper ToBuyInnerBlock(HtmlHelper<PurchaseConfirmationView> Html)
    {
       
        @Html.ValidationSummary(false)
        <div class="ToScroll">
                  <table >
                  <thead>
                      <tr>
                      <td><strong>Product</strong></td>
                      <td><strong>Quantity</strong></td>
                      <td><strong>Delivery Date</strong></td>
                      <td><strong>Delete</strong></td>
                      </tr>
                  </thead>
                  @Html.ClientBlockRepeater(m => m.Products,
                    _S.H<ProductView>(
                            @<tr>
                                <td>
                                @{var itemBindings = item.ItemClientViewModel();}
                                @item._D(m => m.Description)
                                </td>
                                <td>
                                @item.TypedTextBoxFor(m => m.Quantity)
                                @item.ValidationMessageFor(m => m.Quantity, "*")
                                </td>
                                <td>
                                @item.DateTimeFor(m => m.DeliveryDate, DateTime.Today,
                                            false).Date()
                                </td>
                                
                                <td><a href="#" data-bind="click: function() { confirmModel.Products.remove($data) }">Delete</a></td>
                         </tr>
                        ),
                        ExternalContainerType.tbody,
                        new { id = "ProductsToBuyContainer" },
                        null,
                        "ProductsToBuyTemplate")
                  
                  </table>
                                    
        </div>
}

We added manually a Click binding to remove a product from the list of products to buy. It uses the remove method of the observableArray class to remove the product from the Products observable array. The argument of the remove method is $data that is substituted with the actual data item that is associated to the template when the template is instantiated. The anonymous object passed as third argument of the repeater just add an Html id attribute to the tbody tag that will be created as container of all instantiated templates, while the last argument of the repeater “ProductsToBuyTemplate” just gives a custom name to the Client Template that will be automatically created by the repeater. In our case this is a necessity, because the standard PrefixedId(m =>m.Products)+”_Template” name that is automatically created by the repeater would have collided with the analogous name of the template for the whole list of products to choose.

In fact the page is divided into two columns, the left column contains all products and the right column the products the user have chosen to buy. The two columns correspond to two different Client Blocks. The root of the Client Block containing the products to buy has been specified in the Template extension method to be the div with id “ProductsToBuy”. The root of the first Client Block that contains all product is specified in the Action method, that returns a ClientBlockViewResult, to be an Html node with id “ProductsToList”:

return this.ClientBlockView(shopping, "productsClientView", "ProductsToList");

Below the first Client Block:

<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>Select Product</strong></td>
                  
                      </tr>
                  </thead>
                  
                   @Html.ClientBlockRepeater(m => m.Products,
                       _S.H<ProductViewBase>(
                            @<tr>
                                    <td>
                                    @item._D(m => m.Description)
                                    </td>
                                    <td>
                                    @item._D(m => m.Price)
                                    </td>
                                    <td><a href="#" data-bind="click: function() { productsClientView.buyProduct($data) }">Buy</a> </td>
                             </tr>
                        ),
                        ExternalContainerType.tbody,
                        new { id = "ProductsContainer" })
                  </table>

Also here we added manually a Click binding.  In this case the click handler calls the javascript function that adds a new product to the list of products to buy:

<script language='javascript' type='text/javascript'>

                        productsClientView.buyProduct = function (product) {
                            confirmModel.Products.push(
                            {
                                Quantity: ko.observable(1),
                                Code: product.Code,
                                Description: product.Description,
                                Price: product.Price,
                                DeliveryDate: ko.observable(this.DefaultDeliveryDate())
                            }

                        );
                        };
                  </script>

Description and Code are not defined as observables since they will not be bound to any html element with a permanent binding but they will just fill predefined “holes” in the template.Quantity, instead, is defined as an observable and is given the initial value 1, since it will be bound to an input field. DeliveryDate, too, is defined as an observable since it will be bound to a DateTime input. Please notice, that, since it is copied from the DefaultDeliveryField of the first ViewModel, this date is already defined as an observable. However, we are forced to unwrap it from the original observable and place it into a new observable because if we use an unique observable for all dates all dates are forced to have the same value.

The divs named automaticLoad and automaticLoadContent before the repeater handles both the scrolling of the left column and the automating loading of a new page of products that is triggered automatically when the user move the scrollbar to the bottom:

<script language='javascript' type='text/javascript'>
                    $(document).ready(function () {
                        $('#automaticLoad').scroll(function () {
                            if ($('#automaticLoad').scrollTop() >= $('#automaticLoadContent').height() - $('#automaticLoad').height()) {
                                $.getJSON("@MvcHtmlString.Create(nextPageUrl)" + "&startFrom=" + productsClientView.LastKey(), function (data) {
                                    if (data != null && data.Products != null) {
                                        data = ko.mapping.fromJS(data);
                                        for (var i = 0; i < data.Products().length; i++) {
                                            productsClientView.Products.push(data.Products()[i]);
                                        }
                                        productsClientView.LastKey(data.LastKey());
                                    }
                                });
                            }
                        });
                        
                    });
                </script>

That’s all! Download the full code from the download area of the Mvc Controls Toolkit and enjoy! The file is named: RazorClientTemplating

 

                                                                           Francesco

Tags: , , , ,

Oct 28 2011

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

Category: MVCFrancesco @ 23:07

Defining MVC Controls 2: Using the DataGrid

Defining MVC Controls 3: Datagrid, Sorting, and Master-Detail Views

Data Filtering, in the New Mvc 3 Version of the Mvc Controls Toolkit

Handling Big Amounts of Data with Client-Side Templates

Handling Big Amounts of Data with Client-Side Templates 2

This post is a tutorial on the new Client-Side templates, and on the ClientBlockRepeater of the Mvc Controls toolkit. I assume that everyone is already familiar with the Client Block feature of the Mvc Controls Toolkit.

Client-Side Templates allow a rich UI to be defined just once in a template, and then to use this template to render each of the items in a big collection contained in a Client-Side ViewModel. This way the rich interface is obtained with a low Internet BandWidth cost, since code needed to create the rich UI is sent to the client just once in a template.

Templates are defined as normal Mvc Controls Tool kit templates, by the developer, then they are rendered on the sever and transformed into client-side templates. The template is enriched with some extra code that enables both client and server side validation on the input fields that are created dynamically when the template is instantiated on each Javascript item on the client-side.Once on the client, they are instantiated with each of the data items contained into an array of the Client-Side ViewModel.

The insertion of the actual data into the template can be done statically by inserting them just once when the template is instantiated with the data item, or dynamically, in which case we create a permanent client-side binding between the data field of the data item and an html element of the template. In the case of dynamic byndings each data item acts as a ViewModel for the instance of the template that is associated to it. Dynamic instantiation of a data fields has the advantage that each time the data is changed, the html element it is bound to is automatically updated without requiring the re-instantiation of the whole template. Moreover, dynamic binding can be also two-way, in which case also modifications of the html element can be reflected back on the data field the html elements is bound. Two way bindings are necessary when we want to collect input from the user into the Client-side ViewModel.

As a conclusion dynamic bindings are more flexible, but they are also slower to render, expecially two way bindings, so if one would actually like to put on the screen thousands of items they should be planned carefully.

Suppose we want to show a list of products that the user can purchase by just inserting the quantity he would like to receive together with the desired delivery date in the same row displaying the product. This approach is quite rough and it is not efficient, however the main purpose of this tutorial is to show how client-side template works, so we will use it. In a further tutorilal we will show a better approach.

For simplicity we will just display the description of  the product. Thus our simple ViewModel description of each product is:

public class ProductView
    {
        public int Code { get; set; }
        public string Description { get; set; }
        [Range(0, 4), Display(Name="Quantity", Prompt="Insert Quantity")]
        public int? Quantity { get; set; }
        [DateRange(DynamicMinimum="MinDeliveryDate", DynamicMaximum="MaxDeliveryDate")]
        public DateTime DeliveryDate { get; set; }
        [MileStone, ScriptIgnore]
        public DateTime MaxDeliveryDate
        {
            get
            {
                return DateTime.Today.AddMonths(2);
            }
            
        }
        [MileStone, ScriptIgnore]
        public DateTime MinDeliveryDate
        {
            get
            {
                return DateTime.Today.AddDays(2);
            }
            
        }
    }

We put a range attribute on the Quantity, to allow a maximum purchase of 4 instances of the same product, than we added two read only properties just to define some constraints on the delivery date through the DateRangeAttribute as already explained in a previous tutorial.The Milestone attribute declares that the two Date bounds will not be rendered, and the ScriptIgnoreAttribute, avoid that they are rendered when the model is serialized in json.

Our full ViewModel is:

public class ToBuyViewModel
    {
        public List<ProductView> Products { get; set; }
        public int LastKey { get; set; }
    }

The LastKey property is needed to handle our paging technique: we put on the View an “AddNewPage” button, that when clicked add a new page of products to the ones already shown on the screen. Thus we have no page changes, but just the addition of new products…just a marketing technique to sell more products :)

Since we want to handle our products with cliemt-side templates, we define the whole View as a Client Block. Accordingly our Action method will return a ClientBlockViewResult by calling the ClientBlockView controller extension method:

return this.ClientBlockView(shopping, "productsClientView");

Where shopping is our ViewModel filled with data. We will use a ClientBlockRepeater to render our instantiated Client-Side templates as child of a tbody tag of a table:

@Html.ValidationSummary(false)
              <table >
              <thead>
                  <tr>
                  <td><strong>Product</strong></td>
                  <td><strong>Quantity</strong></td>
                  <td><strong>Delivery Date</strong></td>
                  </tr>
              </thead>
              
              @Html.ClientBlockRepeater(m => m.Products,
                   _S.H<ProductView>(
                        @<tr>
                            <td>
                            @item._D(m => m.Description)
                            </td>
                            <td>
                            @item.TypedTextBoxFor(m => m.Quantity)
                            @item.ValidationMessageFor(m => m.Quantity, "*")
                            </td>
                            <td>
                            @item.DateTimeFor(m => m.DeliveryDate, DateTime.Today,
                                        false).Date()
                            </td>
                     </tr>
                    ),
                    ExternalContainerType.tbody,
                    new { id = "ProductsContainer"})           
              </table>

As you can see the template passed to the repeater is a normal server side template. It is compiled and transformed into a client side template in a way that is completely transparent to the developer.

The bindings of the TypedTextBox with the Quantity, and the one of the  DateTimeInput with the DeliveryDate of each ProductView object in the Products collection of our Client-Side ViewModel are deined automatically by the Client Block engine of the Mvc Controls Toolkit, since the input fields are defined through expressions(the Client Block engine define automatically bindings according to the same name convention used by the default model binder). The Description is inserted into the template statically (that is once for all when the template is instantiated) with the _D helper.This helper renders its parameter enclosed in a span element and with the formatting defined in a FormatAttribute.

There is also an _F helper that displays its parameter with formatting but without the span element, and the _P helper that displays its parameter in a rough format without any formatting.

As you can see, thanks, to both the implicit binding definition of input fields performed by the Client Block engine and to The _D helper we have defined the Client-Side template in exactly the same way we would have defined a Server-Side template! This make the use of Client-Side template straigthforward and enhance the re-usability of templates.

Let’ handle paging now. We need just to ask a new page of products through an Ajax call to an action method: As a first step we build the Url string of the request. We pass a ViewHint= “json” parameter to our action method, just to say we want the answer in JSON. Since our action method has been decorated with the attribute:

[AcceptViewHint(JsonRequestBehavior.AllowGet)]

as explained here, the result of the action method is automatically transformed into a JsonResult.

At run time we add also the parameter that specifies the code of the product to start with. When data arrive we push it in our original Products collection…and miracle of the Client Templates, new templates are instantiated automatically for the new products! Finally we update, our LastKey property to be ready for the next page request.

@{
                    string nextPageUrl = Url.Action("Index", "Shopping", new { ViewHint = "json" });
                }

                <script language='javascript' type='text/javascript'>
                    $(document).ready(function () {
                        $('#Button1').bind('click', function () {
                            $.ajax({
                                url: "@MvcHtmlString.Create(nextPageUrl)" + "&startFrom=" + productsClientView.LastKey(),
                                dataType: "text",
                                success:
                                function (textData) {
                                    var data = $.parseJSON(textData);
                                    if (data != null && data.Products != null) {
                                        data = ko.mapping.fromJS(data);
                                        var unwrapped = productsClientView.Products();
                                        for (var i = 0; i < data.Products().length; i++) {
                                            unwrapped.push(data.Products()[i]);
                                        }
                                        productsClientView.Products(unwrapped);
                                        productsClientView.LastKey(data.LastKey());
                                    }
                                }
                            });
                        });
                    });

When the user clicks the “submit” button, in order to avoid sending a big amount of useless data to the server, we copy into another Client-Side ViewModel, that is located into another form, just the items with a Quantity greater than 0, and then submit this other form. This way, since we are submitting another form, we avoid sending all input fields that we have created to the server, and we avoid also sending our main Client-Side ViewModel that might have become very big after a lot of new page requests.

Below the other form with the secondary ViewModel:

@using (Html.BeginForm("Confirm", "Shopping"))
            {
                var confirmModel = Html.SAClientViewModel(
                    "confirmModel",
                    new PurchaseConfirmationView(),
                    initialSave: false,
                    applyBindings: false);
                    
            }

 

We rendered our secondary Client-Side ViewModel differently from our main Client-Side ViewModel since it is not part of the Server-Side ViewModel, but it is destined to an Action method that uses a different type of ViewModel. The SAClientViewModel helpers accepts directly an instance of an object and defines a Client ViewModel with it. Finally, below the javascriot function that fill the secondary Client-Side ViewModel:

<script language='javascript' type='text/javascript'>
        productsClientView.submitProductsChosen = function () {
            confirmModel.Products = new Array();
            for (var i = 0; i < this.Products().length; i++) {
                var quantity = this.Products()[i].Quantity();
                if (quantity != null && quantity != 0) {
                    confirmModel.Products.push(this.Products()[i]);
                }
            }
            if(confirmModel.Products.length>0)
                confirmModel.saveAndSubmit();
        };
    </script>

Now we need just to attach the javascript function that submits the form to the click event of some button. We can do this by defining manually a click binding for this button. We ask for a IBindingsBuilder interface to the helper of our View by calling the ClientBindings extension method, and use it to define our click binding:

@{
                    var clientModel = Html.ClientBindings();
                    var sumbitButtonBindings = clientModel.Click(m => m, "submitProductsChosen").Get();
                }
                <input type="button" value="Submit" data-bind='@sumbitButtonBindings'/>

 

That’s all! The full code is contained in the RazorClientTemplating file in the Mvc Controls Toolkit download page. Download it and enjoy!

                                                                    Francesco

Tags: , , , , ,