Full-stack application development with AngularJS 11 and Asp.Net MVC Core 5.0 — Creating and Consuming WebServices [part3/4]

Creating the Web Service

Creating the Web Service Controller

using Microsoft.AspNetCore.Mvc;
using ServerApp.Models;
namespace ServerApp.Controllers
{
[Route("api/products")]
[ApiController]
public class ProductValuesController : Controller
{
private DataContext context;
public ProductValuesController(DataContext ctx)
{
context = ctx;
}
[HttpGet("{id}")]
public Product GetProduct(long id)
{
return context.Products.Find(id);
}
}
}

Testing the Web Service

dotnet run watch
https://localhost:5001/api/products/1

Using Swagger to Explore the Web Service

dotnet add package Swashbuckle.AspNetCore

Enabling and configuring Swashbuckle

services.AddSwaggerGen(options => {
options.SwaggerDoc("v1",
new OpenApiInfo { Title = "FullStackApp API", Version = "v1" });
});
Add outlined statements to IConfiguration Method of startup.cs class
app.UseSwagger();
app.UseSwaggerUI(options => {
options.SwaggerEndpoint("/swagger/v1/swagger.json","FullStackApp API");
});
Add outlined statements to Configure Method of startup.cs class
https://localhost:5001/swagger/v1/swagger.json
https://localhost:5001/swagger

Updating the Controller, View, and Layout of ServeApp

<!DOCTYPE html><html lang="en">
<head>
<base href="/">
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Full-Stack App</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
</head>
<body>
<h2 class="bg-dark text-white p-2">Full-Stack App</h2>
@RenderBody()
@RenderSection("Scripts", required: false)
</body>
</html>
@section scripts {<script src="runtime.js"></script>
<script src="polyfills.js"></script>
<script src="styles.js"></script>
<script src="vendor.js"></script>
<script src="main.js"></script>
}<app-root></app-root>

Updating the content of app.component.html of AngularApp

<h2>SportStore</h2>
<span>Angular Content Will Go Here</span>

Testing the connection between ServerApp and ClientApp

npm start
dotnet watch run

Consuming the Web Service Data in the Repository

import { Product } from "./product.model";
import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
@Injectable()
export class Repository {
product: Product;
constructor(private http: HttpClient) {
this.getProduct(1);
}
getProduct(id: number) {
this.http.get<Product>("/api/products/" + id)
.subscribe(p => this.product = p);
}
}
import { Component } from '@angular/core';
import { Repository } from "./models/repository";
import { Product } from "./models/product.model";
@Component({selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})export class AppComponent {constructor(private repo: Repository) { }get product(): Product {return this.repo.product;
}
}
<div class="p-2"><table class="table table-sm">
<tr><th>Name</th><td>{{product?.name}}</td></tr>
<tr><th>Category</th><td>{{product?.category}}</td></tr>
<tr><th>Description</th><td>{{product?.description}}</td></tr>
<tr><th>Price</th><td>{{product?.price}}</td></tr>
</table>
</div>

Eager Loading of related data in ServerApp

[HttpGet("{id}")]public Product GetProduct(long id)
{
Product result = context.Products
.Include(p => p.Supplier).ThenInclude(s => s.Products)
.Include(p => p.Ratings)
.FirstOrDefault(p => p.ProductId == id);
if (result != null)
{
if (result.Supplier != null)
{
result.Supplier.Products = result.Supplier.Products.Select(p =>
new Product
{
ProductId = p.ProductId,
Name = p.Name,
Category = p.Category,
Description = p.Description,
Price = p.Price,
});
}
if (result.Ratings != null)
{
foreach (Rating r in result.Ratings)
{
r.Product = null;
}
}
}
return result;
}
<div class="p-2">
<table class="table table-sm">
<tr><th colspan="2" class="bg-info">Product</th></tr>
<tr><th>Name</th><td>{{product?.name || 'Loading Data...'}}</td></tr>
<tr><th>Category</th><td>{{product?.category || 'Loading Data...'}}</td></tr>
<tr>
<th>Description</th>
<td>{{product?.description || 'Loading Data...'}}</td>
</tr>
<tr><th>Price</th><td>{{product?.price || 'Loading Data...'}}</td></tr>
<tr><th colspan="2" class="bg-info">Supplier</th></tr>
<tr><th>Name</th><td>{{product?.supplier?.name}}</td></tr>
<tr><th>City</th><td>{{product?.supplier?.city}}</td></tr>
<tr><th>State</th><td>{{product?.supplier?.state}}</td></tr>
<tr><th>Products</th><td>{{product?.supplier?.products?.length}
</td></tr>
</table>
</div>

Implementing the GET Method for Multiple Objects

[HttpGet]
public IEnumerable<Product> GetProducts(bool related = false)
{
IQueryable<Product> query = context.Products;
if (related)
{
query = query.Include(p => p.Supplier).Include(p => p.Ratings);
List<Product> data = query.ToList();
data.ForEach(p => {
if (p.Supplier != null)
{
p.Supplier.Products = null;
}
if (p.Ratings != null)
{
p.Ratings.ForEach(r => r.Product = null);
}
});
return data;
}
else
{
return query;
}
}

Querying Multiple Objects in the Angular Application

import { Product } from "./product.model";
import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
const productsUrl = "/api/products";@Injectable()
export class Repository {
product: Product;
products: Product[];
constructor(private http: HttpClient) {
this.getProducts(true);
}
getProduct(id: number) {
this.http.get<Product>(`${productsUrl}/${id}`)
.subscribe(p => this.product = p);
}
getProducts(related = false) {
this.http.get<Product[]>(`${productsUrl}?related=${related}`)
.subscribe(prods => this.products = prods);
}
}
get products(): Product[] {
return this.repo.products;
}
<div class="p-2">
<table class="table table-sm table-striped">
<tbody>
<tr>
<th>Name</th>
<th>Category</th>
<th>Price</th>
<th>Supplier</th>
<th>Ratings</th>
</tr>
<tr *ngFor="let product of products">
<td>{{product.name}}</td>
<td>{{product.category}}</td>
<td>{{product.price}}</td>
<td>{{product.supplier?.name || 'None'}}</td>
<td>{{product.ratings?.length || 0}}</td>
</tr>
</tbody>
</table>
</div>

Applying Filtering in the Web Service

public IEnumerable<Product> GetProducts(string category, string search, bool related = false)
{
IQueryable<Product> query = context.Products;
if (!string.IsNullOrWhiteSpace(category))
{
string catLower = category.ToLower();
query = query.Where(p => p.Category.ToLower().Contains(catLower));
}
if (!string.IsNullOrWhiteSpace(search))
{
string searchLower = search.ToLower();
query = query.Where(p => p.Name.ToLower().Contains(searchLower)
|| p.Description.ToLower().Contains(searchLower));
}
if (related)
{
query = query.Include(p => p.Supplier).Include(p => p.Ratings);
List<Product> data = query.ToList();
data.ForEach(p => {
if (p.Supplier != null)
{
p.Supplier.Products = null;
}
if (p.Ratings != null)
{
p.Ratings.ForEach(r => r.Product = null);
}
});
return data;
}
else
{
return query;
}
}

Applying Filtering in the Angular Application

export class Filter {
category?: string;
search?: string;
related: boolean = false;
reset() {
this.category = this.search = null;
this.related = false;
}
}

Summary

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store