Posts Tagged MVC

ASP.Net MVC: Single Page Application (SPA) from Scratch; A How To Step by Step

During my presentation to the Denver Visual Studio Users Group on Asp.net SPA on March 25, 2013, I created an example SPA application from scratch using Visual Studio 2012 with Asp.Net and MVC. The intent was to provide an understanding of the tools and technologies needed to build an Asp.net spa. The following are the steps I used to create the basic SPA entitled UserGroupSPA in VS2012.

The following technologies and templates were used or provided inspiration in creating the example:

Pre Setup

  1. Using Microsoft Web Platform Installer 4.5, install  Asp.Net and Web Tools 2012.2 Update.
    1. Note that as of this writing that update will be included in the future Visual Studio 2012 Update #2.
    2. In the Microsoft Web Platform installer it is listed as Asp.net and Web Tools 2012.2

Visual Studio Project

  1. Create new project
    1. ASP.Net MVC 4 Web Application named UserGroupSPA
      1. Choose Basic Asp.net MVC4 Project Template with Razor view engine
  2. Package Manager Console (NUGET)
    1. Get-Package –Update
      1. Update-Package jQuery
      2. … (update all packages which can be updated as reported)
      3. Update-Package knockoutjs
    2. Install-Package Breeze.Webapi

Create Viewable Web Page in MVC at Root

  1. In the Views Folder
    1. Delete Shared folder and all of its contents
    2. Delete the view _View Start.cshtml
    3. Create DVSUG directory in the Views folde
    4. Add the view index.cshtml into the DVSUG directory
  2. This is the code for index.cshtml
    @using System.Web
    @using System.Web.Optimization
    <!DOCTYPE html>
    <html>
    <head>
        <title>Denver Visual Studio Users Group</title>
    
        @Styles.Render("~/Content/css")
    </head>
    <body>
        <h1>Denver Visual Studio Users Group Main</h1>
    </body>
    </html>
  3. Global.asax
    1. Comment out the line RegisterBundles such as:
      WebApiConfig.Register(GlobalConfiguration.Configuration);
      FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
      RouteConfig.RegisterRoutes(RouteTable.Routes);
      //BundleConfig.RegisterBundles(BundleTable.Bundles);
  4. App_Start folder
    1. Add C# class DVSUGConfig
      using System.Web.Optimization;
      
      [assembly: WebActivator.PostApplicationStartMethod(
          typeof(UserGroupSPA.App_Start.DVSUGConfig), "PreStart")]
      
      namespace UserGroupSPA.App_Start
      {
          public class DVSUGConfig
          {
              public static void PreStart()
              {
                  BundleConfig.RegisterBundles(BundleTable.Bundles);
              }
          }
      
      }
    2. Add C# class DVSUGRouteConfig
      using System.Web.Mvc;
      
      [assembly: WebActivator.PreApplicationStartMethod(
          typeof(UserGroupSPA.App_Start.DVSUGRouteConfig), "RegisterDVSUGPreStart", Order = 2)]
      
      namespace UserGroupSPA.App_Start
      {
          ///<summary>
          /// Inserts the DVSUGRouteConfig SPA sample view controller to the front of all MVC routes
          /// so that the DVSUGRouteConfig SPA sample becomes the default page.
          ///</summary>
          ///<remarks>
          /// This class is discovered and run during startup
          /// http://blogs.msdn.com/b/davidebb/archive/2010/10/11/light-up-your-nupacks-with-startup-code-and-webactivator.aspx
          ///</remarks>
          public static class DVSUGRouteConfig
          {
      
              public static void RegisterDVSUGPreStart()
              {
      
                  // Preempt standard default MVC page routing to go to DVSUG 
                  System.Web.Routing.RouteTable.Routes.MapRoute(
                      name: "DVSUGMvc",
                      url: "{controller}/{action}/{id}",
                      defaults: new
                      {
                          controller = "DVSUG",
                          action = "Index",
                          id = UrlParameter.Optional
                      }
                  );
              }
          }
      }
  5. Controllers directory
    1. Add controller DVSUGController (Empty MVC Controller)
  6. Run the application in IE (not chrome) in debug (F5)

Adding Toastr For Notifications

  1. Package Manager Console
    1. install-package toastr
  2. App_Start
    1. BundleConfig.CS change to:
      using System.Web;
      using System.Web.Optimization;
      
      namespace UserGroupSPA
      {
          public class BundleConfig
          {
              // For more information on Bundling, visit http://go.microsoft.com/fwlink/?LinkId=254725
              public static void RegisterBundles(BundleCollection bundles)
              {
                  bundles.IgnoreList.Ignore("*.intellisense.js");
                  bundles.IgnoreList.Ignore("*-vsdoc.js");
      
                  bundles.Add(
                              new ScriptBundle("~/scripts/UGScripts")
                              .Include("~/Scripts/jquery-{version}.js")
                              .Include("~/scripts/knockout-{version}.debug.js")
                              .Include("~/scripts/toastr.js")
                              .Include("~/scripts/Q.js")
                              .Include("~/scripts/breeze.debug.js")
                              );
      
                  bundles.Add(
                              new StyleBundle("~/Content/css")
                             .Include("~/Content/bootstrap.css")
                              .Include("~/Content/toastr.css")
                              );
              }
          }
      }
  3. Views\DVSUG\index.cshtml
    @using System.Web
    @using System.Web.Optimization
    <!DOCTYPE html>
    <html>
    <head>
        <title>Denver Visual Studio Users Group</title>
    
        @Styles.Render("~/Content/css")
    
    </head>
    
    <body onload="toastr.info('Loading Main Body')">
    
        @Scripts.Render("~/scripts/UGScripts")
    
        <h1>Denver Visual Studio Users Group Main</h1>
    
    </body>
    
    </html>
  4. Run in IE again and verify the toast event “Loading Main Body” comes up. such as:DVSUG

Install Durandal

  1. Package Manager Console
    1. install-package Durandal
    2. install-package Durandal.Router
    3. install-package Durandal.Transitions
    4. install-package Twitter.Bootstrap
  2. App_Start\BundleConfig.CS
    using System.Web;
    using System.Web.Optimization;
    
    namespace UserGroupSPA
    {
        public class BundleConfig
        {
            // For more information on Bundling, visit http://go.microsoft.com/fwlink/?LinkId=254725
            public static void RegisterBundles(BundleCollection bundles)
            {
                bundles.IgnoreList.Ignore("*.intellisense.js");
                bundles.IgnoreList.Ignore("*-vsdoc.js");
    
                bundles.Add(
                            new ScriptBundle("~/scripts/UGScripts")
                            .Include("~/Scripts/jquery-{version}.js")
                            .Include("~/scripts/knockout-{version}.debug.js")
                            .Include("~/scripts/sammy-{version}.js")
                            .Include("~/scripts/toastr.js")
                            .Include("~/scripts/Q.js")
                            .Include("~/scripts/breeze.debug.js")
                            .Include("~/scripts/bootstrap.js")
                            .Include("~/scripts/moment.js")
                            );
    
                bundles.Add(
                            new StyleBundle("~/Content/css")
                    .Include("~/Content/ie10mobile.css")
                    .Include("~/Content/bootstrap.css")
                    .Include("~/Content/bootstrap-responsive.css")
                    .Include("~/Content/durandal.css")
                    .Include("~/Content/toastr.css")
                    .Include("~/Content/app.css")
    
                            );
    
            }
        }
    }

Work With Views as a SPA using Durandal

  1. UserGroupSPA\App directory (created by Durandal)
    1. Add the folders
      1. viewmodels
      2. views
    2. Add the javascript file main.js
      require.config({
          paths: { "text": "durandal/amd/text" }
      });
      
      define(['durandal/app', 'durandal/viewLocator', 'durandal/system', 'durandal/plugins/router'],
          function (app, viewLocator, system, router) {
      
             system.debug(true); // Outputs to the console the process, turn off for production
      
             app.start().then(function () {
                 toastr.options.positionClass = 'toast-bottom-right';
                 toastr.options.backgroundpositionClass = 'toast-bottom-right';
      
                 router.handleInvalidRoute = function (route, params) {
                     toastr.info('No Route Found: ' + route);
                 };
      
                 router.useConvention();
                 viewLocator.useConvention();
      
                 // Our three views
                 router.mapNav('home');
                 router.mapNav('speakers');
                 router.mapNav('events');
      
                 app.adaptToDevice(); // Touch devices to adapt to
      
                 app.setRoot('viewmodels/shell', 'entrance'); //Show the SPA at its root
             });
      
        });
  2. Views\DVSUG\index.cshtml changed to
    @using System.Web
    @using System.Web.Optimization
    <!DOCTYPE html>
    <html>
    <head>
        <title>Denver Visual Studio Users Group</title>
        <meta charset="utf-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" />
        <meta name="apple-mobile-web-app-capable" content="yes" />
        <meta name="apple-mobile-web-app-status-bar-style" content="black" />
        <meta name="format-detection" content="telephone=no"/>
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    
        @Styles.Render("~/Content/css")
    </head>
    
        <body onload="toastr.info('Loading Main Body')">
    
            <div id="applicationHost">
            </div>
    
            @Scripts.Render("~/scripts/UGScripts")
    
            @if(HttpContext.Current.IsDebuggingEnabled) {
                <script type="text/javascript" src="~/App/durandal/amd/require.js" data-main="@Url.Content("~/App/main")"></script>
            } else 
            {
                <script type="text/javascript" src="~/App/main-built.js"></script>
            }
    
        </body>
    </html>
  3. App\viewmodels
    1. add event.js
      define(function () {
          var vm = {
              activate: activate,
              title: 'Events View'
          };
      
          return vm;
      
          function activate() {
              toastr.info('Events View Activated');
              return true;
          }
      });
    2. add home.js
      define(function () {
          var vm = {
              activate: activate,
              title: 'Home View'
          };
      
          return vm;
      
          function activate() {
              toastr.info('Home View Activated');
              return true;
          }
      });
    3. add shell.js
      define(['durandal/system', 'durandal/plugins/router'],
          function (system, router) {
              var shell = {
                  activate: activate,
                  router: router
              };
      
              return shell;
      
              function activate() {
                  return boot();
              }
      
              function boot() {
      
                  toastr.info('DVSUG Boot Loaded!');
                  return router.activate('home');
              }
      
          });
    4. add speakers.js
      define(function () {
          var vm = {
              activate: activate,
              title: 'Speakers View'
          };
      
          return vm;
      
          function activate() {
              toastr.info('Speakers View Activated');
              return true;
          }
      });
  4. App\views
    1. add events.html
      <section>
          <h2 class="page-title" data-bind="text: title"></h2>
      </section>
    2. add footer.html
      <nav class="navbar navbar-fixed-bottom">
          <div class="navbar-inner navbar-content-center">
              <span class="pull-left"><a href="http://omegacoder.com" target="_blank">OmegaCoder.com </a></span>
              <span class="pull-right"><a href="http://www.denvervisualstudio.net/" target="_blank">Denver Visual Studio User Group</a></span>
          </div>
      </nav>
    3. add home.html
      <section>
      
          <table>
              <tr><td><h2 class="page-title" data-bind="text: title"></h2></td></tr>
      
              <tr><td><h3>Next: Asp.Net SPA</h3></td></tr>
      
                    <tr><td><p>Come and find out all about it in this introduction to building Single Page Applications (SPA) which allow for rich client side interactions using JavaScript, HTML 5, and CSS in the ASP.NET world.</p>
                      <p>This introduction will familiarize you, the developer, to the templates available as well as an overview of the components which make up SPAs and how to put them together. This talk is a general how-to on being able to come up to speed on the technology and will focus on actually creating a page and cover the interactions of ASP.Net MVC with basic data transport.</p>
                      <p>You will see how ASP.NET Single-Page Applications (SPA) enable cutting-edge web developers to deliver rich, engaging web applications that surface information and interactivity in fresh and exciting new ways.</p>
            </td></tr>        
      
          </table>
      
      </section>
    4. add nav.html
      <nav class="navbar navbar-fixed-top">
          <div class="navbar-inner">
              <a class="brand" href="/">
                  <span class="title">Denver Visual Studio Users Group</span> 
              </a>
              <div class="btn-group" data-bind="foreach: router.visibleRoutes">
                  <a data-bind="css: { active: isActive }, attr: { href: hash }, text: name" 
                      class="btn btn-info" href="#"></a>
              </div>
      
          </div>
      </nav>
    5. add shell.html
      <div>
          <header>
              <!--ko compose: {view: 'nav'} --><!--/ko-->
          </header>
          <section id="content" class="main container-fluid">
              <!--ko compose: {model: router.activeItem, 
                  afterCompose: router.afterCompose, 
                  transition: 'entrance'} -->
              <!--/ko-->
          </section>
          <footer>
              <!--ko compose: {view: 'footer'} --><!--/ko-->
          </footer>
      </div>
    6. add speakers.html
      <section>
      
          <table>
              <tr><td><h2 class="page-title" data-bind="text: title"></h2></td></tr>
      
              <tr><td><h3>William Wegerson</h3></td></tr>
      
              <tr><td>
                      <p>William Wegerson is an Architect Developer who has been working here in Denver for over 20 years.</p>
                      <p>He has presented to the group before on such topics as Silverlight and Visual Studio releases (VS 2010 and 2012) as well as teaching labs to the members.</p>
                      <p>He has been awarded Microsoft MVP status since 2009 as a result of his community work such as this presentation as well as his blog and as an active resource on the MSDN forums and StackOverflow, answering questions of all types.</p>
             </td></tr>        
      
          </table>
      
      </section>
Share

Tags: , ,

ASP.NET MVC Application Step By Step

This application is built on the using the ASP.NET MVC Preview 3 using VC2008. The goal is to show the basics of MVC by creating a basic web site in a step by step manner. This article shows the creation of a basic web site, with login screen, work screen, validation error page and finally a results screen. It shows how to have the pages (views) rendered and data (model) passed between those pages as a state, with a validation of work and upon success the final page.

I like to build MVC ASP.Net web pages because it bespeaks to me of tier architecture. That is one programming paradigm which makes sense because of the separation of the differing layers and being able to work and test the layers outside of their intended box. When one has the ability to modularize actions, it leads to easier understandings of written code but more important easier maintenance. MVC, the Model (data objects), View (GUI look and feel) and Controller (your code which sits in between the user actions and page generation) allow for such modularity of code.

Hint: Most MVC tutorials tell you to get the MVC Toolkit along with the preview. Don’t bother, as of version 3 it is rolled in!

When the opportunity to use a new methodology in creating thin clients came along, I felt it was time to put my GUI architecture knowledge on a diet. As with corporate America, my current contract saw one of the bigwig(s) require that the team make an estimate for the costs of creating a thin client. Of course currently there is no thin client but an established click once winform application. Well when you work for a company that can cut a billion dollars from its budget and ask at the same time to rewrite a solid application, heck why not dream. It is going to happen right. 😉

In that scenario its best to look at the latest and greatest and fortunately this technology bespeaks to the thick client developer. So this is post is some of things I learned in researching it for my company.

Tribal Knowledge: there are three concepts to get to know about this MVC thin application.

Controller

Processing interactions between the user pages and MVC/page generation go here. This is an ISAPI type approach where the controller code, your .net code, intercepts the operations and interacts with it by working with the model data and determining a view.

Model

Contains the application object. Create class objects which represent data and state between the pages.

View

What the user works with/sees the forms.

Code Download

Here is the zipped code to download. Statement.zip

Preparation

Get the latest MVC preview and install it with Visual Studio 2008.

Goals

We will create a statement page, where a typical CSR will enter in search values and be directed to either a failure validation page or the data page. By that we will create a Controller, Model and Views.

Solution Setup

File->New Project->Visual C#->Web->ASP MVC Web Application and name the project Statement. Accept the default Visual Studio Test Project. Once done you will have a solution with two projects.

Statement SolutionThe things to be aware of are the folders found in the project named Controllers, Models and Views. We will be interacting with those folders primarily.  The following sections will detail how to add the models, views and controllers for the individual objects.

The project wizard has created a basic web site with one View (Home) that has two pages, the home/index page and also the about page. All of the pages are based on the Site.Master template. Later when we create pages we will also base our pages off of that site master which gives the pages a foundation which we will not have to mimic on each page. Each page inherits so to speak from that page and only presents the unique information/tags for that page.

There is only one controller out of the box which is the HomeController which intercepts the page requests which will route to the Home views of About and Index. Which brings us to our next topic of routing.

Routing

There have been quite a few articles on routing and I am not going to get into it here. But our goal is to keep with the routing currently setup by the default web site. We will have the user click on a Statement tab and be directed to the Statement views via the a new statement controller.

Note if you haven’t done this, build play around with the default website to get the feel of it.

Model Creation

Solution Explorer: Select the Models folder. Right Click->Add->Class We are going to create a model first. This class will handle the interactions between the pages and is just a basic class with properties. On the Models folder right click and add a class named StatementState which will just be three strings which our pages will be interested in. The class looks like this:

 1:
 2: namespace Statement.Models
 3: {
 4:     public class StatementState
 5:     {
 6:         public string PortfolioID  { get; set; }
 7:         public string CustomerID   { get; set; }
 8:         public string ErrorMessage { get; set; }
 9:     }
 10: }

Site.Master

The file Site.Master, as found in the Views / Shared folder acts as our base template page. It simply acts as a foundation for all future pages to apply their specific business logic on without worrying about the general architecture of the web site. On that page we need to add a link, an Action.Link to our future Statement page for the CSR to enter in the values to search for.  To accomplish that, open the file and add the code with our statement redirection as shown below:

<div id="header">
    <p id="logo">
        <a href="">My Sample MVC Application</a>
    </p>
    <ul id="menu">
        <li>
            <%= Html.ActionLink("Home", "Index", "Home")%>
        </li>
        <!-- Here is our code to add to create and route for our new tab -->
        <li>
           <%= Html.ActionLink("Statement", "Statement", "Statement") %>
       </li>
        <li>
            <%= Html.ActionLink("About Us", "About", "Home")%>
        </li>
    </ul>
</div>

We provide the special server tags with the Html.ActionLink to link/redirect to the Statement view. Go ahead and build the site. The new link tab will be between the home and about.

New Statement Tab

Views

New Statement Folder under Views

We will now add the views, or web pages which our statement operations pages will be joined to the site.master. In this section we will be adding three pages to compliment our view being Statement, ErrorAction and ViewStatement. To prepare for the pages create a Statement folder under the Views folder such as:

The video below demonstrates how to add View Content pages. The below visually shows how to create the Statement page.

Solution Explorer: Select Statement Folder: Right Click->Add->New Item->Visual C#->Web->MVC->MVC View Content Page which will need input and we will specify Statement as the page to create then select Add. The next dialog box Select a Master Page comes up and we can specify that this content page be associated with a particular master template page. Our template page is the site.master page and we will select Statement->Views->Shared->Site.Master and select OK.

We will need to add two more View Content pages just like above but their names will be ErrorAction and ViewStatement.

FinalViewContentPages

Statement View Contents Pages

In the statement page we are going to add two edit boxes to allow for user input specifics about the statement which are a portfolio and customer number. Those two items will be submitted by the form to our controller which we will write later. Here is the code to place in the Statement.aspx page:

<form action="/Statement/StatementSubmit">
   <table>
    <tr>
        <td>Portfolio ID:</td>
        <td><input type="text" name="portfolioID" /></td>
    </tr>
    <tr>
        <td>Customer ID:</td>
        <td><input type="text" name="customerID" /></td>
    </tr>
    </table>
  <input type="submit" value="Submit" />
</form>

We have two other web pages to contend with…the ErrorAction.aspx page will behave similar to the above statement page, but it is a validation target when validation fails and the user needs to be informed of as to the why and prepopulate the fields from the previous screen:

<!-- ErrorAction page to reset variables and inform user of the problem on the originating page -->
<form action="/Statement/StatementSubmit">
<% if (ViewData.Model.ErrorMessage != null) { %>
  <% } %>
   <table>
   <tr><td colspan="2"><fieldset><legend>Validation Failure</legend>
    The following error occurred while inserting the customer data:
    <br />
    <%= ViewData.Model.ErrorMessage%>
    <br />
    </fieldset></td></tr>
    <tr>
        <td>Portfolio ID:</td>
        <td><%= Html.TextBox("portfolioID", ViewData.Model.PortfolioID)%></td>
    </tr>
    <tr>
        <td>Customer ID:</td>
        <td><input type="text" name="customerID" value="<%= ViewData.Model.CustomerID %>"/></td>
    </tr>
    </table>
  <input type="submit" value="Submit" />
</form>

Finally we have the final page where we will take the user’s request and show the actual generated statement:

<!-- ViewStatement ... just regurgitate what the user typed in. -->
<%= Html.Encode(ViewData.Model.PortfolioID)%>
<br />
<%= Html.Encode(ViewData.Model.CustomerID)%>

Note all this code goes in the asp:Content tags!

Linking up with the Model

The final step with the views is to link up with our data model StatmentState. We do that by changing the class definition of both pages which will be using it which are ErrorAction and ViewStatement. We do that by changing how its inheritance.

Solution Explorer: Select Views\Statement\ErrorAction.aspx (then ViewStatment.aspx next) right click->View Code change the class inheritance to this and and the appropriate using:

 1:
 2: public partial class ErrorAction : ViewPage<StatementState>
 3:

This is important otherwise for the when the page is rendered it will access the StatementState class properly. Otherwise rendering will fail with object does not contain XXXX in class. Do the same for ViewStatment.aspx.

Controller

Ok the final frontier to get this to work is the controller. We need to create a StatementController. We will once again use the wizard to create the controller.

Solution Explorer: Select Controllers folder: Right Click->Add->New Item->Visual C#->Web->MVC->MVC Controller Class and name it StatmentController. Once done the wizard will create a class for us with the appropriate signature and plumbing to handle the calls. We just need to add the specifics of that plumbing such as:

 1: using Statement.Models;
 2:
 3: public class StatementController : Controller
 4: {
 5:     public ActionResult Statement()
 6:     {
 7:         ViewData["Title"] = "Statement Page";
 8:         return View();
 9:     }
 10:
 11:     public ActionResult StatementSubmit(string portfolioID, string customerID)
 12:     {
 13:         // TODO: Validate Choices.
 14:
 15:         StatementState state = new StatementState();
 16:
 17:         state.CustomerID = customerID;
 18:         state.PortfolioID = portfolioID;
 19:
 20:         if ( string.IsNullOrEmpty(state.PortfolioID) )
 21:         {
 22:             state.ErrorMessage = "Portfolio ID Required";
 23:             return View("ErrorAction", state);
 24:         }
 25:
 26:         return View("ViewStatement", state);
 27:     }
 28: }
  • Line 01: We need to pull in our data type StatementState which will be passed between the pages.
  • Line 05: This is needed when the user is directed to the Statement page. We handle the initial setup here.
  • Line 07: We will use the existing system to have the page sub title shown.
  • Line 08: The we simply say view it to display!
  • Line 15: Here is what we will pass to either the ErrorAction page or the final ViewStatement page.
  • Line 17: Assign the current value of the post to our state object for customer ID.
  • Line 18: Assign the current value of the post to our state object for the portfolio ID.
  • Line 20: We only have one validation rule…that is no empty portfolio number.
  • Line 22: Inform the user that they need to fill in the portfolio ID.
  • Line 23: Launch the ErrorAction page and pass in our specific model data. Its a nice way to redirect….
  • Line 26: Everything is fine…pass the info onto the view state to show.

That is it. Run the application, see how the validation will catch an empty portfolio ID and how it works in general.

We have a basic website built upon the foundation which Microsoft has given us. As MVC matures, this technology will only get better. I like were it is heading. Hope this little intro has helped you.

Share

Tags: ,