Καλώς ορίσατε στο dotNETZone.gr - Σύνδεση | Εγγραφή | Βοήθεια

C# and .NET Tips and Tricks

Quests in programming in .NET

Παρουσίαση με Ετικέτες

Όλες οι Ετικέτε... » HTML5   (RSS)
(Windows8) WinJS single page navigation and ViewModels

In the previous post, we have seen simple data-binding scenarios using WinJS and got a glimpse on the way a ViewModel can be created (following the MVVM pattern). In this post, we will build and application where there are more than one views (pages - simple page navigation app) with simple html elements and ListViews with real data. The question we need to answer is:

 

How do the ViewModels and MVVM can be applied in a single page navigation WinJS application?”

(FOR .NET Developers: if you are coming from the .NET world and especially XAML you will also get a chance to see how things like INotifyPropertyChanged and ObservableCollections apply to the WinJS world. The ViewModel presented here is as close as possible to the one that would be applied in a WPF-Silverlight window)

 

We will create a simple app that displays the births and deaths of celebrities in today’s date. Data will come from the RSS feeds provided by IMDB. Clicking on a celebrity should lead us to a new page where we get the IMDB webpage of that celebrity. Within this application, ViewModels will be used for each page. The requirements we have for our ViewModels are as follows:

 

  • There should be one ViewModel for each page.
  • The ViewModel should hold all the variables and methods required to provide the data to the view (html page)
  • The view will connect to the ViewModel through data binding.
  • All events generated by the page should also fire methods of the ViewModel.

 

Open VS 2011 and create a “Navigation Application” project. The “default.html” file that is generated contains the following:

 

<div id="contenthost" data-win-control="BirthsDeaths.PageControlNavigator" 
data-win-options="{home: '/html/homePage.html'}">
</div>

That defines an area that will be loading your pages according to your application’s state. This area is initialized and loads the “homePage.html” page (VS has also generated for you a navigate.js file that handles the navigation. For an introduction to single page navigation you can read this article).

 

You should consider each page that will be loaded in this area as a separate window or view of your application. Therefore, each page should have its own ViewModel. In our case, we have two pages: one (homePage) that displays the births and deaths in two ListViews and one (actorDetails) that displays the actor’s details when the user clicks on an item in the lists.

 

Let’s start with the homePage view that will look like the one in the following figure:

 

image

 

That is, there should be two lists one showing the deaths and one showing the births. Also, just below the main header we need to display the date and time and a downloading message when the data are fetched from IMDB. The html that generates that view is (of course there is the .css that also does the layout magic):

 

<section aria-label="Main content" role="main">
<span id="dateSpan"></span>&nbsp;&nbsp;
<span id="downloadingSpan"></span>
<section class="listsContainer">
<h2 class="birthsHeader">born today</h2>
<h2 class="deathsHeader">died today</h2>
<div id="birthsListElement" data-win-control="WinJS.UI.ListView"></div>
<div id="deathsListElement" data-win-control="WinJS.UI.ListView"></div>
</section>
</section>

So for our ViewModel we will need: A property for the date/time, a property for the “downloading” message, two lists for the births and deaths, two methods to fetch the deaths and births and the event handler that will be invoked when the user clicks on a list item. In the “homepage.js” file, the process for creating this ViewModel is as follows:

Within the self executing function that is generated for us, we define a new class (WinJS.Class.define) that we name HomePageViewModelClass:

 

var HomePageViewModelClass = WinJS.Class.define(
function () {
this.onShowActorDetailsBirths = this.showActorDetailsBirths.bind(this);
this.onShowActorDetailsDeaths = this.showActorDetailsDeaths.bind(this);
},
{
_today: "",
today: {
get: function () {
return this._today;
},
set: function (value) {
this._today = value;
this.notify("today", value);
}
},

_downloading: "",
downloading: (...),

birthsList: new WinJS.Binding.List(),
deathsList: new WinJS.Binding.List(),
getBirths: function () {
this.downloading = "(downloading...)";
var that = this;
var syn = new Windows.Web.Syndication.SyndicationClient();
var url = new Windows.Foundation.Uri("http://rss.imdb.com/daily/born/");
syn.retrieveFeedAsync(url).then(
function (feed) {
for (var i = 0, len = feed.items.length; i < len; i++) {
var item = feed.itemsIdea;
var birth = {
title: item.title.text,
date: item.publishedDate,
content: item.summary.text,
link: item.links[0].nodeValue
}
that.birthsList.push(birth);
}
that.downloading = "";
});
},
getDeaths: (...),
showActorDetailsBirths: function (e) {
this._showActorDetails(this.birthsList.getAt(e.detail.itemIndex));
},
showActorDetailsDeaths: function (e) {
this._showActorDetails(this.deathsList.getAt(e.detail.itemIndex));
},

_showActorDetails: function (item) {
WinJS.Navigation.navigate("/html/actorDetail.html", item);
}

}
);


There are a few things to note here:

 

  • For the properties “today” and “downloading” we use accessors. The accessors get and set their corresponding private fields. Note that after they set their value they call a “strange” (for now) method which is called notify with the name of the property that has changed and the new value. You will see in just a minute where this method is defined but for now remember that this is what makes the data-bound part of the UI update when this property changes (for .NET Developers: the equivalent of PropertyChanged).

 

  • The “showActorDetailsBirths” and “showActorDetailsDeaths” methods are the ones that will be called when the user presses on a list item and therefore will be bound to the “oniteminvoked” event. Due to the javascript’s issues with the “this” keyword”, on the class constructor, we create two new methods from those ones (prefixed with “on”) using the bind method, that have the correct context for “this”, that is the object itself, and those will be the ones that will be bound to the events.

 

  • The two lists are of “WinJS.Binding.List” type in order to provide the necessary events for the UI to be updated when items are added to them (for .NET Developers” the equivalent to ObservableCollection).

 

Now just after the aforementioned class definition, we create from it a new class definition as follows:

 

var BindableHomePageViewModelClass = WinJS.Class.mix(HomePageViewModelClass, WinJS.Binding.mixin);

This actually uses the “WinJS.Class.mix” to add to our HomePageViewModelClass class all methods and properties that are contained in the “WinJS.Binding.mixin” class and guess what, those are the methods needed for providing change notifications of the data-binding (this is the source of the notify method). This also means that we should never use the HomePageViewModelClass but instead always use the BindableHomePageViewModelClass since this is the “complete” one after the application of the mix method.

 

Now we make our ViewModel public in the “ProgwareOrg” namespace as follows creating a new object. This is the object that will be bound to the view:

 

WinJS.Namespace.define("ProgwareOrg", {
HomePageViewModel: new BindableHomePageViewModelClass()
});

In the page’s functions that handle the basic events of our page we can bind the ViewModel to the View as follows (for .NET Developers: this code should be considered as the xaml.cs file and the binding to the ViewModel that occurs there):

 

WinJS.UI.Pages.define("/html/homePage.html", {
init: function init(element, options) {

var calendar = new Windows.Globalization.Calendar();
ProgwareOrg.HomePageViewModel.today = calendar.dayOfWeekAsString() + " " +

calendar.day + " " + calendar.monthAsString() + " " + calendar.yearAsString();
ProgwareOrg.HomePageViewModel.getBirths();
ProgwareOrg.HomePageViewModel.getDeaths();
},
ready: function ready(element, options) {
WinJS.Binding.processAll(home, ProgwareOrg.HomePageViewModel);
}
});


We initialize our ViewModel and in the ready function we bind it to the view via the “WinJS.Binding.ProcessAll” method (for .NET Developers: this would be the this.DataContext=ViewModel in XAML). This method takes as first parameter the root element where the ViewModel is applied and in our case is the one with the id=”home”.

 

The binding in the view is as follows:

 

<div class="homepage" id="home">
<section aria-label="Main content" role="main">
<span id="dateSpan" data-win-bind="innerText:today"></span>&nbsp;&nbsp;
<span id="downloadingSpan" data-win-bind="innerText:downloading"></span>
<section class="listsContainer">
(...)
<div id="birthsListElement"
data-win-control="WinJS.UI.ListView"
data-win-options="{itemDataSource:ProgwareOrg.HomePageViewModel.birthsList.dataSource,
itemTemplate:select('#template'),
layout:{type:WinJS.UI.ListLayout},
oniteminvoked : ProgwareOrg.HomePageViewModel.onShowActorDetailsBirths}"
>
</div>
<div id="deathsListElement" (...)></div>
</section>
</section>
</div>

First note that the top-level element (the one used in processAll) is the id=”home” element. Also note the “data-win-bind”  attributes that provide the binding for the HTML elements.

 

For the two ListViews note the following:

 

The itemDataSource is bound to our ViewModel but it is using the full qualified name. This means that it does not take into consideration the processAll method. I do not know why this happens and I think that this is something that needs to be fixed but for now the hack I found is that for the controls of WinJS for binding with the ViewModel we need to use the full access paths of our ViewModel. This also applies to the binding of the oniteminvoked event handler. The template set is defined in the page just above this code and not displayed here for simplicity.

 

And this summarizes the way the ViewModel can be bound to our view. The ViewModel for the actorDetails page is simpler:

 

(function () {
"use strict";

var ActoDetailViewModelClass = WinJS.Class.define(
function () {},
{
_pageToShow: "",
pageToShow: {
get: function () {
return this._pageToShow;
},
set: function (value) {
this._pageToShow = value;
this.notify("pageToShow", value);
}
}
}
);

var BindableActoDetailViewModelClass = WinJS.Class.mix(ActoDetailViewModelClass, WinJS.Binding.mixin);

WinJS.Namespace.define("ProgwareOrg", {
ActoDetailViewModel: new BindableActoDetailViewModelClass()
});

WinJS.UI.Pages.define("/html/actorDetail.html", {
ready: function ready(element, options) {
ProgwareOrg.ActoDetailViewModel.pageToShow = options.link;
WinJS.Binding.processAll(mainSection, ProgwareOrg.ActoDetailViewModel);
},
updateLayout: function updateLayout(element, viewState) { }
});
})();

And the view:

 

<div id="mainSection" class="actorDetail fragment">
...
<section aria-label="Main content" role="main">
<iframe id="actorsPage" data-win-bind="src:pageToShow" style="width: 100%;height: 100%" />
</section>
</div>

Data are passed between pages as the second parameter of the WinJS.Navigation.navigate("/html/actorDetail.html", item);. They can be retrieved from the navigated page through the options parameter of the ready function.

The “actorDetail” page uses an iframe to display the webpage. Because the page has ActiveX controls when you run the project from within VS you will be getting the error “JavaScript runtime error: Access is denied”.If you look at the Javascript Console you will see that the error is “Cannot load the ActiveX plug-in that has (…)” and that “Apps can't load ActiveX controls”. But if you compile and deploy in “Release” go to Windows8 start page and run the app this will not appear. This does not mean that the error disappears. It just means that in release mode the ActiveX will just don’t load which is fine with us.

 

So in this post we have seen an approach on how to create ViewModels in a single navigation application in Windows8.

Posted: Παρασκευή, 23 Μαρτίου 2012 6:43 πμ από iwannis | 2 σχόλια
Δημοσίευση στην κατηγορία: , ,
HTML5 Client Storage. An introduction on the new cookies.

Let’s talk about cookies. In fact let’s revisit cookies and see how we use them and what nightmares they bring along. Cookies are all about client storage. They are all about storing persistent information at client side.

The syntax for creating/reading them is as follows. Imagine that we want to store client side the value 17 in a cookie named id. This is done by the following BLOCKED SCRIPT document.cookie=”id=17;expires=Mon,6 Feb 2012 12:00:00”;

When we later want to read the value of the cookie named id we need to search within document.cookie for the string “id=” and fetch the value up to the next “;” character.

Based on the previous definitions we may come up with three nice Javascript functions that make our lives a littler easier:

Creating a cookie

function SetCookie(name, value, expirationInDays) {
    var now = new Date();
    var expire = new Date();
    var newCookie = name + "=" + value;
    if (expirationInDays != null && expirationInDays != 0) {
        expire.setTime(now.getTime() + 3600000 * 24 * expirationInDays);
        newCookie += ";expires=" + expire.toGMTString();
    }
    document.cookie = newCookie;
}

Reading the value of a cookie

function ReadCookie(name) {
    var nameEQ = name + "=";
    var ca = document.cookie.split(';');
    for (var i = 0; i < ca.length; i++) {
        var c = caIdea;
        while (c.charAt(0) == ' ') c = c.substring(1, c.length);
        if (c.indexOf(nameEQ) == 0) {
            return c.substring(nameEQ.length, c.length);
        }
    }
}

Or of course you can use a JQuery plugin which provides an implementation for the basic operations with cookies.

Now some basic facts:

 

  • To update a cookie you just create the same cookie with the updated value.
  • If you do not define an expiration date then the cookie will expire as soon as the browser’s window is closed. This cookie is also called a session-based cookie.
  • if you want to delete a cookie then you must call SetCookie for the cookie without providing an expiration date and by setting its value to blank. As a consequence the cookie will be removed when the browser’s window is closed (so it is actually an inferred delete!)
  • Cookies persist among tabs in a browser. So storing tab critical information in the cookie may result in unwanted behavior.

 

So we need a better and easier way to create/update/delete values that we store client-side, and easier way to handle expiration (values that persist even when the browser is closed or values that they don’t). And that is exactly what HTML5 offers us through the use of the following:

 

  • sessionStorage: Equivalent to session-based cookies. Is erased when the browser window is closed but also the values are not shared among browser’s tabs.
  • localStorage: Equivalent to cookies with expiration date but does not expire.

 

The API is native to Javascript and is as follows (for the same example with the value 17 called id). For sessionStorage just replace localStorage with sessionStorage:

 

  • Create: localStorage.setItem("id",17);
  • Read: localStorage.getItem(“id”);
  • Remove: localStorage.removeItem(“id”);
  • Clear all:localStorage.clear();

 

Finally don’t forget that like cookies in those values you can even store full Javascript objects as follows where we store the object Client in sessionStorage using JSON.stringify and retrieve it with :

var Client = {
    firstName:"John",
    lastName:"Panagopoulos",
    coolFactor:10
}
function storeClient() {
    sessionStorage.setItem("client", JSON.stringify(Client));
}
function getClient() {
    var retrievedClient = JSON.parse(sessionStorage.getItem("client"));
    console.dir(retrievedClient);
}

Well this is it. Of course there are some other sophisticated methods to store stuff client-side the “HTML5” way such as Web SQL Databases or the cache but we will cover those in a future post.

Posted: Δευτέρα, 6 Φεβρουαρίου 2012 2:22 μμ από iwannis | 0 σχόλια
Δημοσίευση στην κατηγορία:
ASP.NET MVC, HTML5, CSS3, JQuery templates, IEnumerable binding, knockoutjs and offline cache in a demo. A brave new world

In one of my previous posts(appox. a year ago) I have demonstrated how to bind to IEnumerables in ASP.NET MVC. I have shown several ways of dynamically adding/removing elements from the list through the use of Javascript and then jQuery-templates. In that postI have used the traditional ASP.NET MVC literals <% %> and of course the whole site operated in HTML4 and jQuery.

 

Today a lot of things have changed. From HTML4 we are moving to HTML5, we start using CSS3 for our styles, we use Razor for our views in ASP.NET MVC and we can take advantage of the knockoutjslibrary that has managed to bring the world of MVVM patterns closer to the world of Javascript and it did it with some impressive results. Therefore, in this post we are going to re-implement the same project adopting all those new (or not so new) great technologies.

 

We need to implement a simple site where we have a list of clients with a first and last name and we need to give to our users the ability to manage that list (insert/remove/edit elements). The initial list and all changes are communicated to the server using AJAX calls. We need to make the site friendly to computer and mobile browsers and provide the maximum possible quality in terms of user experience in both environments. Finally we need our site to know when it is online, in order to inform the user and prevent any changes.

 

(Device awareness) CSS3 Styling with media-queries and other goodies

The first change is the fact that we want to differentiate the view depending on whether we look the demo from a mobile device or from our pc. Therefore we will use CSS3 media queries to change the .css file used depending on the situation detected. In the _Layout.cshtml file we add the following:

 

<link href="@Url.Content("~/Content/mobileDevice.css")" 
      rel="stylesheet" type="text/css" media="screen and (max-width:480px)" />
<link href="@Url.Content("~/Content/computer.css")" 
      rel="stylesheet" type="text/css" media="screen and (min-width:481px)" />

 

Where the important attribute is the “media” attribute which differentiates the stylesheet based on the width of the device.

 

Another element that enables us to define specifically how things will be presented on a mobile device is the viewport meta tag as follows:

 

<meta name="viewport" content="user-scalable=no,width=device-width" />

 

This says to the mobile browser that we do not want to allow the user to be able to scale the webpage with his/her fingers and also that the viewport of the browser (the width of the mobile browser) is the same as the width of the device (as opposed to the more traditional approach where the mobile browser allows us to pan and zoom). Just adding this line makes our page look more like a “mobile app” to the device’s browser.

 

We also want to use a specific kind of fonts (Segoe) that are not the “standard” and therefore need to be embedded to the page. We can do that with CSS3 starting by finding the .ttf file we need from our computer and submitting that to the fontsquillerpage in order to get the font files in various formats recognized by most browsers and the .css definitions of our new font. We get the files, we put them in a special “Fonts” folder in our page and include a reference to the .css file holding their definitions (newFonts.css file in our project). Below is a screenshot of the website’s home page as seen in a browser more than 480px wide and in a browser less that 480px wide with the embedded fonts:


 

Capture2    Capture1


Note the new fonts achieved as previously mentioned and the rounded corners of the links in the mobile version. Those are achieved by the new elements in css as follows combined with the advanced selectors in CSS3 (in specific those ones apply the border rounded corner rules only to the first and last li elements of the unordered list (ul) that holds the navigation:

 

<nav>
    <ul>
        <li>@Html.ActionLink("jump to the demo","Simple","Clients")</li>
        <li>@Html.ActionLink("about","About","Home")</li>
    </ul>
</nav>

 

and the CSS applying the rounded borders:

 

nav ul li:first-child a 
{
    -webkit-border-top-left-radius: 8px; 
    -webkit-border-top-right-radius: 8px; 
    border-top-right-radius: 8px; 
    border-top-left-radius: 8px; 
    -moz-border-top-right-radius: 8px; 
    -moz-border-top-left-radius: 8px;
}
nav ul li:last-child a 
{
    -webkit-border-bottom-left-radius: 8px; 
    -webkit-border-bottom-right-radius: 8px; 
    border-bottom-right-radius: 8px; 
    border-bottom-left-radius: 8px; 
    -moz-border-bottom-right-radius: 8px; 
    -moz-border-bottom-left-radius: 8px;
}

 

We have also applied some shadow to the header of the mobile version with: text-shadow:1px 1px #fff; Also note the new semantic markup for HTML5. The “nav” element indicates that there is a site navigation set of links in this part of the webpage. The same difference in styling is applied also to the “demo” page as shown in the images below":


image     image


Again the two different stylesheets have definitions that achieve the differentiation.

 

(Easier unobtrusive Javascript) Data binding with knockoutjs and jQuery templates

And now we move on to the UI in terms of the client’s list. The methods shown in the previous postare all nice but they rely on a page “POST” to save the values and a page “GET” to get the values from the controller’s actions. We want to turn the page fully AJAX based and provide the maximum quality of user experience. For starters, let us imagine that we have “magically” received the list of clients and simulate this by adding in the page’s JavaScript the following list:

 

var clientsList = [{ id :1, FirstName: 'Ioannis', LastName: 'Panagopoulos' },                   
                   { id: 2, FirstName: 'Kostas', LastName: 'Papadopoulos' },                   
                   { id: 3, FirstName: 'Petros', LastName: 'Georgiadis' },                   
                   { id: 4, FirstName: 'Maria', LastName: 'Apostolou' }];

 

This is hardwired and will be later substituted with an AJAX call. To use knockoutjsand jQuery templateswe need to download and include in the _Layout.cshtml file the following:

 

<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.tmpl.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/knockout-1.2.1.js")" type="text/javascript"></script>

 

The first step is to define an element in the page where the list will be displayed:

 

<section id="clientsList" data-bind='template: "clientTemplate"'></section>

 

The data-bind attribute is called a custom data attribute in HTML5. Custom data attributes are all attributes prefixed with “data-“. After the “-“ you can put whatever you like. HTML5 defines that custom data attributes are to be used to assist the programmer in any way (meaning that the word after the – may be anything) in developing the page. We know that knockoutjs searches for the data-bind custom data attribute to do its magic. In the previous declaration we tell through the custom data attribute that within the “clientsList” section (section is another HTNL5 element, it could easily be a div in this case) we want to render a jQuery template named “clientTemplate”. This template is as follows:

 

<script type="text/html" id="clientTemplate">    
    {{each(i,client) clients}}
    <div id="Clientidx">
        <input class="clientNameField" data-bind="value: FirstName" />
        <input class="clientNameField" data-bind="value: LastName"  />
        <span class="removeButton" data-bind="click: function(){ removeClient(client) }">x</span>
    </div>
    {{/each}}
</script>

 

This template says that we need to iterate a list named “clients” consisting of objects which we will call “client” (the name is just a way to have a reference to the object in each iteration). For each object, we need an input element to present the value of its FirstName property and an input element for the value of its LastName property. Finally for each object an “x” will appear that has a handler to be executed on its click event. The handler says that we need to call the function removeClient passing as argument the object of “x”’s line.

 

In another place of our code we need to be showing the total number of clients in the list:

 

<h1>clients list (<span data-bind="text: clients().length">&nbsp;</span>)</h1>
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

 

Where we bind to the total number of elements in the clients list. And in another place we have the following:

 

<button class="defaultButton" data-bind="click: addClient">add a client</button>
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

 

So the template finds the template definition, FirstName and LastName find their object within the clients list but who supplies the clients list and the removeClient and addClient methods? Well those are supplied by an object we call ViewModel and is defined as follows:

 

var ViewModel = {
            clients: ko.observableArray(clientsList),
            removeClient: function (clientToRemove) {
                ViewModel.clients.remove(clientToRemove);
            },
            addClient: function () {
                ViewModel.clients.push({id:-1, FirstName: "", LastName: ""});             
            }
        };
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

 

In other words, the ViewModel holds everything our UI has in terms of functionality and data. The ko.observableArray that is used makes the array be able to notify about its changes to every element attached to its properties (including the one we defined showing the total number of clients in the list). This makes it possible for the dynamic change of the number of clients reported whenever we press “add client”. This is the equivalent to INotifyPropertyChanged! The final step is to attach the ViewModel to the UI:

 

$(document).ready(function () {ko.applyBindings(ViewModel);});
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

 

and all the bindings find their way within the ViewModel. To complete our example we need to be able to get this information from an ASP.NET MVC Controller and submit the results to one through AJAX calls. Provided that we implement a controller to get all the clients as follows:

 

public JsonResult GetClients()
{
    List<Client> Clients = new List<Client>();
    _populateClientsList(Clients);
    return Json(Clients,JsonRequestBehavior.AllowGet);
}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

 

The JavaScript is changed to get the client’s list as follows:

 

$(document).ready(function () {
    $.getJSON("@Url.Action("GetClients","Clients")", null, clientsReceived);
});
function clientsReceived(res) {
    ViewModel.clients = ko.observableArray(res);
    ko.applyBindings(ViewModel);
}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

 

(there is no need anymore for the clientsList)

 

The same applies for posting the results back to the server when the user has finished editing the list we call:

 

saveClientsList: function () {
    $("#ajaxCallWait").toggle();
        $.post("@Url.Action("SaveClients","Clients")", $.toDictionary({ Clients: ko.toJS(ViewModel.clients) }), 
                function (data) {
                   $("#ajaxCallWait").toggle();
                   alert(data.message);
                })
}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

 

A few things to note here. First we use the ko.toJS function to get the “raw” list without the ko.ObservableArray embelishments. Second we use a wonderful plugin from here(toDictionary) to enable the correct generation of the elements required for binding with the ASP.NET MVC controller. The controller now is as follows:

 

[HttpPost]
public JsonResult SaveClients(IEnumerable<Client> Clients)
{
    //Your saving logic here
    return Json(new { result = "success",
                      message = String.Format("{0} clients received",Clients.Count()) });
}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

 

So this is it! We have managed to bind everything with considerable few lines of code compared to what we have achieved.

 

Knowing when we are offline and reacting accordingly

When the user is offline he will not be able to receive any list elements but he will be still be able to add clients and press the save button. We want to prevent this by indicating with a message that the user is offline.

 

We include a cache.manifest file in our project as follows:

 

CACHE MANIFEST
CACHE:
/demo/Clients/Simple
/demo/Scripts/knockout-1.2.1.js
/demo/Scripts/jquery-1.5.1.min.js
/demo/Scripts/jquery-toDictionary.js
/demo/Scripts/jquery.tmpl.js
/demo/Content/computer.css
/demo/Content/mobileDevice.css
/demo/Content/newFonts.css
FALLBACK:
/demo/Scripts/online.js /demo/Scripts/offline.js
NETWORK:
*
#version 1

 

This says that whatever is below the CACHE: should be stored locally, everything else should be fetched from the network (NETWORK: *) and if the browser cannot reach the online.js file (meaning that there is no internet connection), the browser should use the offline.js file that is automatically cached for offline use.

 

The online.js file has only: var online=true; inside while the offline file has only: var online=false; inside. Our webpage includes only online.js as follows (we DO NOT include the offline.js):

 

<script src="@Url.Content("~/Scripts/online.js")" type="text/javascript"></script>

 

and before performing the initial get request for the clients we add:

 

$(document).ready(function () {
        if (!online) {
                alert("Sorry but you are offline!");
                return;
        }
        $.getJSON("@Url.Action("GetClients","Clients")", null, clientsReceived);
});

 

The trick is that when online online.js will set the online variable to true while when offline the online.js file will be substituted with the offline.js file and the online variable will be false. Note that when you use the cache manifest you need to have the complete path for your files. In my case I have deployed the site in a subfolder named demo. Also note that if you have a single mistake or a missing file or a wrong path in the cache.manifest, then the whole file is not parsed. You generally need fiddlerto debug this correctly where you see what is wrong.

 

Well this is it! The complete makeover of an HTML4 page to HTML5. Hope you have enjoyed it!

 

You may download the project

Posted: Παρασκευή, 16 Δεκεμβρίου 2011 6:08 μμ από iwannis | 2 σχόλια
Δημοσίευση στην κατηγορία: ,
ASP.NET MVC HTML5 Before and After: The “semantic” markup of HTML5

In this series of posts, we are going to implement a simple website using ASP.NET MVC initially in HTML4 and demonstrate what changes and why in HTML5 for exactly the same implementation.

 

Featuring in this post: The semantic markup/value of HTML5 In this post, we see how the new semantics of HTML5 alter the HTML generated for the homepage of a simple site and why this is important. Imagine you want to implement a website that every day displays a special offer of some kind. In our example, the site is featuring a special offer for a place to go for the weekend. The homepage is as follows:

 

image

 

That is, you have your header where the company’s logo and tagline are displayed (yellow area), a left area where you have some sponsored banners and exactly below an area with previous offers (orange area), your main page where you show the current offer (green area) and the footer with a small navigation menu and information on the site’s creators (white area at the bottom).

Imagine that you have a view model of the following form:

public class HomePageViewModel
{
public Offer CurrentOffer { get; set; }
public IEnumerable<Offer> PreviousOffers { get; set; }

public string BannerImage1File { get; set; }
public string BannerImage1Link { get; set; }

public string BannerImage2File { get; set; }
public string BannerImage2Link { get; set; }
}

The view model consists of the featured offer (the current one) along with a list of the previous offers. Moreover, it has the link and the image file path of the two banners on the left of the homepage. Each offer is represented by an object of the class Offer which has some basic properties related to is such as the Title, Description, Date, Initial price etc. An object of type HomePageViewModel is returned in your “Index” Action of the “Home” controller and is being rendered in the View (Index.cshtml) using Razor. Prior to HTML5 your _Layout.cshtml file defining the main areas of your website as described above would be as follows (body part):

<body>
<div id="container">
<div id="header">
<img style="float:left;width:100px;height:100px" alt="Site Logo" src="../../Images/SiteLogo.png" />
<div style="float:left">
<h1>Good Deals!</h1>
<h4>tagline</h4>
</div>
</div>

<div id="mainContent">
<div id="leftArea">
@RenderSection("LeftArea",false)
</div>
<div id="mainArea">
@RenderBody()
</div>
</div>

<div id="footer">
<div id="menu">
Home | Previous Offers | About | Join us
</div>
&copy; 2011 - Ioannis Panagopoulos - HTML5 Demo Series from
<a href="http://www.progware.org">http://www.progware.org</a>
For more information call: 210666666666
</div>
</div>
</body>

(a visual representation of the site’s structure can be found at the end of the post)

That is, you have each one of the areas defined with a div element carrying a unique id so that you can apply positioning and styling rules through the Site.css file. This is the pretty straightforward approach of developing the visual structure of the homepage of the website. Similarly, the view template for the homepage (Index.cshtml - a strongly-typed View using the HomePageViewModel object returned from the Index action) is as follows (sections only displayed here):

 

@section LeftArea
{
<a href="@Model.BannerImage1Link"><img alt="Ad1" src="@String.Format("Images/{0}", Model.BannerImage1File)" width=170 height=100 /></a><br />
<a href="@Model.BannerImage2Link"><img alt="Ad2" src="@String.Format("Images/{0}", Model.BannerImage2File)" width=170 height=100 /></a><br />
<h1>Previous Deals</h1>
@foreach (var PrevDeal in Model.PreviousOffers)
{
<h2>@PrevDeal.Title</h2>
<img alt="Deal" src="@String.Format("Images/Places/{0}", PrevDeal.RelativePhotoPath)" width=100 height=60 /><br />
@:from: <p>@PrevDeal.InitialPrice.ToString("C")</p>
@:to: <p>@PrevDeal.Price.ToString("C")</p>
@Html.ActionLink("View", "Index", "Offer", new { Id = @PrevDeal.Id }, null)
}
}

<h1>Current Deal!</h1>
<h2>@Model.CurrentOffer.Title</h2>
<img alt="Deal" src="@String.Format("Images/Places/{0}",Model.CurrentOffer.RelativePhotoPath)" width=300 height=180 /><br />
<p>@Model.CurrentOffer.Description.Substring(0, 100)+"..."</p>
from: <p>@Model.CurrentOffer.InitialPrice.ToString("C")</p>
to: <p>@Model.CurrentOffer.Price.ToString("C")</p>
posted on: <p>@Model.CurrentOffer.Date.ToShortDateString() time: @Model.CurrentOffer.Date.ToShortTimeString() </p>
@Html.ActionLink("View","Index", "Offer", new { Id = @Model.CurrentOffer.Id },null)

 

As you may have already realized, all the divs and ids of the divs, are strictly for display-rendering purposes. The HTML of the page does not give any indication whatsoever on the “meaning” of each area of the page. Any crawler or automated process visiting our site will not be able to deduce anything for the content. HTML5 introduces tag elements to use in the place of the divs that have more “semantic” meaning. Therefore the main objective of this post is the replacement of the divs of the page with HTML5 tag elements that give a meaning to the content apart from the strict visual arrangement governing the use of divs in HTML4.

 

So, the first key element of HTML5 is learning this new semantic markup and using it appropriately. And this is exactly what we will do. Actually, the first thing is changing the header of the site enclosed in a div with id=header to the following:

<header class="mainSiteHeader">
<img style="float:left;width:100px;height:100px" alt="Site Logo" src="../../Images/SiteLogo.png" />
<hgroup style="float:left">
<h1>Good Deals!</h1>
<h4>tagline</h4>
</hgroup>
</header>

As you see we define in markup that this is the header of the site (tag header) and within we define the logo and tagline as a group accompanying the header (tag hgroup). In other words we have given semantic information for the header content of our website.

Considering now the main content, we see that there are three semantic sections. One that contains the main offer, one that contains previous offers and one that comprises of the banners. In HTML5, semantic areas are defined with “section” tags. Individual pieces of information within the same section (such as each offer in the previous offers section) are defined with “article” tags. Each article can also have a header and sections. Finally since the area of the two banners is not directly related to the content of the website in HTML5 it should be enclosed within an “aside” tag. The footer of the homepage in HTML5 has its own “footer” tag and the navigation menu is enclosed in “nav” tags. Finally, note that some specific information such as the date of the offer are semantically defined  with specific HTML5 tags such as the “time datetime” tag.

 

The new _Layout.cshtml, using HTML5 markup is as follows:

<body>
<div id="container">
<header class="mainSiteHeader">
<img style="float:left;width:100px;height:100px" alt="Site Logo" src="../../Images/SiteLogo.png" />
<hgroup style="float:left">
<h1>Good Deals!</h1>
<h4>tagline</h4>
</hgroup>
</header>

<section class="mainContent">
<section class="leftArea">
@RenderSection("LeftArea",false)
</section>
<section class="mainArea">
@RenderBody()
</section>
</section>

<footer>
<nav>
Home | Previous Offers | About | Join us
</nav>
&copy; 2011 - Ioannis Panagopoulos - HTML5 Demo Series from <a href="http://www.progware.org">http://www.progware.org</a>
For more information call: 210666666666
</footer>
</div>
</body>

And the Index.cshtml which works with the same controller but with HTML5 markup is as follows:

@section LeftArea
{
<aside>
<a href="@Model.BannerImage1Link"><img src="@String.Format("Images/{0}", Model.BannerImage1File)" width=170 height=100 /></a><br />
<a href="@Model.BannerImage2Link"><img src="@String.Format("Images/{0}", Model.BannerImage2File)" width=170 height=100 /></a><br />
</aside>
<section>
<header class="sectionHeader"><h1>Previous Deals</h1></header>
@foreach (var PrevDeal in Model.PreviousOffers)
{
<article>
<header class="sectionHeader">
<h2>@PrevDeal.Title</h2>
</header>
<img src="@String.Format("Images/Places/{0}", PrevDeal.RelativePhotoPath)" width=100 height=60 />
<br />
from: <p>@PrevDeal.InitialPrice.ToString("C")</p>
to: <p>@PrevDeal.Price.ToString("C")</p>
@Html.ActionLink("View", "Index", "Offer", new { Id = @PrevDeal.Id }, null)
</article>
}
</section>
}
<header><h1>Current Deal!</h1></header>
<article>
<header class="sectionHeader">
<h2>@Model.CurrentOffer.Title</h2>
</header>
<img alt="Deal Image" src="@String.Format("Images/Places/{0}",Model.CurrentOffer.RelativePhotoPath)" width=300 height=180 /><br />
<p>@Model.CurrentOffer.Description.Substring(0, 100)+"..."</p>
from: <p>@Model.CurrentOffer.InitialPrice.ToString("C")</p>
to: <p>@Model.CurrentOffer.Price.ToString("C")</p>
posted on: <time [email protected]("yyyy-MM-dd") pubdate>@Model.CurrentOffer.Date.ToShortDateString() </time> time:<time>@Model.CurrentOffer.Date.ToShortTimeString()</time> </p>
@Html.ActionLink("View","Index", "Offer", new { Id = @Model.CurrentOffer.Id },null)
</article>

Graphically in HTML4 the structure of the site was defined as follows:

 

image

 

And after with HTML5:

image

The two sites with the help of styling with .css are visually the same but the second in HTML5 carries a lot more semantic information.

 

The first step for making  an ASP.NET MVC site using HTML5 is searching for the new HTML5 semantic tags and introducing them to your Views where appropriate. You will find a lot of sites in the web describing which are those new tags and where it is appropriate to use them in your page.

Shout it

Posted: Σάββατο, 25 Ιουνίου 2011 8:16 πμ από iwannis | 2 σχόλια
Δημοσίευση στην κατηγορία: ,