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":"€"}]
Now it is still left to do the front-end part. For this task I suggest you to copy folders from GitHub and place it in your solution. In the end, your solution tree should look like this
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