Building ASP.NET Core app strengthened with AngularJS 2

Introduction
In this tutorial I will show you how to create Master-Detail web application that also have a search option. We will use the data of the Cruising Company. It contains data about: ships, booking info, sale units etc.Using the code
The application consist of two parts:- Backend: RESTful API written in .NET Core
- Frontend: SPA written in Angular 2
Start by creating new project: New -> Project. Select Web -> ASP.NET Core Angular 2 Starter.

Replace following files with the appropriate code.
project.json
{ "dependencies": { "Microsoft.NETCore.App": { "version": "1.0.1", "type": "platform" }, "Microsoft.AspNetCore.AngularServices": "1.0.0-*", "Microsoft.AspNetCore.Diagnostics": "1.0.0", "Microsoft.AspNetCore.Mvc": "1.0.1", "Microsoft.AspNetCore.Razor.Tools": { "version": "1.0.0-preview2-final", "type": "build" }, "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", "Microsoft.AspNetCore.Server.Kestrel": "1.0.1", "Microsoft.AspNetCore.StaticFiles": "1.0.0", "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0", "Microsoft.Extensions.Configuration.Json": "1.0.0", "Microsoft.Extensions.Configuration.CommandLine": "1.0.0", "Microsoft.Extensions.Logging": "1.0.0", "Microsoft.Extensions.Logging.Console": "1.0.0", "Microsoft.Extensions.Logging.Debug": "1.0.0", "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0", "Microsoft.EntityFrameworkCore.SqlServer": "1.0.1", "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final" }, "tools": { "Microsoft.AspNetCore.Razor.Tools": "1.0.0-preview2-final", "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final", "Microsoft.DotNet.Watcher.Tools": "1.0.0-preview2-final" }, "frameworks": { "netcoreapp1.0": { "imports": [ "dotnet5.6", "portable-net45+win8" ] } }, "buildOptions": { "emitEntryPoint": true, "preserveCompilationContext": true }, "runtimeOptions": { "configProperties": { "System.GC.Server": true } }, "publishOptions": { "include": [ "appsettings.json", "ClientApp/dist", "node_modules", "Views", "web.config", "wwwroot" ] }, "scripts": { "prepublish": [ "npm install", "node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod", "node node_modules/webpack/bin/webpack.js --env.prod" ], "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ] }, "tooling": { "defaultNamespace": "CruiseCompanyApp" } }
package.json
{ "name": "Angular2Spa", "version": "0.0.0", "dependencies": { "@angular/common": "2.0.0", "@angular/compiler": "2.0.0", "@angular/core": "2.0.0", "@angular/forms": "2.0.0", "@angular/http": "2.0.0", "@angular/platform-browser": "2.0.0", "@angular/platform-browser-dynamic": "2.0.0", "@angular/platform-server": "2.0.0", "@angular/router": "3.0.0", "@types/node": "^6.0.38", "angular2-platform-node": "~2.0.10", "angular2-universal": "~2.0.10", "angular2-universal-polyfills": "~2.0.10", "aspnet-prerendering": "^1.0.6", "aspnet-webpack": "^1.0.11", "bootstrap": "^3.3.7", "css": "^2.2.1", "css-loader": "^0.25.0", "es6-shim": "^0.35.1", "expose-loader": "^0.7.1", "extract-text-webpack-plugin": "^1.0.1", "file-loader": "^0.9.0", "isomorphic-fetch": "^2.2.1", "jquery": "^2.2.1", "preboot": "^4.5.2", "raw-loader": "^0.5.1", "rxjs": "5.0.0-beta.12", "style-loader": "^0.13.0", "to-string-loader": "^1.1.5", "ts-loader": "^0.8.2", "typescript": "^2.0.0", "url-loader": "^0.5.7", "webpack": "^1.12.14", "webpack-externals-plugin": "^1.0.0", "webpack-hot-middleware": "^2.10.0", "webpack-merge": "^0.14.1", "zone.js": "^0.6.21" } }Now rebuild project so all dependencies are installed. Add Models folder in the root of your project and add those 2 files:
JSONModel.cs
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; namespace A2SPA.Models { public class SalesUnit { [Key] public int id { get; set; } public string name { get; set; } public string country { get; set; } public string currency { get; set; } } public class Ship { [Key] public int id { get; set; } public int salesUnitId { get; set; } public string name { get; set; } } public class Booking { [Key] public int id { get; set; } public int shipId { get; set; } public string bookingDate { get; set; } public float price { get; set; } } public class JSONModel { public ListsalesUnits { get; set; } public List ships { get; set; } public List bookings { get; set; } } }
ViewModels.cs
using System.ComponentModel.DataAnnotations; namespace A2SPA.Models { //List public class SearchView { [Key] public int BookingId { get; set; } public string ShipName { get; set; } public string BookingDate { get; set; } public float Price { get; set; } } //List public class SalesView { [Key] public int Id { get; set; } public string Name { get; set; } public string Country { get; set; } public float Total { get; set; } public string Currency { get; set; } } //Detail public class SalesDetailView { public int BookingId { get; set; } public string ShipName { get; set; } public float Price { get; set; } public string Currency { get; set; } } }Add Api folder in the root of your project and place this file inside:
BookingsAPI.cs
using System; using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Mvc; using System.IO; using Newtonsoft.Json; using Microsoft.AspNetCore.Hosting; using A2SPA.Models; namespace CruiseCompanyApp.Controllers { [Produces("application/json")] [Route("api")] public class BookingsAPI : Controller { private IHostingEnvironment _env; public JSONModel jsonData; public BookingsAPI (IHostingEnvironment env) { _env = env; ReadJSON(); } //Main list [Route("bookings/{quarter}")] public ListAdd Controllers folder in the root of your project and place this file inside:Bookings (int quarter) { var quarterBookings = BookingsByQuarter(quarter); var subQuery1 = from qb in quarterBookings group qb by qb.shipId into shipBookings select new { ShipKey = shipBookings.Key, Sum = shipBookings.Sum(p => p.price) }; var shipSum = from emp in jsonData.ships join s in subQuery1 on emp.id equals s.ShipKey join pd in jsonData.salesUnits on emp.salesUnitId equals pd.id select new SalesView { Id = pd.id, Name = pd.name, Country = pd.country, Total = s.Sum, Currency = pd.currency }; var subQuery2 = from s in shipSum group s by s.Country into res select new SalesView { Id = res.FirstOrDefault().Id, Name = res.FirstOrDefault().Name, Country = res.FirstOrDefault().Country, Total = (float)Math.Round(res.Sum(r => r.Total)), Currency = res.FirstOrDefault().Currency }; var salesViewList = subQuery2.OrderByDescending(c => c.Total).ToList(); return salesViewList; } //Details [Route("bookings/{saleUnit}/{quarter}")] public List BookingsBySaleUnit (int saleUnit, int quarter) { string currency = jsonData.salesUnits.Where(u => u.id == saleUnit).FirstOrDefault().currency; var quarterBookings = BookingsByQuarter(quarter); var shipsBySaleUnit = from s in jsonData.ships where s.salesUnitId == saleUnit select s; var shipSum = (from b in quarterBookings join s in shipsBySaleUnit on b.shipId equals s.id select new SalesDetailView { BookingId = b.id, ShipName = s.name, Price = b.price, Currency = currency }).ToList(); return shipSum; } // Filter by bookingId or shipname [Route("searchbookings/{searchValue}")] public List SearchBookings (string searchValue) { IEnumerable searchResults = null; IEnumerable bookings = null; int id; bool isNumeric = int.TryParse(searchValue, out id); if (isNumeric) bookings = jsonData.bookings.Where(b => b.id == id); else { var getShipIds = jsonData.ships.Where(s => s.name.ToLower().StartsWith(searchValue.ToLower())); bookings = from booking in jsonData.bookings where getShipIds.Select(s => s.id).Contains(booking.shipId) select booking; } searchResults = from ship in jsonData.ships join b in bookings on ship.id equals b.shipId select new SearchView { BookingId = b.id, ShipName = ship.name, BookingDate = DateTime.Parse(b.bookingDate).ToString("yyyy-MM-dd"), Price = b.price }; return searchResults.ToList(); } // Filter bookisng by quarter (0 for all) public List BookingsByQuarter (int quarter) { // ReadJSON(); int fromMonth = quarter * 3 - 3; int toMonth = quarter * 3; List res = null; if (quarter == 0) res = jsonData.bookings.ToList(); else { res = (from b in jsonData.bookings where Convert.ToDateTime(b.bookingDate).Year == 2016 && Convert.ToDateTime(b.bookingDate).Month > fromMonth && Convert.ToDateTime(b.bookingDate).Month <= toMonth select b).ToList(); } return res; } private void ReadJSON () { if (jsonData != null) return; var jsonFile = _env.ContentRootPath + Path.DirectorySeparatorChar.ToString() + "Data" + Path.DirectorySeparatorChar.ToString() + "TrialDayData.json"; string fileContent; using (var stream = new FileStream(jsonFile, FileMode.Open)) using (StreamReader sr = new StreamReader(stream)) { fileContent = sr.ReadToEnd(); } jsonData = JsonConvert.DeserializeObject (fileContent); } } }
HomeController
using Microsoft.AspNetCore.Mvc; namespace CruiseCompanyApp.Controllers { public class HomeController : Controller { public IActionResult Index() { return View(); } public IActionResult Error() { return View(); } } }Data folder should contain file TrialDayData.json which you will find on Github
Now we should be able to test the application API call. Hit Ctrl-F5 and navigate to http://localhost:64880/api/bookings/1
[{"id":8,"name":"soyoulun.com","country":"China","total":961880.0,"currency":"¥"},
{"id":1,"name":"dreamlines.de","country":"Germany","total":906369.0,"currency":"€"},
{"id":3,"name":"dreamlines.it","country":"Italy","total":831574.0,"currency":"€"},
{"id":7,"name":"dreamlines.nl","country":"Netherlands","total":793538.0,"currency":"€"},
{"id":5,"name":"dreamlines.com.au","country":"Australia","total":778521.0,"currency":"AU$"},
{"id":2,"name":"dreamlines.com.br","country":"Brazil","total":753164.0,"currency":"R$"},
{"id":6,"name":"dreamlines.ru","country":"Russia","total":632055.0,"currency":"RUB"},
{"id":4,"name":"dreamlines.fr","country":"France","total":469810.0,"currency":"€"}]

Source code available at Github
and
Live demo available here
Points of Interest
ASP.NET Core is a powerful platform, though it has not yet been completed as ASP.NET MVC. Yet combined with the AngularJS framework, it gives excellent results.

Comments
Post a Comment