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

Jul 1 2016

Ap.net Core 1.0.0 RTM Version of the Mvc Controls Toolkit Ready

Category: Asp.net core | WebApi | MVC | Htnl5 fallback | Asp.netFrancesco @ 20:00

The first Asp.net Core 1.0.0 RTM release of the Mvc Controls Toolkit is available for download! This is the link that explains how to install it, while a starting example may be downloaded here. Pay attention! You must follows all installation steps also for running the example, since the example among other things has also the purpose of becoming familiar with installation, and configuration stuffs.

Enjoy!  & Stay tuned

In a short time tutorials, live examples, and a complete documentation web site

Francesco

Tags: , , ,

Jun 25 2016

Ap.net Core Rc2 Version of the Mvc Controls Toolkit Ready

Category: Asp.net core | WebApi | Htnl5 fallback | MVC | Asp.netFrancesco @ 02:40

The first Asp.net Core Rc2 release of the Mvc Controls Toolkit is available for download! This is the link that explains how to install it, while a starting example may be downloaded here. Pay attention! You must follows all installation steps also for running the example, since the example among other things has also the purpose of becoming familiar with installation, and configuration stuffs.

Enjoy!  & Stay tuned

In a short time tutorials, live examples, and a complete documentation web site

Francesco

Tags: , , ,

May 7 2015

JavaScript Intensive Web Applications 4: JSON based Ajax and Single Page Applications

Category: WebApi | MVC | JavaScriptFrancesco @ 06:19

JavaScript Intensive Web Application 1: Getting JavaScript Intellisense

JavaScript Intensive Web Applications 2: Enhancing Html with JavaScript

JavaScript Intensive Web Applications 3: Enhancing Applications with Ajax

jsFarmIntellisense

In this last post of the series, I discuss the use of JSON based Ajax calls and client side View Models. I will propose also a simple implementation of a knockout.js binding to apply a generic jQuery plug-in to an Html node. The post is concluded with a short analysis of Single Page Application frameworks.

In my previous post we have seen that Html returning Ajax calls update the needed parts of an Html page while keeping unmodified the remainder of the page. This allow a  tighter interaction between user and server because the user may work on other areas of the page while waiting for a server response, and he/she may ask  supplementary information to the server when he/she is in the middle of a task without loosing the whole page state.

The user experience may be improved further if we are able to maintain the whole state of the current task on the client, because this way we reduce further the need to communicate with the server: the user may prepare all data for the server while receiving immediately all needed help and suggestions with no need to communicate with the server in this first stage. Communication with the server is needed only after everything has been prepared. For instance, the user may modify all data contained in a grid, recurring to a detail  window when needed. Entities connected with one-to-many relations with the main list may be edited in the detail view. Everything without communicating with the server! Then, when all changes have been done, the user performs a single submit, and updates the global state of the system. The server answer may contain corrections performed by the server to the data supplied by the user, that are automatically applied to the client copy of the data.

In other words maintaining the whole state of a task on the client side allows a tighter user-machine cooperation since this cooperation may be performed without waiting for remote server answers. However, the increased complexity of the client side requires a robust and modular architecture of the client code. In particular, since we move logics, and not only UI on the client side, Html nodes that are  mainly UI staffs must be supported by JavaScript models. Models and Html nodes should cooperate while keeping separation of concerns between logics and UI. This means that all processing must take pace on models that are then rendered by using client side templates. Accordingly, Ajax calls can’t return Html anymore, but must return JavaScript models.

Summing up, all architectures where the whole state of the current task is maintained on the client should have the following features:

  1. JSON communication with the server. The format of the data exchanged between server and client might be also Xml based, but as a matter of fact at the moment, the simpler JSON protocol is a kind of standard.
  2. Html is created dynamically by instantiating client templates, thus this kind of Web Applications are not visible to search engines.
  3. The state of client and server must be kept aligned, by performing simultaneous updates on both client and server in a transactional fashion. This means, for instance, that if a server update fails for some reason the client must be able to restore the state of the last client server synchronization.

As a matter of fact at the moment point 3 has not received the needed attention also in sophisticated Single Page Application frameworks, that don’t supply general tools to face it, so the problem is substantially left to custom solutions of the developers.

In the case of Html based Ajax communication we have seen that, since the communication is substantially based on form submits, the server relies on all input fields having adequate names to build a model that then is passed to the Action methods that serve the client requests. In JSON based commutations, instead,  input fields names are completely irrelevant since action methods receive substantially JavaScript models.

Html Ids, and CSS classes are also used as “addresses” to select Html nodes to enhance with JavaScript code.  Several frameworks like knockout.js and angular.js avoid the use of these ids and CSS classes as a way to attach JavaScript behavior to Html nodes. In their case, model properties are “connected” to Html nodes through the so called bindings that are substantially communication channels between Html nodes and the JavaScript properties that updates one of them when the other changes. They may be one-way or two ways. Bindings may also connect Html nodes with JavaScript functions, and the developer may also define custom bindings, thus bindings solve completely the problem of connecting Html nodes with JavaScript code with no need to provide unique ids, or selection-purpose CSS classes.

Below how to use a custom knockout.js binding for applying jQuery Plug-ins to Html nodes:

 

  1. <input type="button" value="my button" data-bind="jqplugins: ['button']"/>
  2. <input type="button" value="my button"
  3. data-bind="jqplugins: [{ name: 'button', options: {label: 'click me'}}]"/>

 

The binding name is followed by an array whose elements may be either simple strings, in case there are no plug-in options, or objects with a name and an option property. As you can see in knockout.js bindings are contained in the Html5 data-bind attribute.

Below the JavaScript code that defines the jqplugins custom binding:

 

  1. (function ($) {
  2.     function applyPlugin(jElement, name, options) {
  3.         if (typeof $.fn[name] !== 'function') {
  4.             throw new Error("unrecognized plug-in name: " + name);
  5.         }
  6.         if (!options) jElement[name]();
  7.         else jElement[name](options);
  8.     }
  9.     ko.bindingHandlers.jqplugins = {
  10.         update: function (element, valueAccessor, allBindingsAccessor) {
  11.             var allPlugins = ko.utils.unwrapObservable(valueAccessor());
  12.             var jElement = $(element);
  13.             for (var i = 0; i < allPlugins.length; i++) {
  14.                 var curr = allPlugins[i];
  15.                 if (typeof (curr) === 'string')
  16.                     applyPlugin(jElement, curr, null);
  17.                 else {
  18.                     applyPlugin(jElement,
  19.                         ko.utils.unwrapObservable(curr.name),
  20.                         ko.utils.unwrapObservable(curr.options));
  21.                 }
  22.             }
  23.         }
  24.     }
  25. })(jQuery)

 

The code above enables the use of all available jQuery plug-ins on all knockout.js based architectures, so that we can move to advanced client architectures based to knockout.js without renouncing to our favorite widgets and CSS/JavaScript frameworks like jQuey UI, Bootstrap, jQuery Mobile, and Zurb Foundation.

 

As a next step we may pass from storing the whole state of a single task, to storing the whole application state on the client side, which implies that the whole application must live in a single Html physical page(otherwise the whole state would be lost). Similar applications are called Single Page Applications.

In a Single Page Application Virtual pages are created dynamically by instantiating client templates that substitute the Html of any previous virtual  page in the same physical page. The same physical page may show simultaneously several virtual pages in different areas. For instance, a virtual page might play the role of master, and another the role of detail page.

Most of Single Page Application frameworks have also the concept of virtual link and/or of routing, and may connect the virtual pages to the browser history, so that the user may navigate among virtual pages with usual links and with the browser buttons.

But… why re-implementing the whole browser behavior inside a single physical page? What are the advantages of Single Page Applications compared to “multiple physical pages applications” based on Client View models?

In general having the whole application state on the client side reduces further the need to communicate with the server, thus increasing the responsiveness to the user inputs. More specifically:

  1. Once the client templates needed to create a new virtual page have been downloaded from the server further accesses to the same virtual page become very fast. On the contrary, loading a complex client model based page that is able to store the whole state of a task may be time consuming, so saving this loading time improves the user experience considerably.
  2. The state of a previously visited virtual page may be maintained so that the user finds the virtual page in exactly the same state he/she left it. This improves the cooperation between different tasks that are someway connected: the user may move forth and back between several virtual pages with the browser buttons while performing a complex task without loosing the state of each page.
  3. The same physical page may contain simultaneously several virtual pages in different areas. Thus, the user may move forth and back between several virtual page in one area, while keeping the content of another area. This scenario enables advanced form of cooperation between virtual pages.
  4. The whole Single Page Application may be designed to work also off-line. When the user has finished working the whole application state may be saved in the local storage and restored when he/she needs to perform further changes, or when he/she can go on-line to perform a synchronization with the server.

The main problem Single Page Application developers are faced with is keeping a large JavaScript codebase modular and maintainable.  Since virtual pages are actually client templates <-> ViewModel pairs, the concept of virtual page itself has been conceived in a way to increase modularity. However several virtual pages need also a way to cooperate that doesn’t undermine their modularity and the independence of each virtual pages from the remainder of the system.

In particular:

  1. Each virtual page definition should not depend on the remainder of the system to keep modularity, which, in turn, implies that virtual pages may not contain direct references to other external data structures.
  2. Notwithstanding point 1, some kind of cooperation that doesn’t undermine modularity, must be achieved among model-view pairs and among model-view pairs and the application models. A modular cooperation may be achieved by injecting interfaces that connect each model-view pair with the external environment as soon as a model-view pair is added to the page.
  3. Pointers, to data structures contained inside each virtual page should be either avoided or handled by resource managers to avoid they are used when a virtual page has been released or when it is not in an active state.

Separation is ensured someway by the concept of ViewModel itself. Durandal.js uses AMD modules to encode ViewModels. AMD protocol is a powerful technique for both loading dynamically and injecting other code modules that the current module might depend on and consequently for handling a large JavaScript codebase. However, the dependency tree is hardwired, so the injection mechanism is more adequate to inject code than dynamic data structures that might depend on the state of the ongoing computation. Accordingly, the full achievement of point 2) requires an explicit programming effort. Angular.js uses a custom dependency injection and module loading mechanism. That mechanism is easier to use, but it is less adequate for managing large codebases (in my opinion , not adequate at all). However, the fact that the injection mechanism is less structured make easier the injection of dynamic data structures when a model-view pair is instantiated.

In general most of frameworks ensure separation with some kind of cooperation, but no frameworks offer a completely out-of-the-box solution for point 2, and an out-of-the-box solution form managing the lifetime of pointers that have been injected into model-view pairs to ensure an adequate cooperation in the context of the ongoing computation (point 3). More specifically, the lifetime of pointers to AMD modules(or other types of dynamically loaded modules), that have been injected are automatically handled, but there is no out-the-of-the-box mechanism for managing pointers that a model-view pair might have to data structures contained into another model-view pair, so the developer has the burden of coding all controls needed to ensure the validity of each pointer, in order to avoid the use of pointers to data structures contained in model-view pairs that have been removed from the page.

The need for a more robust solution to problems 2 and 3 is among is among the reasons that pushed me to to implement a custom Single Page Application framework in the Data Moving Controls suite. The Data Moving SPA framework (see here, and here) relies on contextual rules that “adapt” each virtual page that is being loaded to the “current context”. Where the “current context” includes both interface implementations that connect the virtual page to the remainder of the system and information about the current state of the application, such as, if the user is logged or not, the current culture (that is the browser language and culture settings), and so on. Contextual rules are used also to redirect a not logged user to a login virtual page and to verify if the user has the needed authorizations to access the current virtual page. The interface implementations passed by the contextual rules to the virtual page View Models include also all resource managers needed  for  sharing data structures among all application virtual pages safely. Another communication mechanism is the possibility to pass input data to any page that is being loaded. Such input data are analogous to the input data passed in the query string. In fact, this input may be included also in virtual links.

Another big challenge of Single Page Applications is the duplication of code in both client and server side. In fact, the same classes, input validation criteria, and other metadata must be available on both client and server side, and when the languages used by the two sides are different, this become a big problem. The Meteor framework uses JavaScript on both server and client, and allow code sharing between the two sides. The main  price to pay for this solution is the use of a language that is not strongly typed also on the server side.  In the Data Moving SPA we faced this problem by equipping SPA server with dynamic JavaScript files implemented with Razor views. This way JavaScript data structures may be obtained by serializing in JavaScript their equivalent .Net data structures.

Another important problem all SPA must solve is the data synchronization between Client and Server. Durandal.js works quite well with Breeze.Js that offers some synchronization services for the case the server may be modeled as an OData source. Breeze.Js may be adapted also to most of all other SPA framework, but this solution is acceptable only if there is almost no business logics between the client and the server side database. In fact, only in this case the server API may be exposed as an OData source only, with no need of more complex communication. 

Meteor,  takes care of sever/client synchronization in a way that is completely transparent to the developer. A similar solution facilitates the coding of simple applications, but may be inadequate for complex business systems that needs to control explicitly communication between client and server.

The Data Moving SPA framework offers retrievalManagers to submit a wide range of (customizable) queries(that includes also OData queries)  to the server, while viewModelUpdatesManagers and updatesManagers take care of synchronizing a generic data structure with the server in a transactional fashion, by taking into account both changes in several Entity Sets (additions, modifications, and deletes), and changes in temporary data structures(core workspaces). As a result of the synchronization process they may return either errors that are automatically dispatched in the right places of the UI, in case of failure, or remote commands that apply modifications to the client side data structure to be synchronized with the server. While the synchronization process is completely automatic, the developer has full control on which data to synchronize, and when to synchronize them, and also the possibility to customize various part of the process.

 

That’s all! This post ends the short series about JavaScript intensive web application. This series is no way a tutorial that describes extensively all details of the techniques that has been discussed, but just a guide on how to select the right technique for each application and on how to solve some architectural issues that are not usually discussed in other places.

 

Stay tuned! 

Francesco

Tags: , ,

Jun 21 2014

New Versions of Mvc Controls Toolkit and Data Moving Controls Suite

Category: Asp.net | Javascript | MVC | WebApiFrancesco @ 21:43

New 3.0.0 release of the Mvc Controls Toolkit. See the list of changes.

New 1.2 release of the Data Moving Controls Suite. See the list of changes.

 

Enjoy!

Francesco

Tags: , , ,

Feb 18 2014

Data Moving Mvc Control Suite Available for Purchase!

Category: WebApi | MVC | JavaScript | Asp.netFrancesco @ 06:54

Finally, the Data Moving Controls Suite is available for purchase! Not only, several powerful asp.net mvc controls easy to configure with a fluent interface and easy to style with your favourite framework (supported: jQuey UI, JQuery Mobile and Twitter Bootstrap), but also, a complete Single Page Application Framework. A sophisticated validation framework that extends the standard server/client asp.net mvc validation framework, the possibility to store control settings to reuse them in several pages, futuristic user interfaces based on Interaction Primitives, and more...

Hurry Up 25% off till 2014-4-31! 

Try to win your license by solving the Triangles Enumeration Problem.

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

Mar 15 2013

Data Moving Plugin Controls

Category: Asp.net | MVC | WebApiFrancesco @ 00:09

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

The Data Moving Plug-in is in its RC stage and will be released to the market within about one month. This is the first of 6 introductory tutorials with associated videos about it.

All Controls provided in the Data Moving Plug-in are compatible with both mouse based devices and with touch devices. Moreover all controls are accessible, and the user can interact with them with the only support of the keyboard.

From a conceptual point of view all controls can be classified into two big categories: Client Controls and Server Controls. Client Controls are bound to client side javascript data and create their Html dynamically on the client side, while the Html of Server Controls is rendered on the server side using data contained in a server side ViewModel.

The main advantages of server controls is that their ”static” Html is visible to the search engines, and that they require less “job” on the client side so they are efficiently rendered also by low performance browsers (such as the browsers of low quality mobile devices). On the other side client controls offer more flexibility, and a better interaction with all other html elements of the page. 

The Data Moving Plug-in contains both type of controls, in order to fit better the developers needs. Typically, server side controls communicate with the server through form submits, or through ajax posts that return new Html. Below a grid that filters, pages,sorts and updates data through form submits:

ServerGrid

Item editing may be performed either on line (on the rows themselves) or through a detail window that appears when the user clicks the edit button that is on the left of each row:

EditWindow

A similar windows is used also to filter data.

All these features come at 0 cost since they require no coding. We just need to specify the services required through a fluent interface:

  1. .DeclareQueryResolver(Html,  m => m.PreviousQuery, enablePrevPage: true,  enablePageSize:true)
  2. .CreateDynamicSubmitRetrievalManager("GridExample.QueryModel.RM", actionUrl: Url.Action("IndexQuery", "GridsDemo"),  newPrefix: "")
  3. .DetailFilterBuilder(out detailBuilder)

DeclareQueryResolver specifies the part of the server side  ViewModel where to store all query information (sorting, filtering, etc). The Data Moving Plugin offers the QueryViewModel<T> pre-defined class for this job but the user may choose to configure a custom class. We need just to add this class to our ViewModel, and the Data Moving Plug-in takes care of everything else. The services required (filtering, paging, sorting) are specified with optional arguments of the call: as default all services are enabled. The third argument of DeclareQueryResolver enables the user choice of the page size, while the second argument requires that the paging engine “remembers” the previous page, so that if a page change fails it can return to the previous page. Once we have enabled paging with DeclareQueryResolver, it is enough to add a pager column to a toolbar to have the a working paging service:

  1. .AddPagerColumn("FullCompletePager")

The name passed as argument to the pager column is a reference to a pager “prototype” we defined in a configuration class.

In fact, in order to simplify controls coding and to allow reusability of code, the developer has the option to store controls “options” in a configuration class by defining both controls “prototypes”, and row “prototypes”. Prototypes, may contain all control or row settings that are not specific for a particular data item.

Since, we required user choice of the page size, the pager will automatically show also a textbox to enter the page size.

The CreateDynamicSubmitRetrievalManager methods gives information on how to send queries to the server. The first parameter is a javascript property where to store the RetrievalManager javascript object that will take care of managing all queries-related communication with the server(this is useful if we would like to customize its behavior on the client side). The second parameter is the url of the action method that processes the query, and finally the last argument specifies a possible prefix to add to the quey ViewModel.  This parameter is useful if the model accepted by the action method is not just a QueryViewModel<T>, but  a more complex model containing QueryViewModel<T>  in one of its properties. In our case (and in most of the cases) the prefix must be empty.

The DetailFilterBuilder method provides an object we may use to render the edit/detail window. For more information about edit/detail windows please refer to Data Moving Plugin Forms: Detail Views, Data Filtering UI, and Undo/Redo Stack.

On the server side the query processing is straightforward:

  1. public ActionResult IndexQuery(QueryViewModel<HumanResource> query) //read-edit simple server grid
  2. {
  3.     prepareData();
  4.     IEnumerable<HumanResource> res = fakeDB.Get();
  5.     if (query != null)
  6.     {
  7.         res = query.ApplyToIEnumerable(res, true, true, pageSize);
  8.     }
  9.     var model = new TrackedGridDemoViewModel { Staff = res.Select(m => new Tracker<HumanResource> { Value = m, OldValue = m }), PreviousQuery = query, ViewSelector = null };
  10.     return View(selectViewTracked(model.ViewSelector), model);
  11. }

PrepareData just retrieve a fake Db (implemented with a dictionary)  stored in the Session. The query received from the client is applied to the fake Table with a single call to ApplyToEnumerable. In the case of an actual DB we must use the method ApplyToQueryable, instead.  Then all data are placed in the page ViewModel.

As you can see all items of the list obtained as result of the execution of the query are enclosed within a Tracker Object. The Tracker object enables Changes Tracking on the grid. There are two kind of server grids: the first one accepts directly a list of Tracker<T> objects and takes care automatically of changes tracking, the other one needs and adapter that adds the needed changes tracking capabilities. Below how to use the adapter:

  1. var h = Html.TrackedListRendering(m => m.Staff);
  2. var dBuilder = h.SimpleGrid(m => m.Values, false, true)
  3. .EnableAlternateStyle()
  4. ...........

As you can see the call to the adapter TrackedListRendering returns an Html helper that we can use to render the grid.

The user can add, delete and modify rows. When he hits the submit button, the whole form the Grid is enclosed in is submitted to the server. Below the Action Method that processes this submit:

  1. //Action methods to process item changes
  2. [HttpPost]
  3. public ActionResult TrackedIndex(TrackedGridDemoViewModel model)
  4. {
  5.     prepareData();
  6.     
  7.     if (ModelState.IsValid)
  8.     {
  9.         ModelState.Clear();
  10.         if (model != null)
  11.         {
  12.             IList<HumanResource> inserts, deletes, modified;
  13.             model.Staff.GetChanges(out inserts, out deletes, out modified, m => { m.Id = Guid.NewGuid(); });
  14.             fakeDB.Insert(inserts); fakeDB.Delete(deletes); fakeDB.Modify(modified);
  15.             QueryViewModel<HumanResource> startQuery = model.PreviousQuery != null ? model.PreviousQuery :  new QueryViewModel<HumanResource>().AddSortCondition(m => m.Surname, OrderType.Ascending);
  16.             model = new TrackedGridDemoViewModel { ViewSelector = model.ViewSelector, Staff = startQuery.ApplyToIEnumerable(fakeDB.Get(), true, true, pageSize).Select(m => new Tracker<HumanResource> { Value = m, OldValue = m }), PreviousQuery = startQuery };
  17.         }
  18.  
  19.     }
  20.     else
  21.     {
  22.         if (model != null && model.PreviousQuery != null)
  23.         {
  24.             model.PreviousQuery.Page = model.PreviousQuery.PreviousPage;
  25.             model.PreviousQuery.SortExpression = model.PreviousQuery.PreviousSortExpression;
  26.         }
  27.     }
  28.     return View(selectViewTracked(model.ViewSelector), model);
  29. }

 

Since we enclosed our items within Tracker objects we are entitled to call the GetChanges method that computes all changes in the IEnumerable items and returns the three lists of all inserted, deleted and modified items. The fourth argument of the GetChanges method, if provided, must be am Action that is applied to all inserted elements. Typically it is used to add a principal key to all Inserted elements (when this is possible before accessing the database).

 

In case we use a Client Side Grid the first step is to move a part of the Server Side ViewModel to the client side, and to get an Html helper that has the capabilities to render client side controls. Both tasks are accomplished with a simple call to the SendToClient method:

  1. @{var h = Html.SendToClient(m => m.ClientModel, "GridDemo.ViewModel");}

The second argument just says where to store the Client Side ViewModel.

Now we may use h to specify all options of our grid and to render it:

  1. var dBuilder = h.SimpleGrid(m => m.StaffToShow, true, true, hasRowDisplayAndRowEdit: true).......

Since our client side grid uses OData for the queries the method calls that enable the query engine are a little bit different:

  1. .CreateStandardQueryResolver(Model.PreviousQuery, enablePrevPage: true, enablePageSize: true)
  2. .StartODataRetrievalManager(false, "GridExample.QueryModel.RM", (uint)Model.PreviousQuery.PageSize, actionUrl: Url.HttpRouteUrl("DefaultApi", new { httproute = "", controller = "GridsDemoApi"}))

In this case we call the CreateStandardQueryReoslver method instead of DeclareQueryReolver because the server side models doesn’t need to contain any object to store query information, since all query handling is done on the client side. Accordingly an instance of QueryViewModel<T> is just created and inserted in the Client Side ViewModel in a way that is completely transparent to the developer. However, in our case we add an instance of QueryViewModel<T> also to the server side ViewModel: this instance of QueryViewModel<T> doesn’t take part to any query processing but we use it just to initialize the client side query by passing it as first parameter to the CreateStandardQueryReoslver.

StartODataRetrievalManager specifies the use of a RetrievalManager that is able to communicate with OData points. This javascript RetrievalManger object is stored in the GridExample.QueryModel.RM property. The first argument of StartODataRetrievalManager  set to false says we dont want to issue an initial query immediately after the page is rendered because the initial data have already been passed in the initial Client Side ViewModel. The third argument just says the initial page size to use for paging: this value will probably change during the grid operation since we enabled user choice of the page size. Finally, the last argument is the url of the WebApi controller that processes the queries.

Client grids are able to send items changes to the server with a simple form submit in exactly the same way a server grid would do, but the preferred way to communicate with the server is by sending data in json format with an ajax call. This job is done by the updatesManager javascript class that takes care of computing changes on the client side and to send Inserted, updated, and deleted items to the server in json format (just the keys of the deleted elements are sent to the server).

No javascript coding is required to use an updatesManager class; we need just to declare it with a fluent interface:

  1. .CreateUpdatesManager("UM").BasicInfos(m => m.Id).IsRoot(Url.HttpRouteUrl("DefaultApi", new { httproute = "", controller = "GridsDemoApi" })).EndUpdatesManager()

 

UM is the name of the property of the Client Side ViewModel where to store the newly created updatesManager class, m => m.Id specifies the principal key of the items. Finally, the IsRoot call specifies that this is a root updatesManager that takes care of “interfacing” directly the controller. In fact, there might be also children updatesManagers that takes care of the children of a one-to-many relation, and that delegate the communication with the server to the updatesManager of the father items. The only argument of IsRoot is the url of the action method that processes the updates. Children updatesManagers are discusses in this video.

The controller receives a standard OpenUpdater<M,K> model where M is the type of the items and K is the type of their principal key. In case the page contains several grids and/or several type of entities the controller may receive a complex model containing several OpenUpdater<M,K> inside different properties and possibly also other data the client needs to send to the server. For more information on complex updates please refer to Single Page Applications 1: Manipulating the Client Side ViewModel.

Processing of the OpenUpdater<M,K> is done quite automatically by the ApiResponseBuilder class that takes care of building a response in the format accepted as response by the updatesManger. This response might contain either validation errors or the keys of the Inserted elements computed by the server. In case of a complex model with several OpenUpdater<M,K> the ApiResponseBuilder is able to build a complex response whose parts will be dispatched adequately once the response reaches the client side. The way ApiResponseBuilder handles complex models is detailed in Single Page Applications 1: Manipulating the Client Side ViewModel.

In our simple case:

  1. public HttpResponseMessage Post(OpenUpdater<HumanResource, Guid?> model)
  2.         {
  3.             prepareData();
  4.             //ModelState.AddModelError("", "test error");
  5.             if (ModelState.IsValid)
  6.             {
  7.                 try
  8.                 {
  9.                     var builder = ApiResponseBuilder.NewResponseBuilder(model, ModelState, true, "Error in client request");
  10.                     builder.Process(m => m, m => m.Id);
  11.                     //call to business layer
  12.  
  13.                     fakeDB.Insert(model.Inserted); fakeDB.Delete(model.Deleted); fakeDB.Modify(model.Modified);
  14.                     //
  15.                     var response = builder.GetResponse();
  16.                     return response.Wrap(this.Request);
  17.                 }
  18.                 catch (Exception ex)
  19.                 {
  20.                     ModelState.AddModelError("", ex.Message);
  21.                     return
  22.                         Request.CreateResponse(System.Net.HttpStatusCode.InternalServerError,new ApiServerErrors(ModelState));
  23.                 }
  24.             }
  25.             else return
  26.                 Request.CreateResponse(System.Net.HttpStatusCode.InternalServerError, new ApiServerErrors(ModelState));
  27.         }

In case there is no error we create an ApiResponseBuilder object with a call to NewResponseBuilder. We pass to this object both, the whole model, the ModelState and a default error message to be used in case of hill formed requests. The third argument set to true, require to throw an exception in case of hill formed requests.

The Process method performs all needed key processing for the newly inserted elements. In our case the processing is simple since we have no one-to-many relations, so we need just to provide principal keys to the newly inserted elements. The first argument selects what OpenUpdater<M,K> to process (this argument is needed to handle also complex models with several OpenUpdater<M,K>); in our case it is trivial: m => m. The second argument selects the principal key of the items contained in the selected OpenUpdater<M,K>. A function that receives as input an item and returns a newly computed principal key for it may be passed as third optional argument. Since we have not provided this function, the default Guid key generator is used.

In case of errors we create an ApiServerErrors object that extracts automatically all errors from the ModelState and put them into an Api response with the Internal Server Error header.

In case the receiving controller is not a WebApi controller but a standard Mvc controller we have to substitute the ApiResponseBuilder class with the ResponseBuilder class and the ApiServerErrors class with the ServerErrors class. Moreover the response must be returned directly with return Json(response), and the errors with return Json(new ServerErrors(ModelState)).

 

The way to style and decide the appearance of both server and client grids is substantially the same. We may specify options with a fluent interface, add toolbars, and  define data rows options.

Both toolbars and data rows are composed of columns that are arranged according to a row template. The developer may choose between three pre-defined row templates:

  1. A <table> based template that may be used in case of tabular data.
  2. A <div style=”display: table”…based template if we need a table like appearance but the data are not “tabular”
  3. A div/float based template.

Moreover, the developer can furnish also its own custom row template, that either arranges the pre-existing column definitions or renders directly the item without taking care of column definitions. There are different types of columns:

  1. Standard columns. Their default template may be controlled with a fluent interface. They are able to render properly any .Net data type both in edit and in display mode. We may choose to use the Mvc Display and Edit templates defined for the given types, or Display only, and TypedTextBoxes based templates that are automatically optimized for each data type. It is also possible to specify the use of the new Html5 inputs; the selection of the input type is done according to the column data type and to a possible DataType attribute that may decorate the property associated to the column.
  2. Image columns. They extracts both the src and the alt of the image from the item data;
  3. Link columns capable of extracting both the href an the link text from the item data.
  4. Text Columns, to render constant text or constant Html.
  5. Button Columns, to render single buttons or buttons organized in bars.
  6. Pager Columns, to display a pager.
  7. RowCount columns, to count rows
  8. Error Column, to display errors associated to the whole row.

 

All columns have standard Header, Footer, edit, Display, Edit Detail, Display Detail,  and Filter templates that can be customized with a fluent interface, but for each of them the developer may provide a custom template.

Below the definition of a toolbar that contains a a button that opens the filter data dialog, and a pager:

  1. .StartToolbar(false)
  2.         .RowContainerLevel(ItemContainerLevel.Row)
  3.         .RowHeight("3em", true)
  4.         .AddButtonToolbar(ShowButtonIn.Both, CommandButtonType.FilterWindow).ColumnWidth(40).EndButtons()
  5.         .AddPagerColumn("FullCompletePager")
  6.         .EndToolBar()

 

Below the definition of a data row:

  1. .AddRowType(true, true)
  2.             .DeclareQueryResolver(Html,  m => m.PreviousQuery, enablePrevPage: true,  enablePageSize:true)
  3.             .CreateDynamicSubmitRetrievalManager("GridExample.QueryModel.RM", actionUrl: Url.Action("IndexQuery", "GridsDemo"),  newPrefix: "")
  4.             .DeclareDisplayStore()
  5.             .DetailFilterBuilder(out detailBuilder)
  6.             .RowContainerLevel(ItemContainerLevel.Row)
  7.             .DeclareInvisibleInnerRowContainer()
  8.             .HtmlTitle(ShowTitleIn.Display | ShowTitleIn.Edit)
  9.             .RowHeight("3em", true)
  10.             .AddRowCountColumn(false, width: 4, widthIsPercentage: true)
  11.             .AddButtonColumn(ShowButtonIn.Both, CommandButtonType.EditWindow, "GridExample.selectItem GridExample.unselectItem").ColumnWidth(4, true).HorizontalAlign(MVCControlsToolkit.Controls.Core.HAlign.center).EndButtons()
  12.             .StartColumn(t => t.Name, width: 46, widthIsPercentage: true).Queries(true, true).HorizontalAlign(MVCControlsToolkit.Controls.Core.HAlign.center).EndColumn()
  13.             .StartColumn(t => t.Surname, width: 46, widthIsPercentage: true).Queries(true, true).HorizontalAlign(MVCControlsToolkit.Controls.Core.HAlign.center).EndColumn()
  14.             .AddColumn(t => t.Address, true)
  15.             .AddImageColumn(t => t.AvatarAlt, t => t.AvatarUrl, new { style = "width: 100px; height: 100px;" }, true)
  16.             .StartColumn(t => t.Id).DeclareHidden().EndColumn()
  17.         .EndRowType();

 

It contains the query related methods we already discussed before, the DeclareDisplayStore() method call that require that the rows will work just in display mode, but that they store fields values in hidden fields, so that the row content can be edited with the help of an edit window.

The HtmlTitle() method requires to add an Html title attribute to all columns with the same content of the column field (useful if the content of the field is not fully visible, but it appears “dotted”). There is a RowCountColumn, and a ButtonColumn that opens an edit row dialog.

Then, we have the Name and Surname field columns. Both of them make the call Queries(true, true), that enables both sorting and filtering on them.

Interesting also the Image column for the avatar. It extracts both the src, and the alt from the item. The last argument set to true specifies that the image column must appear just in the detail window.

Finally, The column for the principal key is declared hidden because we don’t want to show the Id, but just to store it into an hidden field.

The Data Moving Plug-in includes 4 different types of grids(two are server side controls and the other two are client side controls), a TreeGrid/TreeView, and a menu control. The TreeGrid/TreeView working is shown in this video. The menu control may be displayed both horizontally and vertically; it works both with mouse hover or with mouse click (or touch in case of touch devices). Moreover, also all menu items use the Data Moving Plug-in row templates engine we described before for grids.

The Data Moving Plugin contains also a powerful Form Control, with undo/redo capabilities, and a DualSelect to select items from a pre-existing list, that are able to work both as server side controls and as client side controls. Both of them are described in Data Moving Plugin Forms: Detail Views, Data Filtering UI, and Undo/Redo Stack and in its associated video.

Included in the plug-in also powerful knockout bindings for jqPlot and tinyMce. Moreover, tinyMce has been enhanced with a “documents template engine” that give us the possibility to define document templates containing “variables” that can be instantiated either with the content of other input fields or with the content of javascript data structures. This video shows both the enhancements provided to jQplot, and tinyMvce and the documents template engine.

Finally, the developer has access also to all Mvc Controls Toolkit controls, such as TypedTextBox, TypedEditDisplay, DateTimeInput, etc. They can me mixed with all Data Moving Plug-in controls and they can be used also in all template definitions.

Below a video that shows a review of all Data Moving Plug-in Controls:

That’ all for now!

Stay tuned and give a look also to all other Data Moving Plugin introductory tutorials and videos

                      Francesco

Tags: , , , , , ,

Nov 14 2012

Data Moving Plug-in

Category: MVC | Asp.net | WebApiFrancesco @ 01:05

The new “Data Moving” Mvc Controls Toolkit plug-in is next to be released. In this post we will review its main and more interesting feautures.

The “Data Moving” plug-in is a complete set of out-of-the-box controls for Asp.Net Mvc, that can be styled with the jQuery UI styling framework, plus some interaction primitives. It contains, a menu 4 kind of grids a TreeView/TreeGrid and a powerful Mvc/knockout interface for jqPlot, and tinyMce. All Item controls can be configured with a simple fluent interface.

All Items controls have facilities to page, filter and sort data, either on the client side or on the server side, and for sending data to the server  in json format, or with normal (or Mvc ajax) form submits. Data from different controls may be grouped into a single client-side ViewModel before being sent to the server, in a quite automatic and simple way. There is also a Detail Form to be used either alone or to show the details of any item of an items control, and a fully out-of-the-box dual select box. The Detail Form has an undo/redo stack, and all items controls have changes tracking and undo capabilities, so that we can submit to the server just the changes done on all items.

 

However, the more interesting feature of this plug-in is that it allows users to modify complex data structures by dragging and manipulating UI elements. This means that all controls may interact among them and with other Html nodes on the page. Interactions can be triggered either programmatically or by means of Drag and Drop operations. Drag-Drop interactions are defined by declaring DragSources:

  1. public static MvcHtmlString DragSource(this HtmlHelper htmlHelper, string selector, IEnumerable<string> offer=null, DataDragOptions options=null)

 

  1. public static MvcHtmlString DragSourceItems(this HtmlHelper htmlHelper, string rootSelector, string itemsSelector, IEnumerable<string> offer=null, DataDragOptions options = null)

 

and DropSources:

  1. public static MvcHtmlString DropTarget(this HtmlHelper htmlHelper, string selector, IEnumerable<string> accept, DataDropOptions options = null)

 

  1. public static MvcHtmlString DropTargetItems(this HtmlHelper htmlHelper, string rootSelector, string itemsSelector, IEnumerable<string> accept, DataDropOptions options = null)

 

The helpers ending in “Items” are conceived to operate on list of items, where new items can be created. Namely they declare as Drag or Drop targets all Html nodes satisfying the the jQuery selector itemsSelector that are descendants of Html nodes satisfying the jQuery selector rootSelector. Also newly created descendants satisfying itemSelector are immediately Drag or Drop enabled. Their typical application is defining all(…or some, depending on itemsSelector) items of a grid or of a TreeView/TreeGrid as Drag or Drop targets.

offer and accept specify respectively what are the “infos” offered and what are the infos “accepted”. Only if there is a match between these two lists a drag and drop operation is possible. DataDragOptions and DataDropOptions specify several options of the operation, that include both pure graphic effects and processing callbacks(the result of a callback may also force the failure of the operation).

The more important property of DataDropOptions is:

  1. public DropBehaviour Behaviour { get; set; }

where:

  1. public enum DropBehaviour {DataBind=0, DataSync=1, InsertBefore=2, InsertAfter=3, Append=4, Replace=5};
  • DataBind. A DataBind operation is performed between the DragSource and the DopTarget: The content of input fields or complex controls of the DropTarget is filled with the content of the input fields/controls of the DragSource that match them. Input fields and complex controls content are processed in .Net Type aware fashion. The way input fields/controls match is decided according to naming conventions, declarations contained in Html5 data- attributes, and/or string expression contained in the custom Reference knockout binding
  • DataSync. A binding occurs as before, but in this case a bidirectional communication channel is built between the matching elements so that any change performed on one element is immediately reflected on the other ones.
  • InserBefore, InsertAfter, Append, Replace. A new item is created and inserted respectively before or  after the DropTarget, or as last element of the DropTarget siblings, or as a replacement for the DropTarget. After , a binding is performed between the DragSource and the newly created element.
    The way the new item is created depends on some item creation capability that is attached in someway  to the father of the DropTarget and on a selection callback specified in the DataDropOptions. For instance, if the DropTarget is an item of a grid the item is created by using the “create new item” capability of the grid, and if the grid has several items templates the template is chosen with the help of the selection callback. The Data Moving plugin  can take advantage also of the new item creation capabilities of all standard knockout bindings like the template binding.

Some controls have such as the SimpleGrid and the TreeView/TreeGrid have native item moving capabilities that are completely independent from the above Drag-Drop primitives.

The video below shows some of the Drag-Drop/Interaction that we have briefly described. The first example shows Reference binding based interactions that work only with knockout powered  controls and Html nodes. While the second example shows Html5 attributes based interactions.

See this video in High Resolution on the Data Moving Plug-in site

It is worth to say that all operations shown in the video works also on Tablets and mobile phones.

The Drag and Drop operation between a Customer entry and an E-Mail that is described in the previous video, and that results in the creation of a new E-Mail containing all relevant customer information is obtained with the following code:

  1. @Html.DragSourceItems("#Directory", ".directoryEntry", new string[] { "ContactInfos" },
  2.                         new DataDragOptions {
  3.                             DestroyOriginal=true,
  4.                             DestroyCallback = DataDragOptions.RemoveItemFromControl,
  5.                             Opacity = 0.8F
  6.                     
  7.                         })

 

  1. @Html.DropTargetItems("#Messages", ".internal-data-row",
  2.                 new string[] { "ContactInfos" },
  3.                 new DataDropOptions{
  4.                     Behaviour = DropBehaviour.InsertBefore,
  5.                     HoverClass = "HoverTarget",
  6.                     ActiveClass = "AcceptableTarget"
  7.                 })

 

Quite easy! A few lines of simple code, a so powerful effect!

 

This other video shows the TreeView dragging and node-moving capabilities, and how to submit just the changes to the server

See this video in High Resolution on the Data Moving Plug-in site

 

In the above video all changes are submitted in json format by some instances of the updatesManager class. These instances are created automatically by the TreeView with the help of a few information supplied with a fluent interface:

  1. .CreateUpdatesManager<HumanResourcesChangesViewModel>("DepUpdater").BasicInfos(m => m.Id, m => m.Departments).IsRoot(Url.Action("HumanResourcesChanges", "TreeView")).EndUpdatesManager()

 

The above settings define the main updatesManager of the left tree in the example. It takes care of the communication with the action method. They specify just the collection to work with(Departments), its principal key(Id) and the Url of the receiving action method.

  1. .CreateUpdatesManager<HumanResourcesChangesViewModel>("TeamUpdater").BasicInfos(m => m.Id, m => m.Teams).IsNotRoot(m => m.Father, 0).EndUpdatesManager()

 

  1. .CreateUpdatesManager<HumanResourcesChangesViewModel>("PersonUpdater").BasicInfos(m => m.Id, m => m.Persons).IsNotRoot(m => m.Father, 1).EndUpdatesManager()

The last two lines of code define the updatesManagers that takes care of all children entities of the left tree in the example. The children entities changes are inserted in the same ViewModel of the main entities that are in the root of the three so that they are submitted to the same action method of the main entities. Accordingly, the parameter containing the Url of the action method is substituted with a parameter containing row they are children of (0 and 1 in the above examples) and with another parameter containing the external key(Father, for both updatesManagers).

All items controls are  based on the concepts of rows and columns that can be configured exactly in the same way in all item controls. Rows are used both to show data, and to to build headers, footers, title bars and toolbars with the help of specialized columns such as: pager columns, parameters columns, button columns, etc. There are pre-defined buttons based on the jQuery UI styling framework icons, however the user may define custom icons, custom button operations and anything clickable with an Html5 data-operation attribute can be used as a button.

There are standard column templates and each control has its own default row template. Both of them can be configured easily with a fluent interface, but the developer can also override them with templates defined with partial views, Razor helpers, or simple code.

Below an example of a custom column template in the menu control:

CustomColumns

And the code to create it:

  1. .AddRowType()
  2.   .StartTextColumn("", htmlEncode:true).CustomTemplateFixed(TemplateType.Display, @<div id='switcher' class="ui-widget"></div>).EndColumn()
  3.   .AddTextColumn("<div id='themeBlender' style='width: 150px;' ></div>", htmlEncode:false)
  4. .EndRowType()

 

Below an example of custom Row template on the same menu:

CustomRow

And the code to create it:

  1. .AddRowType().CustomRowTemplateFixed(TemplateType.Display,
  2.   @<div class="about-info  ui-state-highlight">
  3.       <p>
  4.           MVCControlsToolkit & Data Moving are developed by Francesco Abbruzzese
  5.           <br />
  6.           @item.LinkFor(m => m.Text, m => m.Text, new { target=item.ViewData.Model.Target, tabindex="0"})
  7.       </p>
  8.       <p>
  9.           Copyright (c) 2010 Francesco Abbruzzese,
  10.           <br />
  11.           <a tabindex='0'  href="mailto:francesco@dotnet-programming.com">francesco@dotnet-programming.com</a>
  12.       </p>
  13.   </div>).EndRowType();

 

Another interesting feauture of DataMoving controls is the possibility to store controls and Row settings giving them a name. This way one can define named control styles that can be reused with completely different  type of datas  by “recalling” all their settings through their names.

For instance, the TreeView control can be shaped in very different ways, to implement also TreeGrids, and “classic” Grids.

Below a classic TreeView:

TreeView

And the code to store these settings with the “StandardTreeFile” name:

  1. h.ClientTreeView(m => m.SampleIEnumerableProperty).
  2.     TreeOptions().TreeViewStyle(TreeViewCssClasses.TreeViewFile).CustomNodeCssClasses(null, false).EndTreeOptions()
  3.     .SetAsDefault(DefaultScope.Named, "StandardTreeFile");

 

This is a TreeGrid implemented with the same control:

TreeGrid

And the code to store these settings:

  1. h.ClientTreeView(m => m.SampleIEnumerableProperty).
  2.     TreeOptions().DisableFatherSelection().CustomNodeCssClasses(null, false).EndTreeOptions()
  3.     .Container(ItemContainerLevel.Column, true, null, null, JQueryUICssClasses.ContentContainer)
  4.     .StartToolbar(false)
  5.     .RowContainerLevel(ItemContainerLevel.Row)
  6.     .AddTextColumn(null, null, 30, false, true)
  7.     .AddButtonToolbar(ShowButtonIn.Both, CommandButtonType.Add, "0 edit")
  8.     .AppendButton(ShowButtonIn.Both, CommandButtonType.GoEdit)
  9.     .AppendButton(ShowButtonIn.Both, CommandButtonType.GoDisplay)
  10.     .AppendButton(ShowButtonIn.Both, CommandButtonType.Delete)
  11.     .AppendButton(ShowButtonIn.Both, CommandButtonType.Undo)
  12.     .AppendButton(ShowButtonIn.Both, CommandButtonType.UndoWithChildren)
  13.     .ColumnWidth(180, false, true).EndButtons()
  14.     .EndToolBar()
  15.     .EnableWidthChange()
  16.     .EnableAlternateStyle()
  17.     .RootClass(TreeViewCssClasses.TreeViewCompact)
  18.     .SetAsDefault(DefaultScope.Named, "StandardTreeGrid");

Finally, a “classic” grid:

ClientGrid

And the code to store the associated settings:

  1. h.ClientTreeView(m => m.SampleIEnumerableProperty).
  2.                 TreeOptions().DisableFatherSelection().DisableTreeModifications().DisableOpenClose().TreeViewStyle(TreeViewCssClasses.TreeViewSimpleGrid).EndTreeOptions()
  3.                 .Container(ItemContainerLevel.Column, true, null, null, JQueryUICssClasses.ContentContainer)
  4.                 .EnableAlternateStyle()
  5.                 .EnableWidthChange(true)
  6.                 .ChildrenContainer(200)
  7.                 .StartToolbar(false)
  8.                 .RowContainerLevel(ItemContainerLevel.Row)
  9.                 .StartParameterColumn(0, 0).CustomColumnClass(GenericCssClasses.NoWrap).EndColumn()//Title
  10.                 .EndToolBar()
  11.                 .StartToolbar(false)
  12.                 .RowContainerLevel(ItemContainerLevel.Row)
  13.                 .AddButtonToolbar(ShowButtonIn.Both, CommandButtonType.Add, "0 edit")
  14.                     .CustomColumnClass(GenericCssClasses.NoWrap)
  15.                     .AppendButton(ShowButtonIn.Both, CommandButtonType.GoEdit)
  16.                     .AppendButton(ShowButtonIn.Both, CommandButtonType.GoDisplay)
  17.                     .AppendButton(ShowButtonIn.Both, CommandButtonType.Delete)
  18.                     .AppendButton(ShowButtonIn.Both, CommandButtonType.Undo)
  19.                     .AppendButton(ShowButtonIn.Both, CommandButtonType.UndoAll)
  20.                 .EndButtons()
  21.                 .EndToolBar()
  22.                 .SetAsDefault(DefaultScope.Named, "StandardClientGrid");

Some controls are server side, that is their Html is created on the server, and other controls are client side, that is their Html is created on the client side with the help of client templates. However, both kind of controls are programmed exactly in the same way, and use the same kind of rows or columns templates. The only difference being that in the case of client side controls the server templates are compiled automatically into client templates by the control.

The SendToClient helper can be used to get a new HtmlHelper capable of generating “Client Side code”(which includes alsoa Client-Side ViewModel).  It works as a normal helper, and there is no difference in the way it is used.

The chart control based on jqPlot and theTreeView are client side controls, there is also a pure server side grid, and a grid called SimpleGrid that is able to render in both ways, that is, it detects the kind of HtmlHelper and behaves accordingly. The SimpleGrid has less out-of-the box feautures but it allows an higher degree of control of the Html that is created.

Also the Detail Form control as all other remaining controls is able to work both as a server side and a client side control. 

All client side controls are based on knockout.js, and the developer have the freedom to mix freely client side and server side controls in the same page.

 

That’s all for now !

Be the first one to build a futuristic application with the DataMoving plugin! Companies wishing to start immediately pioneering projects based on the DataMoving Plugin can contact me for consulence

                                              

                                               Francesco

Tags: , , , ,

Jul 3 2012

Mvc Controls Toolkit Support to Mvc4 WebApi 3: Retrieval Manager and other New Nice Features

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

Mvc Controls Toolkit Support to Mvc4 WebApi

Mvc Controls Toolkit Support to Mvc4 WebApi 2: Handling Relations

In my previous posts about the Mvc4 WebApi support I described how the jQueryable and updatesManager Javascript classes may help us in all tasks related respectively with the retrieval of data from the server, and their updates. The jQueryable helps us in building the query to send to an Api Controller or to any oData source, either with a fluent LinQ like interface or by extracting the query information from the Mvc Controls Toolkit filtering, sorting, and paging controls. However, it doesn’t handle all aspects of the ClientViewModel update once we receive the data from the server. Moreover, there is also some query building job to be done “manually”.

In the 2.2 release of the Mvc Controls Toolkit we factored out all this “manual” jobs into the new mvcct.retrievalManager javascript class. Now all we need to do is just intercepting all events triggered by the Mvc Controls Toolkit filtering, sorting, and paging controls, and passing their data to the event method of the mvcct.retrievalManager instance we have created:

  1. $('#root').bind('queryChanged', function (e, data) {
  2.         ClientToDoView.retrieval.event(data);
  3.     });

Where the retrieval property of our client side ViewModel contains our instance of the mvcct.retrievalManager, and root is the id of a div containing all   filtering, sorting, and paging controls inside it. Since the filtering controls doesn’t trigger any event, we triiger the “filter” event with the refresh button of the forrm:

  1. <input id="Button1" type="button" data-bind='click: function(x, y){$(y.target).trigger("queryChanged", {type: "filter", filterPrefix: "ToDoFilter"});}' value="Refresh" />

The data object of the “queryChanged”  event triggered when the filtering criteria change must have a type of “filter”, and must contain the name of the server side ViewModel property that we use for fitering in its filterPrefix property:

  1. @{var hName = Html.DataFilterClauseFor(
  2.                   m => m.ToDoFilter, f => f.Name,
  3.                   "byNameFilter",
  4.                   MVCControlsToolkit.Linq.FilterCondition.StartsWith);}

As you can see from the code above this name is just ToDoFilter.

The instance of mvcct.retrievalManager can be created in the document ready event:

  1. $(document).ready(function () {
  2.     if (!window['ClientToDoView']) return;
  3.  
  4.     ClientToDoView.retrieval = mvcct.retrievalManager(query, ClientToDoView.DataPage.CurrPage, ClientToDoView.DataPage.TotalPages,
  5.     {
  6.         pageSize: 4,
  7.         entitiesContainer: ClientToDoView.DataPage.ToDoList,
  8.         updatesManager: ClientToDoView['updater'],
  9.         jFormsToClear: $('#mainForm'),
  10.         onError: function (args, x) {
  11.             var exception = $.parseJSON(x.responseText);
  12.             var message = mvcct.utils.isString(exception) ? exception : (exception.Message || "Internal Server Error");
  13.             alert("status code: " + x.status + "; " + message);
  14.         }
  15.     });
  16.  
  17.  
  18.     $('#root').bind('queryChanged', function (e, data) {
  19.         ClientToDoView.retrieval.event(data);
  20.     });
  21.     //populate initial results
  22.     ClientToDoView.DataPage.CurrPage(1);
  23.     ClientToDoView.retrieval.event({
  24.         type: "page",
  25.         page: 1
  26.     })
  27.     
  28. });

Where:

  • query, is the jQueryable object to be used to issue the queries.
  • The second argument is the observable that contains the current page number
  • The third argument is the observable that contains the total number of pages. It can be also null.
  • pageSize, contains the desired page size. This value can be changed at a later time by calling the changePageSize(newPageSize) method.
  • entityContainer, contains the observable where to put the array of objects returned by the server.
  • updatesManager, contains the updatesManager that handles the updates of the same array of objects, if any. If no update is needed, there is no need to pass this argument.
  • jFormsToClear, when specified is a jQuery object that contains some forms to clear from previous errors after having received new data from the server(once new data arrive, orld errors become obsolete).
  • onError, contains the function to execute in case of errors. It accepts 4 arguments: args, x1, x2, x3. args contains the data of the queryChanged event that triggered the request to the server, while x1, x2, x3 are the arguments passed to the error function of the ajax jQuery method.

There are are also other properties of the option object that we have not specified in this example:

  • onSuccess, if specified, overrides the standard model update behaviour of the retrievalManager. It accepts 4 arguments: args, x1, x2, x3. args contains the data of the queryChanged event that triggered the request to the server, while x1, x2, x3 are the arguments passed to the success function of the ajax jQuery method.
  • We can also keep the standard model update behaviour of the retrievalManager while simultaneously executing some custom code after the model has been updated, by specifying the onAfterSuccess function that accepts the same arguments of the onSuccess function. In this case we can specify also the dataTransform function that is passed all items returned by the server, as javascript objects before their properties are turned into ko observables. It is expected to apply a transformation to this data, and then return the array of transformed items.
  • onSubmit, if specified is executed before submitting the request to the server. It accepts two arguments: args, data. args give us the opportunity to cancel the request to the server, by setting args.cancel=true. data is the object we can provide in the data property of the option object.
  • immediateSubmit, has a default value of true. This means that each time a new event is passed to the retrievalManager a new request to the server is issued. If we set this property to false, the request to the server(with the query by processing  all events received so far) is issued manually by calling the submit() method of the retrievalManager.
  • resultsField and countField have respectively default values "Results" and "TotalCount". They are not used if the server returns just an array of objects, but they are used just if the server returns also the total count of the objects satisfying the query(information needed to improve the paging experience). In this case they specify the name of the properties containing the results and the total count. As default ApiControllers doesnt return the total count, but it is quite easy to write an action filter to add this value. Complete oData sources, such as Wcf oData web services return the total count if the query requires it. In such a case the default names suffices

The 2.2 release of the Mvc Controls toolkit improved also the updatesManage. Now there is no need to declare separately a child updatesManager. All information needed can be specified when adding it as a child to its father updatesManager. Below the updatesManagers of my previous posts Mvc Controls Toolkit Support to Mvc4 WebApi 2: Handling Relations rewritten with the new api:

  1. ClientToDoView.updater = mvcct.updatesManager(
  2.         '@Url.RouteUrl("DefaultApi", new { httproute = "", controller = "ToDo"})',
  3.          ClientToDoView,
  4.          'DataPage.ToDoList',
  5.          'id', DestinationViewModel, "ToDoCS");

 

  1. ClientToDoView.childUpdater = ClientToDoView.updater.addChildUpdateManager({ expression: 'Tasks', external: 'FatherId' }, 'Key', "TaskCS");

As you can see, there is no need to create separately, the updatesManager that handles the children objects of our ToDo items. In fact a single instruction declares the the information needed to handle the one to many relation(‘Tasks’ and ‘FatherId’ are respectively the collection of the ToDo item containing the children objects, and the external key of the children objects), together with the key of the children entities (‘Key’) and the property of the destination model that will contains the updates of the children entities(‘TaskCS’) to send to the server. This is enough for the child updater to work properly

The  Mvc4 Client-Filtering -Paging-Sorting-updating file in the download area of the download area of the Mvc Controls toolkit contains both the new and old versions of the examples.

There are also other improvements that enhances the capability of the updatesManager to edit one to many relations. Namely:

  • The move method to move a child object from a father to another. This operation can be undone, as all other insert, update, and delete operations.
  • Now an updatesManager can be declared to be child of itself. This means that now the updatesManager is able to handle a one to may relation of an object set with itself. For instance we can handle a database table of tasks items where each task can be a subtask of another task in a recursive fashion.

 

See how the the controls of upcoming Data Moving plugin can take advantage of the new capabilities of the updatesManager to allow the user to edit one to many relations with the help of sophisticated TreeViews:

Below the updatesManager that handles the updates of the tasks TreeView in the video:

  1. HumanResourcesViewModel.TaskUpdater = mvcct.updatesManager(
  2.         '@Url.Action("TaskChanges", "TreeView")',
  3.          HumanResourcesViewModel,
  4.          'AllTasks',
  5.          'Id');

 

  1. HumanResourcesViewModel.TaskUpdater.addChildUpdateManager({ expression: 'Children', external: 'Father', updater: HumanResourcesViewModel.TaskUpdater });

As you can see the updatesManager is declared child of itself

That’s all for now!

                        Stay Tuned

                       Francesco

Tags: , , , , , , , , ,