This is an Angular and .NET Core Web API Starter Application with basic functionality for adding, editing, and deleting customers so you can use it as a starting point for building your applications.
You can see the source and latest updates to this project HERE
This is an Angular/.NET Core Web API starter application that has the basic functionality for adding, editing, and deleting customers so that you can use it as a starting point for building your applications. It uses the following frameworks:
- Angular Material
- Bootstrap
- .NET Core
- Entity Frameworks
How to run on your computer
Please be sure to have the following installed first:
- Node v12.19.*
- Angular CLI 10.2.*
- .NET Core 3.1
In the code downloaded, open the file api/Api/appsettings.Development.json and change the Data Source to point to your SQL Server:
"AngApiStarterConn": "Data Source=YourServerNameHere;
Initial Catalog=AngApiStarter;Integrated Security=True;"
Build the API solution to make sure the NuGet packages are installed and the solution builds successfully. The solution needs to be built successfully before you can run Entity Framework updates for the database.
Open the command prompt and navigate to api/Data, then run the command below which will update your SQL Server with the structure needed for the application:
dotnet ef database update -s ..\Api\Api.csproj
You should now have a database named AngApiStarter
with a table named Customers
in the SQL Server. You can insert a sample customer
with the SQL below into the table:
insert into customers(firstName, lastName)
values('John', 'Smith');
Check that the API can get the data from the database by running the API project and go to the url below, which should return the list of customer
s in the database:
https://localhost:44381/api/customer
Navigate to the ui folder in the command prompt and run the command below to get the libraries needed for running Angular:
npm update
If you get an error for modules not found, then you can run the command below to get the modules needed:
npm install
Start Angular by running the command below, which will start the server and open the application in the browser:
ng serve --open
How the application was built
Starting with the data layer from the API, a class model for Customer
was created:
public class Customer
{
[Required]
public int CustomerId { get; set; }
[Required, StringLength(80)]
public string FirstName { get; set; }
[Required, StringLength(80)]
public string LastName { get; set; }
}
Then the DbContext
class for the Customer
was created, using the Customer
class as the DbSet
which will ultimately become a table in the database:
public class AngApiStarterDbContext : DbContext
{
public DbSet<Customer> Customer { get; set; }
public AngApiStarterDbContext(DbContextOptions<AngApiStarterDbContext> options)
: base(options)
{
}
}
In the API project, we then define the database connection string, and add the code below to the ConfigureServices
method of the Startup.cs as shown below. This is so that we can use the connection string defined in the API project to create the database and table in SQL Server:
//add the db context with the connection string
services.AddDbContextPool<AngApiStarterDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("AngApiStarterConn"))
);
Build the solution, then using the command prompt, navigate to the Data project folder and run the command below to create the classes for the database migration:
dotnet ef migrations add initialCreate -s ..\Api\Api.csproj
After the classes are created, run the command below to create the database and tables in SQL Server:
dotnet ef database update -s ..\Api\Api.csproj
In the Data
project, it has the Interface and the implementation of the Interface using Entity Framework:
public interface ICustomerData
{
Task<IEnumerable<Customer>> Get();
Task<Customer> GetCustomerById(int id);
Task<Customer> Update(Customer customer);
Task<Customer> Add(Customer customer);
Task<int> Delete(int customerId);
}
public class SqlCustomerData : ICustomerData
{
public AngApiStarterDbContext DbContext { get; }
public SqlCustomerData(AngApiStarterDbContext dbContext)
{
DbContext = dbContext;
}
...
}
Now on to the API project. In the API project, it defines the implementation class for the interface in the ConfigureServices
method:
services.AddScoped<ICustomerData, SqlCustomerData>();
Then the controller too uses the interface by dependency injection:
public class CustomerController : ControllerBase
{
public ICustomerData CustomerData { get; }
public CustomerController(ICustomerData customerData)
{
CustomerData = customerData;
}
...
Since the API and the UI will be run from different website, it needs to allow the UI website to access the API using CORS. In the API’s appsettings.Development.json, it has the UI’s url:
"CorsUrl": "http://localhost:4200"
Then the CORS policy is specified in the Startup.cs file:
private readonly string corsPolicy = "defaultPolicy";
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(corsPolicy,
builder =>
{
builder.WithOrigins(Configuration["CorsUrl"])
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
});
...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseCors(corsPolicy);
...
}
Now on to the UI project with Angular. Using the Getting Started project from Angular, the Angular Materials components are defined in the project under app/common/material.module.ts, where all the components are added so that you can use the components without explicitly adding it to app.module.ts. Then in app.module.ts, it just includes the Material module:
import { MaterialModule } from './common/material.module';
The location of the API url is defined in environments/environments.ts file:
export const environment = {
production: false,
apiCustomer: 'https://localhost:44381/api/customer'
};
The model is defined in model/ICustomer.ts file, with the same model signature as the API:
export interface ICustomer{
customerId: number,
firstName: string,
lastName: string
}
The support for interacting with the API is in service/customer.service.ts file. It uses BehaviorSubject
to make the list of customers as an Observable
(you can think of it as a data stream). When the Observable
changes, all the Observers (you can think of it as listeners or subscribers) get notified of the change and can react accordingly. In the CustomerService
class, the customerList.next
method is called when the Observable
needs to notify all the Observers:
private customerList = new BehaviorSubject<ICustomer[] | null>(null);
customerList$ = this.customerList.asObservable();
...
//get the list of customers
get(){
this.http.get<ICustomer[]>(this.url).pipe(
).subscribe(result => {
if (result)
//notify the Observers of the change
this.customerList.next(result)
});
}
The Customer
component, which shows the list of customers that you can manage, listens to the changes in the CustomerService
class by subscribing to it in the ngOnInit
method:
export class CustomerComponent implements OnInit {
...
ngOnInit(){
this.custService.customerList$.subscribe(data =>
this.customerList = data
);
}
When the user clicks on the Add or Edit customer button, it opens a dialog which you can pass the data to the dialog, and define the action to take when the dialog is closed. Below shows the data passed to the dialog when adding a new customer
, and calling the CustomerService
when the dialog is closed:
openAddDialog(){
let dialogRef = this.custDialog.open(CustomerDialog, {
width: '300px',
data: {
action: 'add',
customer: { customerId: 0, firstName: '', lastName: ''}
}
});
dialogRef.afterClosed().subscribe(result => {
//add the customer
if (result)
this.custService.add(result)
});
}
For editing customer
, it needs to pass a copy of the customer
data to the dialog instead of the actual customer
data, so that if the user decided to cancel the customer
values are not changed:
customer: {...customer} //shallow copy the customer using spread operator
The dialog component is defined in app/customer/dialog/customer-dialog.component.ts, which accepts the caller’s data using MAT_DIALOG_DATA
from Material’s Dialog
component:
export class CustomerDialog {
...
constructor(public dialogRef: MatDialogRef<CustomerDialog>,
@Inject(MAT_DIALOG_DATA) public data: any) {
this.action = data.action;
this.customer = data.customer;
}
Finally, the bootstrap library is added using npm and the reference to the bootstrap stylesheet is added to index.html:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap...>
In the CustomerComponent
’s html, the bootstrap’s flexbox classes are added to center align the elements and organize the layout:
<div class="d-flex flex-row justify-content-center">
<div class="d-flex flex-column">
And that’s all. Hope you find it useful as a starting point for your projects.