Find file History
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
..
Failed to load latest commit information.
Images
Slides
Source
README.md

README.md

Front End Development


Overview

In traditional web applications, the client (browser) initiates the communication with the server by requesting a page. The server then processes the request and sends the HTML of the page to the client. In subsequent interactions with the page (e.g. the user navigates to a link or submits a form with data) a new request is sent to the server, and the flow starts again: the server processes the request and sends a new page to the browser in response to the new action requested by the client.

In Single-Page Applications (SPAs) the entire page is loaded in the browser after the initial request, but subsequent interactions take place through Ajax requests. This means that the browser has to update only the portion of the page that has changed; there is no need to reload the entire page. The SPA approach reduces the time taken by the application to respond to user actions, resulting in a more fluid experience.

The architecture of a SPA involves certain challenges that are not present in traditional web applications. However, modern JavaScript frameworks like AngularJS, combined with HTTP services provided by ASP.NET Core, make it really easy to design and build SPAs.

In this module, you will take advantage of those technologies to implement a website based on the SPA concept. You'll first work with a rich and responsive UI using Angular 2 taking advantage of the Visual Studio Tooling. Then, you'll implement the service layer with ASP.NET Core API to expose the required endpoints. You'll then check your application for web standards compliance using the Vorlon.js Edge extension, and see how you can package it as an app using ManifoldJS.

Objectives

In this module, you will see how to:

  • Work with Single Page Applications using Angular 2
  • Leverage Visual Studio web tools
  • Integrate HTTP services using ASP.NET Core web api's
  • Test your applications for web standards compliance with the Vorlon.js Edge extension
  • Package your site as an app using ManifoldJS

Prerequisites

The following is required to complete this module:

Note: You can take advantage of the Visual Studio Dev Essentials subscription in order to get everything you need to build and deploy your app on any platform.

Setup

In order to run the exercises in this module, you will need to set up your environment first.

  1. Open Windows Explorer and browse to the module's Source folder.
  2. Right-click Setup.cmd and select Run as administrator to launch the setup process that will configure your environment and install the Visual Studio code snippets for this module.
  3. If the User Account Control dialog box is shown, confirm the action to proceed.

Note: Make sure you have checked all the dependencies for this module before running the setup.

Ensure Developer Mode is enabled on Windows 10 by going to settings > Update & Security > For developers and selecting Developer mode.

Using the Code Snippets

Throughout the module document, you will be instructed to insert code blocks. For your convenience, most of this code is provided as Visual Studio Code Snippets, which you can access from within Visual Studio 2015 to avoid having to add it manually.

Note: Each exercise is accompanied by a starting solution located in the Begin folder of the exercise that allows you to follow each exercise independently of the others. Please be aware that the code snippets that are added during an exercise are missing from these starting solutions and may not work until you have completed the exercise. Inside the source code for an exercise, you will also find an End folder containing a Visual Studio solution with the code that results from completing the steps in the corresponding exercise. You can use these solutions as guidance if you need additional help as you work through this module.


Exercises

This module includes the following exercises:

  1. Creating an Angular 2 application
  2. Exploring a more advanced Angular 2 application in an ASP.NET Core application
  3. Integrating with ASP.NET Core API controller
  4. Developing for Web Standards with Edge and Vorlon.js
  5. Packaging SPA as an installable App using ManifoldJS

Estimated time to complete this module: 60 minutes

Note: When you first start Visual Studio, you must select one of the predefined settings collections. Each predefined collection is designed to match a particular development style and determines window layouts, editor behavior, IntelliSense code snippets, and dialog box options. The procedures in this module describe the actions necessary to accomplish a given task in Visual Studio when using the General Development Settings collection. If you choose a different settings collection for your development environment, there may be differences in the steps that you should take into account.

Exercise 1: Creating an Angular 2 application

Angular is a development platform for building mobile and desktop web applications using modern web standards. Is an open-source JavaScript framework that augments browser-based applications with Model-View-Controller (MVC) capability, facilitating both development and testing.

In this exercise, you will learn the basics of Angular 2 taking advantage of the official documentation.

Task 1 - Introducing the Angular 2 documentation

In this task, you will explore some of the resources that Angular 2 have to start working with it.

  1. Open Microsoft Edge and navigate to the Angular 2 site. The home page includes some basic examples as well as some of the features included as part of Angular 2.

  2. Navigate to the Angular 2 Doc's page by clicking the DOCS button located at the top menu. In that page you can find several resources.

  3. Navigate to the Angular Cheat Sheet located under the GUIDE section in the left menu. In this page you can find a summary of the most useful template syntaxes, build-in directives, forms directives, class decorators and more.

  4. If you have experience working with Angular 1, you can find useful the Angular 1 to 2 Quick ref page, located under the COOKBOOK section in the left menu.

  5. The documentation site includes a lot of more advanced content for you to look at later, including full docs, an full tutorial and more.

Task 2 - Exploring the Angular 2 Quickstart

In this task, you will explore the code in the 5 Minutes Quickstart page.

  1. In Microsoft Edge, navigate to the 5 Minutes Quickstart page and click the live example link in the See It Run section.

    Note: If you have any issues opening the live example link, you can find the files in the Source/Ex1/Begin folder.

    Openning the live sample

    Openning the live sample

  2. In the quickstart's live preview, open the index.html file and locate the dependencies required by Angular 2 under the <!-- 1. Load libraries --> comment. All of the libraries are hosted on unpkg which is a content delivery network for libraries published to npm.

    <!-- 1. Load libraries -->
     <!-- Polyfill(s) for older browsers -->
    <script src="https://unpkg.com/core-js/client/shim.min.js"></script>
    
    <script src="https://unpkg.com/zone.js@0.6.25?main=browser"></script>
    <script src="https://unpkg.com/reflect-metadata@0.1.8"></script>
    <script src="https://unpkg.com/systemjs@0.19.39/dist/system.src.js"></script>

    Note: The following is a brief description of each library.

    • Core-js Shim: This library provides compatibility shims so that legacy JavaScript engines behave as closely as possible to ECMAScript 6 and beyond.
    • Zone.js: Implements Zones in JavaScript which are execution contexts that persists across async tasks.
    • reflect-metadata: A polyfill for the proposed Metadata Reflection API.
    • SystemJS: An universal dynamic module loader - loads ES6 modules, AMD, CommonJS and global scripts in the browser and NodeJS. Works with both Traceur and Babel.
  3. Note that there are no references to the applications files. The main.ts and the app.component.ts files are loaded by SystemJS, in order to do that, System.js is configured in the systemjs.config.js file to transpile the TypeScript files and import the main file.

    <!-- 2. Configure SystemJS -->
    <script src="systemjs.config.js"></script>
    <script>
      System.import('app').catch(function(err){ console.error(err); });
    </script>
  4. In order to add the Angular application to your index.html file you only need to add the my-app tag inside the body. The tag name is the one defined as the selector in your Component.

    <!-- 3. Display the application -->
    <body>
        <my-app>Loading...</my-app>
    </body>
  5. Now, open to the main.ts file located at the app folder by clicking in the app/main.ts link under FILES in the left menu.

    Openning the main.ts file

    Openning the main.ts file

  6. The app/main.ts file is the first one loaded by System.JS as it was the one specified in the import method call. Note that this file contains two imports which loads the components from the files specified after the from statement.

    import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
    
    import { AppModule } from './app.module';
    
    const platform = platformBrowserDynamic();
    platform.bootstrapModule(AppModule);
  7. Open the app.component.ts file located at the app folder. Every Angular app has at least one root component, conventionally named AppComponent, that hosts the client user experience. Components are the basic building blocks of Angular applications. A component controls a portion of the screen — a view — through its associated template.

    import { Component } from '@angular/core';
    
    @Component({
    selector: 'my-app',
    template: '<h1>My First Angular App</h1>'
    })
    export class AppComponent { }
  8. Note the Component decorator of the AppComponent class. This decorator takes a metadata object including the selector and the template. The selector specifies a simple CSS selector for and HTML element that represents the component, in this case the my-app tag. The template specifies the component's companion template, written in an enhanced form of HTML that tells Angular how to render this component's view.

    @Component({
        selector: 'my-app',
        template: '<h1>My First Angular 2 App</h1>'
    })
  9. Update the AppComponent class implementation by adding a new private property named name with your name as value (e.g. John Doe)

    export class AppComponent {
      private name = "John Doe";
    }
  10. Now update the template property in the @Component decoration with data bindings to this new property.

    @Component({
        selector: 'my-app',
        template: '<h1>Hello {{name}}!</h1>'
    })

    Updating the quickstart sample

    Updating the quickstart sample

Exercise 2: Exploring a more advanced Angular 2 application in an ASP.NET Core application

In a real world application, you will generally have more than a single component and they will be more complex, not only showing the data using bindings but enabling editing it and changing the behavior based on the state. Additionally, you might use complex component like grids that supports different kind of events as well as editing the values that are displayed.

In this exercise, you'll explore a more advanced application that uses Angular 2 in the front end and ASP.NET Core in the back end. You'll also explore the Visual Studio tooling that make your life easier when developing this kind of applications.

Note: OrderApp is showing mock data at this point. In the next exercise, you'll wire it up to the back-end. Additionally, as this module will only cover part of the solution, we encourage you to explore and play with the application after completing the module.

Task 1 - Exploring the application

In this task, you will explore the OrderApp application which uses two grids to show all the orders from a store and its details whenever you select one of the orders in the main grid.

  1. Open Visual Studio Community 2015 and the OrderApp.sln solution located in the Source/Ex2/Begin folder. Compile the solution and wait until all the dependencies are loaded.

  2. Expand the js folder inside wwwroot and notice that you this solution uses TypeScript. Visual Studio have great support for working with TypeScript. By compiling the application, you will obtain the transpiled version of the typescript files, enabling you to consume JavaScript files directly instead of TypeScript files.

    TypeScript files in wwwroot

    TypeScript files in wwwroot

  3. Open the tsconfig.json file located at the root of the OrderApp project and notice that this file configures TypeScript, enabling the use of decorators and setting system for modules.

    {
        "compilerOptions": {
            "noImplicitAny": false,
            "noEmitOnError": true,
            "removeComments": false,
            "sourceMap": true,
            "target": "es5",
            "emitDecoratorMetadata": true,
            "experimentalDecorators": true,
            "module": "system",
            "moduleResolution": "node"
        },
        "exclude": [
            "node_modules",
            "wwwroot/lib"
        ]
    }
  4. Right-click the npm folder under Dependencies and select Open package.json in order to open the npm package.json file.

    Openning the package.json file

    Openning the package.json file

  5. Locate the dependencies node inside the package.json file. In that node, you can find the Angular 2 dependencies described in the previous exercise. Additionally, note that there are two additional dependencies ag-grid and ag-grid-ng2 which enable you to use a grid component in your application.

    {
    "name": "ASP.NET",
    "version": "0.0.0",
    "scripts": {
      "start": "tsc && concurrently \"tsc -w\" \"lite-server\" ",
      "lite": "lite-server",
      "tsc": "tsc",
      "tsc:w": "tsc -w"
    },
    "dependencies": {
      "@angular/common": "~2.1.1",
      "@angular/compiler": "~2.1.1",
      "@angular/core": "~2.1.1",
      "@angular/forms": "~2.1.1",
      "@angular/http": "~2.1.1",
      "@angular/platform-browser": "~2.1.1",
      "@angular/platform-browser-dynamic": "~2.1.1",
      "@angular/router": "~3.1.1",
      "@angular/upgrade": "~2.1.1",
      "ag-grid": "6.2.x",
      "ag-grid-ng2": "6.2.x",
      "angular-in-memory-web-api": "~0.1.13",
      "bootstrap": "^3.3.7",
      "core-js": "^2.4.1",
      "reflect-metadata": "^0.1.8",
      "rxjs": "5.0.0-beta.12",
      "systemjs": "0.19.40",
      "zone.js": "^0.6.25"
    },
    "devDependencies": {
      "gulp": "3.9.1",
      "gulp-concat": "2.6.0",
      "gulp-cssmin": "0.1.7",
      "gulp-uglify": "2.0.0",
      "rimraf": "2.5.4",
      "@types/core-js": "^0.9.34",
      "@types/node": "^6.0.45",
      "concurrently": "^3.0.0",
      "lite-server": "^2.2.2",
      "typescript": "^2.0.3"
    }
    }
  6. These dependencies will be located at the node_modules folder which is not served by the web server. In order to be able to consume them, you will need to move the inside the wwwroot folder. Open the gulpfile.js file located at the root of the OrderApp project.

  7. In the gulpfile.js file you can find some tasks that will copy each of the dependencies to the lib/npmlibs/ folder inside wwwroot. The main task is moveToLibs which depends on the moveToLibs:singleFiles as well as all the copy-deps tasks.

    // ...
    
    gulp.task("copy-deps:ag-grid", function () {
    return gulp.src([paths.npmSrc + '/ag-grid/main.js',
                paths.npmSrc + '/ag-grid/dist/**/*.js',
                paths.npmSrc + '/ag-grid/dist/styles/*.*'
    ], { base: paths.npmSrc + '/ag-grid/' })
         .pipe(gulp.dest(paths.npmLibs + '/ag-grid/'));
    });
    
    gulp.task('moveToLibs:singleFiles', function () {
    return gulp.src(libsToMove).pipe(gulp.dest(paths.npmLibs));
    });
    
    gulp.task("moveToLibs", ["moveToLibs:singleFiles", "copy-deps:ag-grid-ng2", "copy-deps:ag-grid", 'copy-deps:angular2', 'copy-deps:systemjs', 'copy-deps:rxjs']);
    
  8. In the Task Runner Explorer view, right-click the moveToLibs task and select Run to execute the task. Note that in the same menu, you can bind the tasks to before or after the build, the clean of the project or when the project opens.

    Running the moveToLibs task

    Running the moveToLibs task

  9. Open the _Layout.cshtml file located under the Views/Shared folder. Locate all the dependencies at the end of the head tag. Note that this files are loaded from the ~/lib/npmlibs/ folder which is where the gulp tasks is moving the dependencies.

    Dependencies loaded from the npmlibs folder

    Dependencies loaded from the npmlibs folder

  10. Open the Index.cshtml file located under the Views/Home folder. Note that there is an order-app tag which is the main component's selector. Additionally, you can a script tag with a src pointing to the systemjs.config.js configuration file as well as the import of the main file.

    Index.cshtml file with System.js configuration

    Index.cshtml file with System.js configuration

  11. Now, open the main.ts file located at the js folder inside wwwroot. This file bootstraps the root App module located in the app.module.ts file.

  12. Open the app.module.ts file. In the top of the file, you will find several imports including imports that correspond to all the components used in this module. Every Angular app has at least one module, the root module, conventionally named AppModule. Modules may contain multiple components and may import other modules whose components are needed by the component templates.

    import { NgModule }      from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { AppComponent }   from './app.component';
    import { AgGridModule } from 'ag-grid-ng2/main';
    import { DetailsGridComponent } from "./details-grid.component";
  13. Open the app.component.ts file. In the top of the file, you will find several imports including one that corresponds to AgGrid and one for the OrderService service.

    import { Component, OnInit } from "@angular/core";
    
    import { GridOptions } from "ag-grid/main";
    import RefData from "./refData";
    import { IItemInfo } from "./itemInfo";
    import { OrderService } from "./order.service";
  14. Note that the OrderApp component defines its template in a separated html file named order-app.html which is located in the html folder. Open this file to see how the ag-grid and details-grid components are used inside this component.

    <ag-grid-ng2 #agGrid id="master-grid" class="ag-blue"
                    [gridOptions]="gridOptions"
                    [columnDefs]="columnDefs"
                    [rowData]="rowData"
                    enableColResize
                    enableSorting
                    enableFilter
                    groupHeaders
                    rowHeight="22"
                    rowSelection="single"
                    suppressCellSelection
                    (modelUpdated)="onModelUpdated()"
                    (rowClicked)="onRowClicked($event)"
                    (ready)="onReady($event)">
    </ag-grid-ng2>
    
    <details-grid [itemInfo]="selectedItem" (updatedTotal)="updateOrderTotal($event)"></details-grid>
  15. Back in the app.component.ts file, locate the Component decorator and notice the providers property. This property is used to configure the dependency injection system in this case configuring the OrderService that its consumed in the class's constructor.

    constructor(private _orderService: OrderService) {
        // we pass an empty gridOptions in, so we can grab the api out
        this.gridOptions = <GridOptions>{};
        this.createColumnDefs();
    }
  16. In order to be able to inject a class you need that it has the Injectable decorator. Open the order.service.ts file and verify that this service has this decorator. Note that this service is using mock data, in the next exercise you will update it to consume an API to retrieve and save the data in the server.

Task 2 - Running the Solution

In this task, you will execute the solution.

  1. Press F5 to run the solution.

    Running the OrderApp solution

    Running the OrderApp solution

  2. Select one of the rows to see the details of that specific order.

    Selecting an order

    Selecting an order

  3. Modify the quantities of one of the items in the order and verify that the totals fields are updated. You can also update the comments field.

    Editing an Order item

    Editing an Order item

  4. Go back to Visual Studio and press SHIFT + F5 to stop debugging.

Exercise 3: Integrating with ASP.NET Core API controller

One of the key parts of a SPA is the service layer. It is responsible for processing the Ajax calls sent by the UI and returning data in response to that call. The data retrieved should be presented in a machine-readable format in order to be parsed and consumed by the client. ASP.NET Core is designed to make it easy to implement HTTP services, generally sending and receiving JSON- or XML-formatted data through a RESTful API.

In this exercise, you'll create an ASP.NET Core API and then update the OrderService in the Angular application to call the new API to retrieve and store the information.

Task 1 - Creating an ASP.NET Core API controller

In this task, you'll build a simple API service that interacts with the OrderApp's model and exposes the following actions:

  • GET /api/orders: Retrieves all the available orders.
  • GET /api/orders/{id}: Retrieves all the order's details including all items that are part of that order.
  • PUT /api/orderDetails/{id}: Stores the changes in the existing order details' items like quantities and comments.

You'll use ASP.NET Core API, creating an API Controller class to each of the endpoints (i.e. orders and orderDetails).

  1. Open Visual Studio Community 2015 and the OrderApp.sln solution located in the Source/Ex2/Begin folder. Alternatively, you can continue with the solution that you obtained in the previous exercise.

  2. In Solution Explorer, right-click the ViewModels folder of the OrderApp project and select Add | Class.... Name the class file OrderViewModel.cs and click Add.

  3. Replace the OrderViewModel class with the following code snippet.

    (Code Snippet - FrontEndDev - Ex3 - OrderViewModelClass)

    public class OrderViewModel
    {
        public int Id { get; set; }
    
        public string Date { get; set; }
    
        public decimal OrderTotal { get; set; }
    
        public string Name { get; set; }
    
        public string Address { get; set; }
    
        public string Country { get; set; }
    
        public string Continent { get; set; }
    
        public string Language { get; set; }
    
        public string Phone { get; set; }
    }
  4. Add a new view model class. To do this, in Solution Explorer, right-click the ViewModels folder of the OrderApp project and select Add | Class.... This time, name the class file OrderDetailsItem.cs and click Add.

  5. Replace the OrderDetailsItem class with the following code snippet.

    (Code Snippet - FrontEndDev - Ex3 - OrderDetailsItemClass)

    public class OrderDetailsItem
    {
        public int OrderDetailsId { get; set; }
    
        public int ProductId { get; set; }
    
        public string ProductName { get; set; }
    
        public int Quantity { get; set; }
    
        public decimal Price { get; set; }
    
        public string Comments { get; set; }
    
        public decimal Total { get { return this.Price * this.Quantity;  } }
    }
  6. In Solution Explorer, right-click the Controllers folder of the OrderApp project and select Add | New Item....

  7. In the Add New Item dialog box, make sure that the DNX | Server-side node is selected in the left pane. Then, select the Web API Controller Class template in the center pane, name it OrdersController and click Add.

    Selecting the Web API Controller Class template

    Selecting the Web API Controller Class template

  8. The OrdersController.cs file is then added to the Controllers folder of the OrderApp project, containing a base OrdersController class. Remove all the content of the class and add the following using statements at the beginning of the file.

    (Code Snippet - FrontEndDev - Ex3 - OrdersControllerUsings)

    using Microsoft.EntityFrameworkCore;
    using OrderApp.Models;
    using OrderApp.ViewModels;
  9. Add the following code at the OrdersController class to define, initialize and dispose the OrdersContext instance in the controller.

    (Code Snippet - FrontEndDev - Ex3 - OrdersControllerContext)

    namespace OrderApp.Controllers
    {
         [Route("api/[controller]")]
         public class OrdersController : Controller
         {
              private OrdersContext context;
    
              public OrdersController(OrdersContext context)
              {
                    this.context = context;
              }
    
              protected override void Dispose(bool disposing)
              {
                    if (disposing)
                    {
                         this.context.Dispose();
                    }
    
                    base.Dispose(disposing);
              }
         }
    }

    Note 1: The Dispose method of OrdersController invokes the Dispose method of the OrdersContext instance, which ensures that all the resources used by the context object are released when the OrdersContext instance is disposed or garbage-collected. This includes closing all database connections opened by Entity Framework.

    Note 2: The OrdersContext will be automatically injected by ASP.NET Core thanks to the configuration in the Startup class.

  10. Add the following Get action method to the OrdersController class. This action method retrieves all the orders from the context and creates a new OrderViewModel instance for each one.

    (Code Snippet - FrontEndDev - Ex3 - OrdersControllerGetAction)

    // GET: api/orders
    [HttpGet]
    public IEnumerable<OrderViewModel> Get()
    {
        return this.context.Orders.Select(o =>
            new OrderViewModel()
            {
                Address = o.Address,
                OrderTotal = o.OrderDetails.Select(od => od.Quantity * od.Product.Price).Sum(),
                Continent = o.Continent,
                Country = o.Country,
                Language = o.Language,
                Date = o.Date.ToString(),
                Name = o.Name,
                Id = o.OrderId,
                Phone = o.Phone
            });
    }
  11. Add the following GetOrder action method to the OrdersController class. This action method retrieves the order details from the context and create a new OrderDetailsItem instance for each one of the OrderDetail instances that match the order's id.

    (Code Snippet - FrontEndDev - Ex3 - OrdersControllerGetOrderAction)

    // GET: api/orders/5
    [HttpGet("{id}", Name = "GetOrder")]
    public async Task<IActionResult> GetOrder([FromRoute] int id)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
    
        var order = await this.context.Orders
                                            .Include(o => o.OrderDetails)
                                            .ThenInclude(od => od.Product)
                                            .SingleAsync(m => m.OrderId == id);
    
        if (order == null)
        {
            return NotFound();
        }
    
        return Ok(order.OrderDetails.Select(orderDetails => new OrderDetailsItem()
        {
            Comments = orderDetails.Comments,
            OrderDetailsId = orderDetails.OrderDetailsId,
            ProductName = orderDetails.Product.Name,
            ProductId = orderDetails.Product.ProductId,
            Price = orderDetails.Product.Price,
            Quantity = orderDetails.Quantity
        }));
    }
  12. Modify the API controller to produce json by adding the Produces attribute to the OrdersController class definition.

    namespace OrderApp.Controllers
    {
         [Produces("application/json")]
         [Route("api/[controller]")]
         public class OrdersController : Controller
         {
              // ...
         }
    }
  13. Create a new controller named OrderDetailsController. To do this, in Solution Explorer, right-click the Controllers folder of the OrderApp project and select Add | New Item.... In the Add New Item dialog box, make sure that the DNX | Server-side node is selected in the left pane. Then, select the Web API Controller Class template in the center pane, name it OrderDetailsController and click Add.

  14. In the OrderDetailsController.cs file, replace all the content of the file with the following code snippet to add the using statements and initialize and dispose the OrdersContext instance in the controller in the same way that was performed in the other controller.

    (Code Snippet - FrontEndDev - Ex3 - OrderDetailsControllerBase)

    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Http;
    using Microsoft.EntityFrameworkCore;
    using OrderApp.Models;
    using OrderApp.ViewModels;
    
    namespace OrderApp.Controllers
    {
         [Produces("application/json")]
         [Route("api/[controller]")]
         public class OrderDetailsController : Controller
         {
              private OrdersContext context;
    
              public OrderDetailsController(OrdersContext context)
              {
                    this.context = context;
              }
    
              protected override void Dispose(bool disposing)
              {
                    if (disposing)
                    {
                         this.context.Dispose();
                    }
    
                    base.Dispose(disposing);
              }
         }
    }
  15. Add the following PutOrderDetails action method to the OrderDetailsController class. This action method saves the changes in the details's item.

    (Code Snippet - FrontEndDev - Ex3 - OrderDetailsControllerPutOrderDetailsAction)

    // PUT: api/OrderDetails/5
    [HttpPut("{id}")]
    public async Task<IActionResult> PutOrderDetails([FromRoute] int id, [FromBody] OrderDetailsItem orderDetails)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
    
        if (id != orderDetails.OrderDetailsId)
        {
            return BadRequest();
        }
    
        var storedOrderDetails = await this.context.OrderDetails.SingleAsync(m => m.OrderDetailsId == id);
    
        if (storedOrderDetails == null)
        {
            return NotFound();
        }
    
        storedOrderDetails.Quantity = orderDetails.Quantity;
        storedOrderDetails.Comments = orderDetails.Comments;
    
        await this.context.SaveChangesAsync();
    
        return new StatusCodeResult(StatusCodes.Status204NoContent);
    }

Task 2 - Integrating the front end with the API controller

In this task, you will use Angular's Http service to perform http requests to the API created in the previous task using XMLHttpRequest.

  1. Open the app.module.ts file located in the js folder under wwwroot.

  2. Add a new import statement for HTTP_PROVIDERS from @angular/http and add it to the imports array in the NgModule decorator.

    import { NgModule }      from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { HttpModule } from '@angular/http';
    
    import { AppComponent }   from './app.component';
    import { AgGridModule } from 'ag-grid-ng2/main';
    import { DetailsGridComponent } from "./details-grid.component";
    
    @NgModule({
    imports: [BrowserModule, AgGridModule.withNg2ComponentSupport(), HttpModule],
    declarations: [AppComponent, DetailsGridComponent],
    bootstrap: [AppComponent]
    })
    export class AppModule { }
  3. Open the order.service.ts file located in the js folder under wwwroot.

  4. In the top of the order.service.ts file, remove the MockOrders imports and add the following import to use the Http service.

    import { Http, Response, Headers, RequestOptions } from '@angular/http';
  5. Add the following imports to be able to use the specific rxjs libraries needed as opposed to importing the entire heavy Rx.js library.

    //import "rxjs/Rx"; // adds ALL RxJS statics & operators to Observable
    
    // Statics
    import 'rxjs/add/observable/throw';
    
    // Operators
    import 'rxjs/add/operator/catch';
    import 'rxjs/add/operator/debounceTime';
    import 'rxjs/add/operator/distinctUntilChanged';
    import 'rxjs/add/operator/map';
    import 'rxjs/add/operator/switchMap';
    import 'rxjs/add/operator/toPromise';
  6. Add two new private properties to the OrderService class, one for each of the endpoints.

    private orderEndpoint = "/api/orders/";
    private orderDetailsEndpoint = "/api/orderDetails/";
  7. Add the constructor of the OrderService class below the new properties to be able to inject the Http service and consume it from the methods.

    private orderEndpoint = "/api/orders/";
    private orderDetailsEndpoint = "/api/orderDetails/";
    
    constructor(private http: Http) {
    }
  8. Replace the implementation of the getOrders method with the following code that uses the get method of the Http service.

    getOrders(): Promise<IItemInfo[]> {
        var headers = new Headers();
        headers.append("If-Modified-Since", "Mon, 27 Mar 1972 00:00:00 GMT");
    
        return this.http.get(this.orderEndpoint, { headers: headers })
            .map((res: Response) => <IItemInfo[]>res.json())
            .toPromise();
    }
  9. Replace the implementation of the getOrderDetails method with the following one.

    getOrderDetails(id: number): Promise<any> {
        var headers = new Headers();
        headers.append("If-Modified-Since", "Mon, 27 Mar 1972 00:00:00 GMT");
        let options = new RequestOptions({ headers: headers });
    
        return this.http.get(this.orderEndpoint + id, options)
            .map((res: Response) => res.json())
            .toPromise();
    }
  10. Replace the implementation of the updateOrderDetails method with the following code that take advantage of the put method of the Http service to send the updated orderDetails.

    updateOrderDetails(orderDetails: any): Promise<any> {
        let body = JSON.stringify(orderDetails);
        let headers = new Headers({ "Content-Type": "application/json" });
        let options = new RequestOptions({ headers: headers });
    
        return this.http.put(this.orderDetailsEndpoint + orderDetails.orderDetailsId, body, options)
            .toPromise();
    }

Task 3 - Running the Solution

In this task, you will execute the solution to verify that the data is loaded and stored in the backend.

  1. In Visual Studio, press F5 to run the solution.

  2. Once the site is running in Microsoft Edge, open the F12 Developers Tools by pressing F12 and switch to the Network tab. Make sure that its recording and clear the session.

    Networks tab of the F12 Developers Tools

    Networks tab of the F12 Developers Tools

  3. Select one of the rows to see the details of that specific order.

    Selecting an order

    Selecting an order

  4. Switch back to the F12 Developers Tools to see the new request. Select the request from the list and then select the Body tab from the details to see the JSON response.

    Body details of the GET request

    Body details of the GET request

  5. Modify the quantities of one of the items in the order and verify that the totals fields are updated. You can also update the comments field.

    Editing an Order item

    Editing an Order item

  6. Switch back to the F12 Developers Tools to see the new request. Select the new request from the list and then select the Request Body tab instead of Response body.

    Body details of the PUT request

    Body details of the PUT request

  7. Close the F12 Developers Tools and refresh the browser by pressing F5 to verify that the fields were updated in the database.

  8. Go back to Visual Studio and press SHIFT + F5 to stop debugging.

Exercise 4: Developing for Web Standards with Edge and Vorlon.js

Vorlon.JS is a remote debugging and testing tool for JavaScript. It helps you remotely load inspect, test and debug JavaScript code running on any device with a web browser: whether it's a games console, mobile device or fridge.

In this exercise you will take advantage of using Vorlon.JS to improve your site.

Task 1 - Using Vorlon.js Extension

In this task you will use the Vorlon.js Extension to analyze your site in order to detect issues.

  1. Open Microsoft Edge, click the ... dropdown menu button in the navigation bar and click the Extensions menu item. Click the Get extensions from the Store link and wait for the Store app to load. Use the search bar to search for the "Page Analyzer" app and install it.

  2. Open Visual Studio Community 2015 and the OrderApp.sln solution located in the Source/Ex3/End folder. Alternatively, you can continue with the solution that you obtained in the previous exercise.

  3. Run the solution in Microsoft Edge by pressing F5 in Visual Studio.

  4. In Microsoft Edge, start the Vorlon.js extension.

  5. Once the Vorlon.js page appears, select the Home Page - OrderApp site under the TAB LIST located at the left panel and run the analysis by clicking the Start checks button on the top bar.

    Starting the checks for OrderApp

    Starting the checks for OrderApp

  6. Once the analysis is completed, explore the results by clicking the rules to get more context about each of them.

    Page analyzer results

    Page analyzer results

  7. The analysis is designed to provide guidance regarding web standards, accessibility, performance and the mobile web. Expand all of the categories and explore the results.

    Exploring all the categories

    Exploring all the categories

Task 2 - Using Vorlon.js server

Vorlon.JS itself is a small web server you can run from your local machine, or install on a server for your team to access, that serves the Vorlon.JS dashboard and communicates with your remote devices. Installing the Vorlon.JS client in your web site or application is as easy as adding a single script tag.

In this task, you will set up Vorlon.JS and integrate it in your web application.

  1. Open a Command Prompt and validate that you have npm installed by executing npm -v.

  2. Install vorlon.js by executing the following command in the Command Prompt.

    npm install -g vorlon
    
  3. In a Command Prompt, run the vorlon command to initialize the vorlon.js server. Don't close it until the end of this exercise.

  4. In Visual Studio, open the Layout.cshtml file located at the Views/Shared folder and add the following script tag under the title tag inside the head tag.

    <script src="http://localhost:1337/vorlon.js"></script>
  5. Run the web application using Ctrl + F5 and don't stop it as you will need the app running for the following steps.

  6. Open another instance of Microsoft Edge and navigate to http://localhost:1337/ in order to see the Vorlon.JS dashboard.

  7. Select the Windows - 0 option under CLIENT LIST the left panel and explore the different features by selecting the tabs in the top menu of the Vorlon.JS dashboard.

    Using Vorlon.js Dashboard

    Using Vorlon.js Dashboard

Exercise 5: Packaging SPA as an installable App using ManifoldJS

Hosted Apps are web apps submitted to app markets that point to web content that remains on the web server as appose to being packaged and submitted to the store.

ManifoldJS helps you reach more users by packaging your web experience as native apps across Android, iOS, Windows, Firefox OS, and Chrome. Its takes the meta-data about your site taking advantage of the W3C Manifest for web apps and bookmarks standard and generates native "hosted" apps.

In this exercise, you will use ManifoldJS to create a Hosted Web Application for your Web application.

Task 1 - Creating a Hosted Web Application with ManifoldJS

In essence, a Hosted Web App is a container app. The content of the app (or the majority of the content) is hosted on a web server. To the customer, a Hosted Web App looks and feels like any other Store app. For the developer, the app is a standalone instance of the web app with all the features and privileges of a Store app. The same code that runs in the Hosted Web App can also run in a browser, as well as be reused on other platforms.

In this task, you will create a Hosted Web Application using ManifoldJS and the W3C Manifest.

  1. Open Visual Studio Community 2015 and the OrderApp.sln solution located in the Source/Ex4/End folder. Alternatively, you can continue with the solution that you obtained in the previous exercise.

  2. In Solution Explorer, right-click the wwwroot folder and select Add | New Item.... Select JSON File under the Code menu, change the name to manifest.json and click Add.

    Creating the manifest.json file

    Creating the manifest.json file

  3. In the manifest.json add the following code snippet to declare the sites information using the W3C Manifest for Web Apps standard.

    {
      "name": "OrderApp",
      "short_name": "OrderApp",
      "start_url": "http://localhost:55804/",
      "icons": [
         {
            "src": "images/splashscreen.png",
            "sizes": "620x300"
         },
         {
            "src": "images/logo.png",
            "sizes": "150x150"
         },
         {
            "src": "images/widelogo.png",
            "sizes": "310x150"
         },
         {
            "src": "images/storelogo.png",
            "sizes": "50x50"
         },
         {
            "src": "images/logo44.png",
            "sizes": "44x44"
         },
         {
            "src": "images/smalllogo.png",
            "sizes": "30x30"
         }
      ],
      "splash_screens": [
         {
            "src": "images/splashscreen.png",
            "sizes": "620x300"
         }
      ],
      "mjs_api_access": [
         {
            "match": "http://localhost:55804/*",
            "access": "all",
            "platform": "windows10"
         }
      ],
      "theme_color": "#7D89A2"
    }
  4. Copy the images from the Source/Ex5/Assets folder to the wwwroot/images folder.

    Assets' images in wwwroot

    Assets' images in wwwroot

  5. Open the Layout.cshtml file located at the Views/Shared folder and add the following line under the title tag inside the head tag. You can take this oportunity to remove the tag you added in the previous exercise for the vorlon.js client.

    <link rel="manifest" href="manifest.json">
  6. Run the web application using Ctrl + F5 and don't stop it as you will need the app running for the following steps.

  7. Open a Command Prompt and validate that you have npm installed by executing npm -v.

  8. Install ManifoldJS by executing the following command in the Command Prompt.

    npm install -g manifoldjs
    
  9. Once ManifoldJS is installed, run the following command in the same Command Prompt to see all the possible parameters for manifoldjs.

    manifoldjs -h
    
  10. Run the following command to create the Windows 10 app based on the W3C Manifest previously created in the parent folder or any other folder. Note that we are specifying the platform to be Windows 10. Additionally, note that the port number should match the ones specified in the manifest.json and the one the app is actually running.

    manifoldjs http://localhost:55804/ -p windows10
    
  11. Navigate to the generated OrderApp folder using the cd command and run the solution by executing the following command

    manifoldjs run windows10
    

    OrderApp running as a Hosted Web App

    OrderApp running as a Hosted Web App

  12. You can explore the generated content. In order to build and submit the application to the store, review the instructions in the Windows10-next-steps.md document located inside the windows10 folder.

Task 2 - Adding toast and live tiles to your Hosted Web Application

Hosted Web Apps in Windows 10 have the same advantages as any other Windows app, which means you can take advantage of platform specific APIs such as Live Tiles, back button, secondary pinning and support for Cortana interactions. Generally, users will expect the Store app to provide them with some additional functionality they can’t get in the browser, so it’s highly recommended you augment your web app with some of these features when moving outside the browser and into the Hosted Web App. You don’t need to fork your code to do this. Just like any web API, Windows 10 APIs can be feature detected.

In this task, you will add live tiles and toast notifications support to your site to be used from inside the Hosted Web App.

  1. Switch back to Visual Studio. In Solution Explorer, right-click the js folder located under wwwroot and select Add | New Item.... Select JavaScript File under the Client-Side menu, change the name to windows-features.js and click Add.

  2. Add the following code snippet to perform a feature test to make sure we only execute the calls for Windows 10 APIs when we are actually inside a Windows 10 app.

    (Code Snippet - FrontEndDev - Ex5 - WindowsFeatureTest)

    (function (window) {
         if (window.Windows) {
              // add in Windows features...
         }
    }(window));
  3. Add the following function above the if statement. This function will create and display a sample toast notification.

    (Code Snippet - FrontEndDev - Ex5 - AddToastNotification)

    function AddToastNotification() {
        var notifications = window.Windows.UI.Notifications;
    
        // Generate the toast notification template
        var template = notifications.ToastTemplateType.toastText01;
        var toastXml = notifications.ToastNotificationManager.getTemplateContent(template);
    
        // Set the toast text
        var toastTextElements = toastXml.getElementsByTagName("text");
        toastTextElements[0].appendChild(toastXml.createTextNode("Order App Toast notification!"));
    
        // Create the toast notification based on the XML content you've specified
        var toast = new notifications.ToastNotification(toastXml);
    
        // Send the toast notification
        var toastNotifier = notifications.ToastNotificationManager.createToastNotifier();
        toastNotifier.show(toast);
    }
  4. Add the following function below AddToastNotification. This function will add a sample live tile.

    (Code Snippet - FrontEndDev - Ex5 - AddLiveTile)

    function AddLiveTile() {
        var notifications = window.Windows.UI.Notifications;
    
        var template = notifications.TileTemplateType.tileWide310x150Text01;
        var tileXml = notifications.TileUpdateManager.getTemplateContent(template);
    
        // Supply text content for your notification
        var tileTextAttributes = tileXml.getElementsByTagName("text");
        tileTextAttributes[0].appendChild(tileXml.createTextNode("OrderApp"));
        tileTextAttributes[1].appendChild(tileXml.createTextNode("Live tile"));
    
        // Include notification bindings for multiple tile sizes in the payload
        var squareTemplate = notifications.TileTemplateType.tileSquare150x150Text04;
        var squareTileXml = notifications.TileUpdateManager.getTemplateContent(squareTemplate);
    
        var squareTileTextAttributes = squareTileXml.getElementsByTagName("text");
        squareTileTextAttributes[0].appendChild(squareTileXml.createTextNode("OrderApp - Tile notification!"));
    
        var node = tileXml.importNode(squareTileXml.getElementsByTagName("binding").item(0), true);
        tileXml.getElementsByTagName("visual").item(0).appendChild(node);
    
        // Create the notification based on the XML content you've specified
        var tileNotification = new notifications.TileNotification(tileXml);
    
        // Set an expiration time for the notification
        var currentTime = new Date();
        tileNotification.expirationTime = new Date(currentTime.getTime() + 600 * 1000);
    
        // Send the notification to the app tile
        notifications.TileUpdateManager.createTileUpdaterForApplication().update(tileNotification);
    }
  5. Update the content of the feature test added before, by replacing the // add in Windows features... comment with the call to the AddToastNotification and AddLiveTile functions.

    if (window.Windows) {
        AddToastNotification();
        AddLiveTile();
    }
  6. Open the Layout.cshtml file located at the Views/Shared folder and add the following line at the end of the body tag to include the reference to the windows.js file you created.

    <script src="~/js/windows-features.js"></script>
  7. Open the manifest.json file and verify that the following line is present. This property enables the Windows application to have access to the native APIs.

    "mjs_api_access": [
        {
            "match": "http://localhost:55804/*",
            "access": "all",
            "platform": "windows10"
        }
    ],
  8. Run the web application using Ctrl + F5 and don't stop it as you will need the app running for the following steps.

  9. Back in the Command Prompt, make sure that you are located in the folder that ManifoldJS generated and run the solution by executing the following command.

    manifoldjs run windows10
    
  10. Once the application starts, you should see the toast notification. Each time you navigate to a new page the code will be executed again, so you will be able to see the notification.

    OrderApp Toast notification

    OrderApp Toast notification

  11. Pin the application to your start menu to see the live tile.

    OrderApp Live Tile

    OrderApp Live Tile


Summary

By completing this module, you should have:

  • Worked with Single Page Applications using Angular 2
  • Integrated HTTP services using ASP.NET Core web api's
  • Tested your applications for web standards compliance with the Vorlon.js Edge extension
  • Packaged your site as an app using ManifoldJS

Note: You can take advantage of the Visual Studio Dev Essentials subscription in order to get everything you need to build and deploy your app on any platform.