Click here to Skip to main content
15,867,141 members
Articles / Web Development / ASP.NET

DaST Concept: A simpler, smarter, and much more powerful alternative to Forms and MVC.

Rate me:
Please Sign up or sign in to vote.
4.65/5 (15 votes)
12 May 2011CPOL35 min read 70.3K   433   28   38
Pure HTML templates and uniform codebehind is the future of web development. The article presents ASP.NET DaST Rendering Engine and its underlying DaST concept that brings web apps architecture to a new level getting rid of all problems and complexity related to standard ASP.NET Forms and MVC.

Abstract

DaST pattern and ASP.NET DaST Rendering Engine is a further development of the ASP.NET Scopes Framework and its underlying server page technology based on fully templated data scope trees which I proposed in my first article in November 2010. From now on the pattern will be called DaST which stands for Data Scope Trees. After working with DaST for a while, my opinion is that this approach opens unmatched web development opportunities and has all chances to completely replace standard ASP.NET Forms and MVC patterns in the near future.

This article consists of 4 parts. First of all, I'll present a new VideoLibrary DEMO application which demonstrates full power of DaST pattern on a real web site. This application will serve as a good how-to sample and a DaST technology demonstrator for all further releases of ASP.NET DaST framework. In the second part I'll give a brief overview of the DaST concept itself and will decompose a VideoLibrary page to a scope tree, so that those who see this technology for the first time could get an idea about the new DaST approach and its advantages. In the third section I'll take a closer look at the demo application back-end architecture and implementation and will highlight all important syntax and design changes that took place since 1st Alpha release in November. This section is targeted only for those, who read my previous article. And in the final section I'll share some framework development plans for the near future.

One more thing :) As many of you criticized my previous article for its length, I'll keep this one short, lean and mean. But if you come across this framework for the first time and you wish to try it in your projects, current article will not be enough and you'll have to read my more detailed and deep Scopes Framework article from November.

Currently, all DaST releated news and events are posted on the DaST development site. Check this site for framework updates and important changes. The latest version of ASP.NET DaST Rendering Engine as well as the VideoLibrary DEMO source code are located in the download section of the site.

!!!UPDATE (May 11, 2011): ASP.NET DaST Rendering Engine project has officially become open-source starting from May 9th! We're looking for open-source developers, so if you feel you can contribute the project, you're welcome to contact the DaST team. DaST open-source information site is here. DaST project console home on SourceForge is here. Public DaST discussion forums are here (anynimous access is enabled)

Contents

VideoLibrary DEMO - Technology Demonstrator

To demonstrate advantages of DaST web development pattern over the standard Forms and MVC approaches, I created a sample application, VideoLibrary, which will serve us as an example, tutorial, and technology demonstrator in the same time. VideoLibrary DEMO is a prototype of a video portal built on ASP.NET DaST framework. Having complex and dynamic UI, this sample application includes most of typical functional elements used to build rich Web 2.0 sites and demonstrates how these functionalities could be achieved using DaST Rendering Engine. Here are some nice and very popular Web 2.0 features that are implemented in VideoLibrary:

  • Numerous partially updated areas with random and complex nesting structure.
  • Delayed loading with partial update to display content that requires data access.
  • Details dialog box with delayed loading of its body content.
  • Numerous jQuery client widgets + jQuery theming

The following are some application screenshots (Fig. 1 and Fig. 2) and brief description of how the application operates on a user level. Although the application does not do much meaningful things by itself and serves mostly as demonstrator, it can be used as a prototype for a similar applications in a real world. Note that Fig. 1 and Fig. 2 show the application in 2 different jQuery UI themes.

Image 1
Fig. 1: VideoLibrary DEMO initial look

The application displays paged selection of a number of video packshots (see Fig. 1). Of course, there are no real videos -- we just use black rectangle with colored text on it. Color of that text defines the category of the video: red category, green category etc. Each packshot has "+" button in the top-right corner. Clicking on it, the video is added to the playlist on the left side and button turns to "-". Clicking "-" removes video from the playlist. On mouse hover, the packshot displays "i" button which invokes the details dialog window.

Image 2
Fig. 2: VideoLibrary DEMO details dialog

The dialog window (see Fig. 2) displays some more details and paged list of video packshots in the same category (i.e. with the same color). Packshots in this list work the same way as in the playlist and the selection grid. The dialog window is tabbed. You can switch to another tab to add video reviews and ratings. Note that all changes you make are saved within your current session only. You can also play with application look-and-feel by selecting your favorite jQuery UI theme in the top-left corner.

All application data comes from data layer simulated by XML file and LINQ. Since this application is a DEMO, I've made it so that all changes you make (add reviews, ratings, playlist, etc) are persisted within the current session only and are discarded when the session ends. On Fig. 3 you can see a part of XML file used as a data source. It's structure is trivial and you can get an idea how VideoLibrary data is organized.

Image 3
Fig. 3: Back-end data XML

Although, beautiful and attractive UI elements is an important part of the application, all the advantages of DaST web development pattern become obvious only when we look at the application back-end code. Being clear, transparent and uniform, the DaST architecture of the VideoLibrary back-end results in unmatched front-end flexibility and allows almost unlimited complexity of the web application UI.

If you wish to run the live test of VideoLibrary DEMO application immediately, you can do this on the DaST Development Site where I'll always deploy the latest version of this application. The entire VideoLibrary DEMO source code is available for download as attachment to this article. I suggest that you get the source code, browse and get familiar with it, as for the rest of this article I'll use this sample code for all my explanations.

DaST Concept Overview

Do you think it is possible to create an ultimate server page rendering engine that outperforms standard Forms and MVC in easiness, architecture, flexibility, performance, presentation separation, and all other important web development attributes? As the answer to this question I'd like to present the ASP.NET DaST Rendering Engine based on a new DaST web development pattern. The name of the pattern stands for Data Scope Trees and you'll understand what this means later. Even if you're an experienced web application programmer or architect, and not familiar with this pattern yet, I promise that the rest of this article will change your idea about the server-page based web application design and development.

The entire DaST concept departs from a very simple thought that every web site in the world is nothing, but a bunch of data produced by server-side logic and presented to the user in the client browser. All you see on a web site is just some data wrapped into HTML text! This means that the entire page rendering process can be viewed as two separate steps: 1) generation of some data values and 2) presentation of these data values in the form of HTML to the client.

To accomplish data generation on the first step we need some kind of codebehind class. Speaking abstractly, the output of such class would be a set of string values. In DaST framework this class is called a controller (like in MVC). The second rendering step is more interesting. After all string values are generated by the controller, we need to mix them with some markup and give the HTML output to the client. Sounds simple, but how to accomplish this technically? Every experienced developer knows how standard Forms or MVC platforms render their pages by going though the complicated and resource consuming page lifecycle, rendering multiple individual server controls, applying master pages, calling data binding implementations for repeating controls, processing events, etc. etc. This process is quite complex and we will try to approach the rendering task from another side. In order to render our prepared data into HTML, we will just take a complete and valid HTML template and insert the prepared string values into this template at the right spots. The spots where data values get inserted are marked with special placeholders which are replaced on rendering stage by real data values. And this is it! We just need a pure HTML template and nothing else -- no server controls, no page lifecycle, no data binding -- we just dumped all that complexity at once!

Look at our demo application on Fig. 1. Now imagine if we had a list of prepared data values (like all those names, descriptions, page numbers, etc) and a complete HTML template with placeholders at the locations where values are to be inserted. What would our rendering process be? Well, the entire rendering would be reduced to a trivial search-and-replace operation for substituting placeholders by the real values. And this is basically all the DaST does to render your pages!

Data Scope Trees

Of course, I abstract away some technical details by just saying that a bunch of data gets applied to the template. In reality, this process has to be more granular, because we also need some way to manage our data values so that they get applied at the right location only. Having placeholders is not enough, because we simply don't want to produce all data values at once and, moreover, we may not be able to. It's much better to, first, produce values for one area of the page, then for another, and so on. Plus we need some way to provide data for repeated content that can be nested in another repeater at any level. And what about Ajax and partial page updates? Thinking about all these requirements I finally came to the idea of the data scopes and data scope trees which became the core of the DaST pattern (hence, the pattern name) and the framework based on it.

Data scope is simply a group of cohesive data values. Cohesion criteria is chosen by the developer. Data scope can be applied to the specific area of an HTML template, meaning that placeholders in that area are replaced by the corresponding data values from data scope. As a result, we get the final HTML output for this specific piece of the template. Extending this technique for the entire page, we can have multiple data scopes applied to all of the areas of HTML template to render the template completely and output the page to the client. Data scopes can also be nested to form hierarchical structure called data scope tree. And finally, I assert the every page of any complexity can be viewed as an HTML template with a data scope tree applied to it. Voila! :)

So, first thing that a web developer must accomplish is to decompose the application UI to a set of randomly nested data scopes. From my own experience, choosing the right data scope nesting structure requires some DaST specific skills that come after several exercises. Let's do this for a part of our demo application UI shown on Fig. 1. The resulted data scopes are shown on Fig. 4.

Image 4
Fig. 4: VideoLibrary partial UI decomposition into data scopes

First of all, there is always a root scope (NULL scope) that serve as a parent of all other scopes. Then we have PageSizeRepeater and SiteThemeRepeater to output items for page size and for jQuery theme drop-down lists (see Fig. 1). Next we have several other data scopes on the left side to form playlist UI. PlaylistHeader scope shows current pager info. PlaylistPager scope wraps a playlist item pager. PlaylistRepeater is needed to display multiple video packshots represented by a nested VideoItem scope. Each VideoItem scope has 2 more nested scopes: AddButton and RemoveButton. The idea is to show only AddButton scope when item is not in the playlist, and only RemoveButton scope otherwise. Next, on the right side of the page we have a video item selection grid. Structure for this grid is absolutely the same as for playlist except that data scopes on the same level have different names. Now, if we depict our nested data scopes on Fig. 4 as tree, we will get a data scope tree identifying our page. A complete data scope tree for the VideoLibrary application is shown on Fig. 5. Upper part of the tree corresponds to UI on Fig. 1 which we have just decomposed. The bottom subtree coming from DetailsPopup scope corresponds to UI on Fig. 2. Data scopes with attached controllers have red backgrounds and this will be explained later.

Image 5
Fig. 5: VideoLibrary complete data scope tree

Data Scopes in Template

Ok, after we logically decomposed the UI into data scopes, next question is how to point the specific data scope at the specific area in the template. The solution here is straightforward. Since our data scope tree is built so that it represents the logical structure of the document, we just use the same structure in the HTML template by wrapping corresponding areas into valid DIV container elements representing boundaries within which data scopes are are applied. This means, that these containers in the HTML template have the same hierarchical structure defined by the data scope tree. Each data scopeis identified by its name e.g. "PlaylistPager" or "VideoItem". The same name should be specified in the scope attribute of the corresponding HTML container element. So, the system does not care how complex your template is is and what type of markup it has -- it only cares about nested scope DIVs i.e. the data scope tree inside this template. For example, if we wanted to create a valid template for data scope tree on Fig. 5, a part of this template could look like the markup on Listing 6.

Listing 6: Possible part of valid template
your markup here ...
<div scope="PageSizeRepeater">your markup here ...</div>
<div scope="SiteThemeRepeater">your markup here ...</div>
<div scope="PlaylistHeader">your markup here ...</div>
<div scope="PlaylistPager"></div>
<div scope="PlaylistRepeater">
  your markup here ...
  <div scope="VideoItem"></div>
  your markup here ...
</div>
rest of template follows ...

NOTE that using only DIVs for scope containers is a limitation of the current version of DaST Rendering Engine and will go away in one of the next versions of the framework.

Controller Overview and Data Binding

To generate real data for template the developer has to implement a controller class. The process of generating data for the corresponding template I call data binding. For every data scope in the tree and the corresponding scope DIV in the template the controller contains a data binding function called binding handler. The output of a binding handler is basically a set of values for a current data scope.

In the simplest case, there is a single controller responsible for generating data for all data scopes in the scope tree. In the normal case, there are multiple controllers and each one of them is responsible only for a part of a scope tree. When a controller is attached to a data scope, it becomes responsible for generating data for this scope and all child scopes in the subtree unless those child scopes have their own controllers attached.

Look at Fig. 5 again -- data scopes on the red background are the ones that have controllers attached. There is always at least one controller attached to a NULL scope called a root controller. Other controllers are child controllers. Just like user controls in Forms or partial views in MVC, child controllers in DaST should be used whenever it is appropriate to factor out a certain common piece functionality. One of the examples in our demo application is PagerController attached to PlaylistPager, VideoItemPager, AlikeVideosPager, and CommentsPager data scopes (see Fig. 5).

Rendering Process

Now I will redefine the DaST rendering process in terms of controllers and binding handlers. To render the entire page, the framework starts from generating data for the NULL data scope in the tree by invoking the corresponding binding handler in the root controller and recursively repeats this operation for all data scopes in the tree invoking appropriate handlers on responsible controllers. There is always a specific and consistent order in which data scopes are processed. In terms of tree walking algorithms, the framework uses post-order walk with top-to-bottom in-order traversal. Recalling graphs from high school, it's actually called right-to-left in order traversal, but because it is more appropriate to depict scope tree horizontally (like I did on Fig. 5), I call it top-to-bottom. Such traversal order is chosen, because the actual scope DIV tags in HTML template are encountered exactly in this order.

So, all the developer needs to do to render a page using DaST Rendering Engine is to 1) create an HTML template similar to the one on Listing 6 and then 2) implement a controller for it (or multiple controllers for multiple templates). Listing 7 shows a high-level outline for the root controller (VideoLibraryController.cs) of our demo application. This root controller is the one attached to the NULL scope on Fig. 5. Binding handlers producing values for corresponding scopes are on lines 87-193. Names of these handler functions are chosen by the developer. On line 10 you see the SetTemplate() function - that's how we tell the controller which template to use. SetModel() function on line 15 is needed to assign binding handlers or child controllers to data scopes in the tree. Finally, lines 45-73 are action handlers invoked in response to user actions (think of these as a counterpart of events).

Listing 7: VideoLibrary root controller outline
C#
  8    public class VideoLibraryController : ScopeController
  9    {
 10      public override void SetTemplate(SetTemplateArgs template) { ... }
 14    
 15      public override void SetupModel(ControllerModelBuilder model) { ... }
 43
 44
 45      private void Action_PageSizeChanged(ActionArgs args) { ... }
 58
 59      private void PlaylistPager_PageChanged(ActionArgs args) { ... }
 65
 66      private void VideoItemPager_PageChanged(ActionArgs args) { ... }
 72
 73      private void VideoItem_PlaylistUpdated(ActionArgs args) { ... }
 85
 86
 87      private void ROOT_DataBind() { ... }
104
105      private void PageSizeRepeater_DataBind() { ... }
118
119      private void SiteThemeRepeater_DataBind() { ... }
130
131      private void PlaylistHeader_DataBind() { ... }
165
166      private void PlaylistRepeater_DataBind() { ... }
181
182      private void VideoItemGridInfo_DataBind() { ... }
192
193      private void VideoItemRepeater_DataBind() { ... }
208    }

Actions and AJAX

A huge advantage of data scope trees is that they allow for the most clear definition of partial page updates and the simplest usage of AJAX capabilities in web applications comparing to all other existing frameworks. The underlying idea of DaST Ajax is as simple as everything else in DaST -- every scope in data scope tree can be individually refreshed at any time during the async postback! When data scope is refreshed, all its child data scopes in the scope tree are refreshed too. In terms of data binding this means that the system re-invokes binding handlers corresponding to the refreshed scopes to regenerate data values, update parts of the template, and output them back to the client. Data scope tree traversal order is kept on the postback, maintaining the consistent order of binding handler invocation.

Next, async postbacks do not occur randomly, but in response to some events in the browser i.g. button clicks, timer ticks, etc. DaST Rendering Engine provides a simple and lightweight mechanism allowing to raise events on the client side, and handle those events on the postback inside the controller classes. In DaST a counterpart of event is called action. Each action has a name and a string argument. This is not a limitation, because string argument can contain just any possible JSON-serializable object. When action occurs, async postback comes to the controller and appropriate action handler is executed (see lines 45-73 on Listing 7). Action handler is the place where we can do some processing and instruct the specific data scopes to refresh.

DaST Pattern Résumé

OK, at this point you should have a clear top-level picture of what the DaST pattern and DaST Rendering Engine actually are and how they differ from other patterns and frameworks available to the developers today. Below I will summarize the most obvious benefits of using DaST pattern in your web applications.

  • Maximum possible level of separation between presentation and back-end code.
  • As the result of the above, outstanding application back-end testability and easiness of TDD.
  • Usage of W3C compliant pure HTML templates to control every single byte of the UI markup.
  • As the result of the above, unmatched UI flexibility, maintainability, and simplicity in the same time. Instead of delving into markup of server controls, you work with trivial HTML!
  • Ability to change your page layout and look-and-feel entirely in 10 minutes by playing with HTML markup in the template. Obviously, this DOES NOT require any site recompilation!
  • Minimum framework learning period. No need to read 400 page books to start creating highly dynamic and complex sites -- all you need to know is how to decompose your UI into i>scope trees and how to implement action and binding handlers inside the controller classes.
  • All back-end design becomes simple, clear, and uniform. No matter how complex your application UI is, no matter how big your data scope tree is, in the back-end it will always be the same -- just a controller with a list of action and binding handlers!
  • No page lifecycle. No server controls. No more wondering how to make the control render this way or that way -- you HTML template and binding handlers control your rendering process entirely from the beginning to the end.
  • Native support of AJAX partial update without single line of code or additional markup. Just compare to update panels in Forms :)))
  • Binding handlers are re-invoked on the postback ONLY for the refreshed part of the page meaning that your code is executed ONLY when it needs to! Again, compare to Forms, where a page goes through the entire lifecycle executing tons of unnecessary code.
  • DaST does not deny any of standard Forms features, but rather compliments them. Our favorite session management, application cache, authentication classes, etc. are still available within action and binding handlers inside the controllers. Standard Forms based pages can coexist with DaST based pages is one application allowing smooth step-by-step transition from Forms to DaST.
  • Summarizing all this, and based on my own experience, application development time decreases by up to 90% (not a joke).

Main purpose of the current article is to give an overview of important changes in the new version of the framework. If you see this concept for the first time and want to learn in details how to create HTML templates and implement controllers, you'll have to read this article which is much more deep and detailed. Or you can wait until documentation and tutorial becomes available on DaST dev site at http://www.rgubarenko.net, but I can't promise any dates.

Important Framework Changes. VideoLibrary Back-End.

In this section I'll quickly go through all the important syntax and design changes since the previous version of the framework. I'll not delve into much back-end details, because the entire mechanism has already been described in my previous article in November, I'll only highlight the differences for those who read that article and who is already familiar with the Scopes Framework. So, the following sub-sections is just a list of differences between 2 versions.

1) Binary and namespace

Changes happened to the framework binary and namespace are the following:

  • Name of Name of the framework DLL is changed to AspNetDaST.dll (located in demo site Bin folder).
  • All public classes used by the developers are located in AspNetDaST.Web namespace.

2) ScopesManagerControl is gone

In Scopes Framework I used a temporary solution that we had to add a ScopesManagerControl to an ASPX page to make it work with the framework. Now this is gone. In the DaST Rendering Engine we inherit pages from DaSTPage class from AspNetDaST.Web namespace and implement only one ProvideController() method. You dont have to add any markup to the ASPX page -- DaSTPage parent class takes care of it. Listing 8 shows the listing of VideoLibrary.aspx.cs codebehind class. Everything should be clear: we instanciate the root controller on line 14 and enable partial updates on line 15. Note that currently, EnablePartialUpdates must always be set to TRUE. This is due to the fact that I did not decide yet if we need to support full postback or not. In Forms the reason to make async postbacks optional was that UpdatePanel controls add significant complexity and performance overhead. In DaST, partial refreshes are native and I don't see any reason to make them optional.

Listing 8: VideoLibrary.aspx.cs listing
C#
 8    using AspNetDaST.Web;
 9
10    public partial class VideoLibrary : DaSTPage
11    {
12      protected override void ProvideController(PageSetup setup)
13      {
14        setup.RootController = new VideoLibraryController();
15        setup.EnablePartialUpdates = true;
16      }
17    }

3) Data binding changes

As you may notice on Listing 7, arguments in binding handlers are eliminated. Handler argument was used to add placeholder replacements for the current scope. From now on, placeholder replacements are added directly to the scope instance which you can access using CurrentPath and ControlPath tree navigators. The meaning of these rendered scope tree navigators has also slightly changed:

  • ControlPath used inside action or binding handler always points the scope to which current controller is attached.
  • CurrentPath used inside binding handler points to the currently bound scope. CurrentPath used inside action handler points to the scope specified in client-side JavaScript Action(..) call as target scope.

Just look at Listing 9 that shows one of binding handlers from DetailsPopupController.cs class. On lines 202-203 instead of calling Replace(..) on the binder object passed as a parameter like it was before, we call Replace(..) directly on the scope instance, using CurrentPath that points to the current ReviewsInfo scope.

Listing 9: Some of binding handlers in DetailsPopupController controller.
C#
195    private void ReviewsInfo_DataBind()
196    {
197      decimal rating = CurrentPath.Scope.Param<decimal>("Rating");
198      int commCount = CurrentPath.Scope.Param<int>("CommCount");
199
200      CurrentPath.Scope.ShowConditionArea("comm-empty:yes", commCount <= 0);
201      CurrentPath.Scope.ShowConditionArea("comm-empty:no", commCount > 0);
202      CurrentPath.Scope.Replace("{Rating}", rating.ToString("0.0"));
203      CurrentPath.Scope.Replace("{CommCount}", commCount);
204
205      CurrentPath.Rew(1).Fwd("ReviewsInfo2").Scope.ShowConditionArea("comm-empty:yes", commCount <= 0);
206      CurrentPath.Rew(1).Fwd("ReviewsInfo2").Scope.ShowConditionArea("comm-empty:no", commCount > 0);
207      CurrentPath.Rew(1).Fwd("ReviewsInfo2").Scope.Replace("{Rating}", rating.ToString("0.0"));
208      CurrentPath.Rew(1).Fwd("ReviewsInfo2").Scope.Replace("{CommCount}", commCount);
209    }

At the first look it may seem that data binding syntax became more complex, but actually this design gives us a huge programming benefit, because now we are able to databind scopes from inside the binding handlers of other scopes. This is great, because if a scope is simple and contains just a couple of values, why would I flood my controller class creating a separate binding handler for it? This new feature is used on lines 207-208 of DetailsPopupController class (see Listing 9) where we use navigator to point at another ReviewsInfo2 scope and add same placeholder replacements to it.

4) Working with scope instance

In the new version of DaST Rendering Engine all framework calls became more uniform. Now all methods and properties related to manipulation with the specific scope are accessible through the scope instance object. Data binding functions (some of them I already talked about in the previous section) is one group of such methods. Another group of methods provide operations on scope context parameters. Diagram on Fig. 10 shows public interface of RenderedScopeInstance class.

Image 6
Fig. 10: Public interface of RenderedScopeInstance class.

Below is a brief explanation of all class members. Most of them should be well familiar to you from Scopes Framework. Even though the names are changed, the meaning is still the same. Members marked "(NEW)" are the ones that were added in the current version of the framework.

  • RenderTyRenderType (NEW) - gets or sets the type of scope rendering. Explained later.
  • ScopeClientID - id of the scope container as you see it on a rendered page.
  • HasParam - indicates if parameter with specified name is already set.
  • InitParam (NEW) - sets parameter if only it's not set yet.
  • Method (NEW) - calls method in scope context. Explained later.
  • Param (NParam (NEW) - gets strongly typed parameter.
  • Refresh - causes partial update on the target scope.
  • Repeat - repeats content of the current scope.
  • Replace, ReplaceRange - adds placeholder replacements.
  • RestartRepeater (NEW) - resets content repeating to 0. Explained later.
  • SetParamSetParam, SetParams - set single or range of parameters for target scope. Parameter can be any object serializable to JSON.
  • ShowConditionArea (NEW) - beatiful feature allowing direct manipulation of the scope markup. Explained later.

5) Scope content repeating

In DaST framework content of each scope is output one time by default meaning that the resulting HTML fragment is just whatever this scope container has in the template. In order to repeat scope content, we should call Repeat(..) function on the target scope. We also have a RestartRepeater(..) method (see Fig. 10) that resets repeating count to 0. If this method is called and is not followed by calls to Repeat(..), the scope will not output any content. Listing 11 shows how PlaylistRepeater scope outputs its list of video packshots. First, on lines 168-170 we retrieve pager values from PlaylistPager scope to which PagerController is attached. Then we get list of video items from simulated data layer. Then, on line 173, we have a call to RestartRepeater(..) and, finally, on lines 174-179 we simply loop through the list of objects calling Repeat(..) on each run and passing the video item object to VideoItem scope with VideoItemController attached to it.

Listing 11: PlaylistRepeater scope binding in VideoLibraryController class.
C#
166    private void PlaylistRepeater_DataBind()
167    {
168      int startItemIdx = CurrentPath.Rew(1).Fwd("PlaylistPager").Scope.Param<int>("StartItemIdx");
169      int pageSize = CurrentPath.Rew(1).Fwd("PlaylistPager").Scope.Param<int>("PageSize");
170      int itemTotalCount = CurrentPath.Rew(1).Fwd("PlaylistPager").Scope.Param<int>("ItemTotalCount");
171
172      object[] videoItems = DataLayer.GetPlayItems(startItemIdx, pageSize);
173      CurrentPath.Scope.RestartRepeater();
174      for (int i = 0; i < videoItems.Length; i++)
175      {
176        CurrentPath.Scope.Repeat();
177        CurrentPath.Fwd(i, "VideoItem").Scope.SetParam("ItemIndex", startItemIdx + i);
178        CurrentPath.Fwd(i, "VideoItem").Scope.SetParam("VideoItemObject", videoItems[i]);
179      }
180    }

One more new feature in this version of the framework is that we can save generic objects as scope parameters. On line 178 in listing on Listing 11 we set the entire video item object as a parameter for the VideoItem scope. The system accepts any object as scope parameter as long as this object is serializable to JSON i.e. can be saved to a simple string.

6) Scope visibility

In the previous version of the framework a data scope could be made invisible meaning that output of this scope was an empty string. In the current version each data scope has more visibility options and this is set using RenderType property of the scope instance (see Fig. 10). This property is of enumeration type and its possible values are summarized below:

  • Normal - scope is rendered normally.
  • Empty - scope is rendered as a container only i.e. the output of such scope is an empty DIV container. Binding handlers for this scope and all scopes in a subtree are NOT called. Parameters for this scope ARE persisted. Parameters for scopes in a subtree are NOT persisted.
  • None - same as Empty, except that it is rendered as an empty string and its parameters are NOT persisted.

Note that when scope is refreshed, its RenderType is automaticaly reset to Normal. Also, it's important to understand that if visibility is set to None, you will not be able to refresh this scope, simply because there is no HTML container that would be able to accept the new content. Finally, you have to plan which scopes in the scope tree should be used to save parameters, because when you set scope visiblity to Empty or None, all its child scope parameters are discarded.

Let's look at the example how delayed loading effect is made on details dialog. Listing 12 shows the most important back-end functions participating in the delayed loading. On the client side, when you click "i" button on any video packshot, jQuery Dialog plugin is used to popup the dialog box. Initially dialog box has only animated loader icon and no other content, because in ROOT_DataBind() method we set DetailsContent scope render type to Empty. This means that none of binding handlers for child scopes of DetailsContent scope are invoked on the initial load. When dialog opens, "LoadContent" action is raised and handled on line 51. Inside this action handler we refresh the DetailsContent scope and set its "VideoID" param to the id passed as action argument from client side. Next, since DetailsContent scope is refreshed, the system re-invokes its binding handler as well as binding handlers of all its child scopes in traversal order. So, DetailsContent_DataBind() on line 170 gets invoked. "VideoID" populated in action handler is retrieved and used inside binding handler to retrieve actual video item and use its values to add placeholder replacements. So, after this binding handler is executed, the system carries updated ouput to the client and DetailsContent scope is updated with real video item info i.e. our delayed loading works exaclty as it should. Notice lines 174 and 175 where we hide CommentsContent and AddCommentScreen to apply the same delayed loading technique to them, but this time within the open dialog box.

Listing 12: Part of DetailsPopupController class.
C#
 51    private void Action_LoadContent(ActionArgs args)
 52    {
 53      string videoID = (string)args.ActionData;
 54  
 55      ControlPath.Fwd("DetailsContent").Scope.Refresh();
 56      ControlPath.Fwd("DetailsContent").Scope.SetParam("VideoID", videoID);
 57    }
...
160    private void ROOT_DataBind()
161    {
162      ControlPath.Fwd("DetailsContent").Scope.RenderType = ScopeRenderType.Empty;
163
164      CurrentPath.Scope.Replace("{DetailsContent_ScopeID}", CurrentPath.Fwd("DetailsContent").Scope.ScopeClientID);
165      CurrentPath.Scope.Replace("{CommentsContent_ScopeID}", CurrentPath.Fwd("DetailsContent", "CommentsContent").Scope.ScopeClientID);
166      CurrentPath.Scope.Replace("{AddCommentScreen_ScopeID}", CurrentPath.Fwd("DetailsContent", "AddCommentScreen").Scope.ScopeClientID);
167      CurrentPath.Scope.Replace("{ErrorDisplay_ScopeID}", CurrentPath.Fwd("DetailsContent", "AddCommentScreen", "ErrorDisplay").Scope.ScopeClientID);
168    }
169  
170    private void DetailsContent_DataBind()
171    {
172      string videoID = CurrentPath.Scope.Param<string>("VideoID");
173   
174      CurrentPath.Fwd("CommentsContent").Scope.RenderType = ScopeRenderType.Empty;
175      CurrentPath.Fwd("AddCommentScreen").Scope.RenderType = ScopeRenderType.Empty;

7) Method handlers

Sometimes ability to set scope parameters is not enough and we wish to call an actual method on the controller to execute certain activities. This feature was added to a new version of the framework. Listing 13 shows how UpdatePagerValues(..) method is registered for PagerController class. In the future I may come up with something more intelligent, but in the current version, a valid method has to take single object parameter and return object result. On line 110 we have the UpdatePagerValues(..) private method. It's not a problem that there is only one parameter allowed, because I can always pass a JSON object graph and get separate values from it. On line 25 the method handler is registered so that it becomes callable from other controllers.

Listing 13: Registering method in PagerController class.
C#
 18  public override void SetupModel(ControllerModelBuilder model)
 19  {
 20    model.SetDataBind(new DataBindHandler(ROOT_DataBind));
 21
 22    model.HandleAction("NextPage", new ActionHandler(Action_NextPage));
 23    model.HandleAction("PrevPage", new ActionHandler(Action_PrevPage));
 24
 25    model.RegisterMethod("UpdatePagerValues", new MethodHandler(UpdatePagerValues));
 26  }
...
110  private object UpdatePagerValues(object jsonData)
111  {
112    int startItemIdx, pageSize, itemTotalCount;
113    if (DaSTUtils.HasValue("StartItemIdx", jsonData)) startItemIdx = (int)DaSTUtils.GetValue("StartItemIdx", jsonData);
114    else startItemIdx = ControlPath.Scope.Param<int>("StartItemIdx", 0);
115    if (DaSTUtils.HasValue("PageSize", jsonData)) pageSize = (int)DaSTUtils.GetValue("PageSize", jsonData);
116    else pageSize = ControlPath.Scope.Param<int>("PageSize", 1);
117    if (DaSTUtils.HasValue("ItemTotalCount", jsonData)) itemTotalCount = (int)DaSTUtils.GetValue("ItemTotalCount", jsonData);
118    else itemTotalCount = ControlPath.Scope.Param<int>("ItemTotalCount", 0);
119
120    startItemIdx = Math.Min(startItemIdx, itemTotalCount - 1);
121    startItemIdx = ((int)(startItemIdx / pageSize)) * pageSize;
122    
123    // set recalculated values
124    ControlPath.Scope.SetParam("StartItemIdx", startItemIdx);
125    ControlPath.Scope.SetParam("PageSize", pageSize);
126    ControlPath.Scope.SetParam("ItemTotalCount", itemTotalCount);
127
128    return null;
129  }

Next, Listing 14 shows how the method is invoked from an action handler of VideoLibraryController. On line 77 we, first, point to the PlaylistPager scope which has a PagerController attached and call Method(..) function (see Fig. 10) to invoke previously registered method by name. Notice how I pass the generic object as method parameter.

Listing 14: Invoking PagerController method from inside of VideoLibraryController.
C#
73    private void VideoItem_PlaylistUpdated(ActionArgs args)
74    {
75      // pass total items count to parent scope
76      int itemTotalCount = DataLayer.GetPlayItemCount();
77      ControlPath.Fwd("PlaylistPager").Scope.Method(
78        "UpdatePagerValues", new { ItemTotalCount = itemTotalCount });
79  
80      ControlPath.Fwd("PlaylistHeader").Scope.Refresh();
81      ControlPath.Fwd("PlaylistPager").Scope.Refresh();
82      ControlPath.Fwd("PlaylistRepeater").Scope.Refresh();
83
84      ControlPath.Fwd("VideoItemRepeater").Scope.Refresh();
85    }

The first question that rises here is why do we use all this method registering technique and dont just expose a public interface on the controller and call functions directly? Well, this should never be done in DaST, because, recall, that there is always a single instance of the controller per data scope which is reused for all repeated scope instances. So, calling controller methods through registration mechanism we simply allow the system to maintain the right context on the target controller instance.

8) Direct template manipulation

This is one of my favourite features in the new DaST framework. So far we modified scope content only by replacing placeholders with real values. If we needed to hide or show some areas depending on certain condition, we could enclose these areas into actual scopes and use RenderType property for hiding and showing these scopes. But the problem is that we might need multiple conditional areas and creating separate scopes for each of them would become messy in the template and would flood the back-end controller with tons of unwanted trivial code for hiding, showing, and refreshing these data scopes. And the solution here is to allow direct scope markup manipulation which beautifully complements already existing placeholder replacements. First manipulation that I've added is ShowConditionArea(..) function (see Fig. 10) to show or hide a part of the scope markup.

For example, ReviewsInfo scope (see Fig. 5) is responsible for the text displayed in the title of the second tab of the details dialog (see Fig. 2). When there are comments for the current video, the text should say "XX user review(s)". If there are no comments, the text will be "0 user reviews(s)". But what if I wish to display more user friendly message instead, something like "No user reviews yet"? Now the answer is simple - use the conditional area! Listing 15 shows part of the template with ReviewsInfo scope. To create a conditional area, markup must be enclosed between <!--showfrom:condition_name--> and <!--showstop:condition_name-->. This special format is recognized by the system during rendering and processed accordingly. On Listing 15 we have 2 conditional areas with conditions comm-empty:yes and comm-empty:no meaning that comments are empty and not empty respectively.

Listing 15: Part of DetailsPopupTemplate.htm showing ReviewsInfo scope.
 4    <div class="screenContent nested0" scope="DetailsContent">
 5      <div id="tabs">
 6        <ul>
 7          <li><a href="#tabs-1">Video Details</a></li>
 8          <li>
 9            <a href="#tabs-2"><span>
10              <div style="display: inline;" scope="ReviewsInfo">
11                <!--showfrom:comm-empty:yes-->
12                No user reviews yet
13                <!--showstop:comm-empty:yes-->
14                <!--showfrom:comm-empty:no-->
15                {CommCount} user review(s)
16                <!--showstop:comm-empty:no-->
17              </div></span>
18            </a>
19          </li>
20        </ul>

After template is ready, we just need to call ShowConditionArea(..) on the ReviewsInfo scope to show or hide areas depending on the condition. Look how this is done at lines 200-201 in the code listing on Listing 9. On line 200 we instruct the rendering engine to show comm-empty:yes area only when commCount <= 0 i.e. when there are no comments. Now imagine how flexible your UI can be having this small tool. You can have multiple conditional areas, nest them into each other for conditional AND, or put them beside each other for conditional OR! Just like placeholder replacements, this manipulation can be applied to repeated content. Also remember, that DaST applies template transformations in the same order they were called in the controller class. I.e. if you first add some placeholder replacements and then apply conditional areas, the order of transformations will be kept during rendering. Using this amazing tool we must keep in mind that conditional areas, obviously, cannot have nested data scopes. In the next version of the framework I plan to add more useful direct template transformations.

9) DaSTUtils class

I've added DaSTUtils class containing a set of utility functions simplifying some common programming tasks. This utility was used in listing on Listing 13 to retrieve values from generic object passed as a parameter to UpdatePagerValues(..) function. DaSTUtils class diagram is shown on Fig. 16.

Image 7
Fig. 16: DaSTUtils class.

Brief summary of the functions is below:

  • GetValue - retrieves public property with specified name from the object.
  • HasValue - checks if the object has a public property with specified name.
  • ParseJSON - parses JSON string and returns object graph.

Further Development Plans

Although, current version of the DaST Rendering Engine is still Beta, it's pretty stable and you can start playing with it and using it in your projects (obvioulsly, no warranty -- see copyright). I can't promise anything about official documentation -- most likely we will have to wait for several months. Overall architecture of the platform is complete and I dont expect any major design changes; however, I'll be refactoring certain parts of the framework and adding more features to it. Most significant DaST developments that will take place in the near future are summarized below.

  • Clean exceptions, troubleshooting utils, etc.

    First thing needed is clean and uniform exceptions handling with meaningful messages to help finding a problem. I'll create a new exception type thrown for all developer faults. We also need some way to view model scope tree information at any time. This is very useful for troubleshooting and testing. By design, the scope tree data structure is not exposed to outside and this is not going to change, but I'll add something like ToXml(..) method or similar so that the developer will always be able to see how his changes impact the internal data structures.

  • More direct template manipulation features.

    In addition to placeholder replacements and conditional areas I plan to add the ability to repeat parts of the resulting fragment. This means that right inside the data scope we will be able to create repeaters and mix them with conditional areas. This feature is extremely important and useful, because we might not want to create separate data scopes for repeaters and grids. I'm sure the developers will really appreciate outstanding flexibility allowed by direct template manipulation in DaST applications.

  • Transfer scope DIV attributes to initial scope parameters.

    I was planning to do this for the current version, but simply forgot. It happens :) Like standard Forms allow parametrizing controls by specifying their properties right inside the ASPX file, DaST framework will allow parametrizing data scopes. Inside the template you'll specify scope DIV tag with a set of attributes. All atributes starting from underscore "_" will become scope parameters and will be available to you through the scope instance. Underscore is needed so that scope params do not overlap with standard HTML attributes.

  • Allow various scope containers.

    I have to check everything first, but it seems to me that there is no problem to allow scope containers in the templates to be not just DIV tags, but all suitable container tags i.e. those that have innerHtml property. This is needed, because, some apps have to have strict W3C compliance meaning that, for example, DIVs cannot be put inside SPANs which creates certain limitations for placing DIV scopes in the templates. Allowing data scopes to use various containers solve this problem completely.

  • Moving from ASP.NET Ajax to jQuery Ajax implementation.

    This, I think, is the most significant change that will involve major development. Current partial update implementation in DaST Rendering Engine is totally based on the standard ASP.NET Ajax Library. The implementation itself is very simple, partial update engine is quite stable, and I was able to merge DaST partial updates into the standard AJAX in a very elegant way, which sounds pretty good. The only problem is that Microsoft's client AJAX Library is huge and 90% of its code becomes useless if your application uses DaST framework. So, my opinion is that in a good production version such overhead is unacceptable and must be eliminated. This basically means that in the future the entire AJAX library will have to be rewritten to suite the needs of DaST developers. The solution that seems attractive to me is to use jQuery Ajax implementaion, but I'm not sure that this is the best solution. I might also end up with clear JavaScript implementation without any 3rd party libraries.

And this is it. I invite everyone to visit DaST dev site frequently and watch for news and framework updates. We're still setting up an open-source project environment and I'll post the news about it whenever anything is ready. I apologize for the delays -- I was really busy during last several months with new interesting projects on my primary job. But since all architectural work is complete, from now on I'll be updating the framework very frequenly adding more features to it. The official release of the DaST Rendering Engine will be in a couple of months, but the current Beta version is fully functional and stable to be used in your projects.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Canada Canada
Software Architect with over 15 years in IT field. Started with deep math and C++ Computer Vision software. Currently in .NET and PHP web development. Creator of DaST pattern, open-source frameworks, and plugins. Interested in cutting Edge IT, open-source, Web 2.0, .NET, MVC, C++, Java, jQuery, Mobile tech, and extreme sports.

Comments and Discussions

 
GeneralMy vote of 2 Pin
Saar Yahalom13-May-11 12:25
Saar Yahalom13-May-11 12:25 
GeneralRe: My vote of 2 Pin
rgubarenko18-May-11 15:16
rgubarenko18-May-11 15:16 
GeneralMy vote of 2 Pin
mertner14-Apr-11 22:00
mertner14-Apr-11 22:00 
GeneralRe: My vote of 2 Pin
rgubarenko15-Apr-11 4:16
rgubarenko15-Apr-11 4:16 
GeneralRe: My vote of 2 Pin
mertner15-Apr-11 4:53
mertner15-Apr-11 4:53 
GeneralRe: My vote of 2 Pin
rgubarenko15-Apr-11 6:55
rgubarenko15-Apr-11 6:55 
GeneralRe: My vote of 2 Pin
mertner15-Apr-11 10:55
mertner15-Apr-11 10:55 
GeneralRe: My vote of 2 Pin
rgubarenko15-Apr-11 19:37
rgubarenko15-Apr-11 19:37 
GeneralRe: My vote of 2 Pin
mertner16-Apr-11 8:48
mertner16-Apr-11 8:48 
GeneralRe: My vote of 2 Pin
rgubarenko16-Apr-11 18:34
rgubarenko16-Apr-11 18:34 
GeneralMy vote of 3 Pin
n.podbielski12-Apr-11 3:29
n.podbielski12-Apr-11 3:29 
GeneralRe: My vote of 3 Pin
rgubarenko12-Apr-11 4:35
rgubarenko12-Apr-11 4:35 
GeneralRe: My vote of 3 Pin
n.podbielski12-Apr-11 7:29
n.podbielski12-Apr-11 7:29 
GeneralRe: My vote of 3 Pin
rgubarenko12-Apr-11 9:40
rgubarenko12-Apr-11 9:40 
GeneralRe: My vote of 3 Pin
n.podbielski12-Apr-11 12:54
n.podbielski12-Apr-11 12:54 
GeneralMy vote of 3 Pin
User 482203311-Apr-11 20:08
User 482203311-Apr-11 20:08 
GeneralRe: My vote of 3 Pin
rgubarenko12-Apr-11 8:50
rgubarenko12-Apr-11 8:50 
GeneralInteresting concept but I think you are being slightly disingenuous Pin
Pete Sutcliffe11-Apr-11 4:50
Pete Sutcliffe11-Apr-11 4:50 
GeneralRe: Interesting concept but I think you are being slightly disingenuous [modified] Pin
rgubarenko11-Apr-11 10:00
rgubarenko11-Apr-11 10:00 
GeneralRe: Interesting concept but I think you are being slightly disingenuous Pin
lolocmwa11-Apr-11 12:58
lolocmwa11-Apr-11 12:58 
GeneralRe: Interesting concept but I think you are being slightly disingenuous Pin
rgubarenko11-Apr-11 10:06
rgubarenko11-Apr-11 10:06 
GeneralRe: Interesting concept but I think you are being slightly disingenuous Pin
Pete Sutcliffe11-Apr-11 23:48
Pete Sutcliffe11-Apr-11 23:48 
GeneralRe: Interesting concept but I think you are being slightly disingenuous Pin
rgubarenko12-Apr-11 5:59
rgubarenko12-Apr-11 5:59 
GeneralRe: Interesting concept but I think you are being slightly disingenuous Pin
Pete Sutcliffe12-Apr-11 23:43
Pete Sutcliffe12-Apr-11 23:43 
GeneralRe: Interesting concept but I think you are being slightly disingenuous Pin
rgubarenko13-Apr-11 6:20
rgubarenko13-Apr-11 6:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.