Before proceeding further, please read the following previous articles for updating .NET Core version to use Angular.
Getting Started
- Start Visual Studio 2017.
- Select "New Project".
- Select .NET Core from Web template and select ASP.NET Core Web Application.
- Provide a project name.
- Provide project location to save and click OK.
Image 1.
- Select Angular from the given templates.
- Select .NET Core version and click OK.
Image 2.
- Here is the structure of your project.
Image 3.
Here is my database table schema structure diagram.
Image 4.
Now, let’s work on functionality part. First of all, add a connection string in appsettings.json.
Now, go to Tools >> NuGet Package Manager >> Package Manager Console now.
Image 5.
Add Scaffold DB Context command and provide the database connection string, output directory name, and table’s name.
Image 6.
Wait until the command executes. Once done, check in the Models folder.
Image 7.
As you can see 3 class are added which has all model classes and context in the Models folder.
First thing before going further, go in the dbcotext class and comment this line of code which has hardcoded connection string.
- protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
- {
-
-
-
-
-
- }
Now, go to the Startup.cs class and add the following line for connection string and to serialize JSON data.
- public void ConfigureServices(IServiceCollection services) {
-
- services.AddDbContext < NORTHWNDContext > (options => options.UseSqlServer(Configuration.GetConnectionString("NORTHWNDConnString")));
- services.AddScoped < EmployeeDAL > ();
- services.AddMvc().AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
- }
Now, add a database access layer class in the Models folder.
EmployeeDAL.cs
- using Microsoft.EntityFrameworkCore;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading.Tasks;
- namespace secondsample.Models {
- public class EmployeeDAL {
- private readonly NORTHWNDContext _context;
- public EmployeeDAL(NORTHWNDContext context) {
- _context = context;
- }
-
-
-
-
- public async Task < IEnumerable < Employees >> GetAllEmployees() {
- try {
- return await _context.Employees.ToListAsync();
- } catch {
- throw;
- }
- }
-
-
-
-
-
- public int AddEmployee(Employees employee) {
- try {
- _context.Employees.Add(employee);
- _context.SaveChanges();
- return 1;
- } catch {
- throw;
- }
- }
-
-
-
-
-
- public int UpdateEmployee(Employees employee) {
- try {
- _context.Entry(employee).State = EntityState.Modified;
- _context.SaveChanges();
- return 1;
- } catch {
- throw;
- }
- }
-
-
-
-
-
- public Employees GetEmployee(int EmployeeID) {
- try {
- Employees employee = _context.Employees.Find(EmployeeID);
- return employee;
- } catch {
- throw;
- }
- }
-
-
-
-
-
- public int DeleteEmployee(int EmployeeID) {
- try {
- Employees employee = _context.Employees.Find(EmployeeID);
- _context.Employees.Remove(employee);
- _context.SaveChanges();
- return 1;
- } catch {
- throw;
- }
- }
- }
- }
Add a new controller class in Controller folder and make all API functions and Routes.
EmployeeController.cs
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading.Tasks;
- using Microsoft.AspNetCore.Mvc;
- using secondsample.Models;
- namespace secondsample.Controllers {
- [Route("api/Employees")]
- public class EmployeeController: Controller {
- private readonly EmployeeDAL _employeeDAL;
- public EmployeeController(EmployeeDAL employeeDAL) {
- _employeeDAL = employeeDAL;
- }
- [HttpGet]
- [Route("GetEmployees")]
- public async Task < IEnumerable < Employees >> GetEmployees() {
- return await _employeeDAL.GetAllEmployees();
- }
- [HttpPost]
- [Route("AddEmployee")]
- public int AddEmployee([FromBody] Employees employee) {
- return _employeeDAL.AddEmployee(employee);
- }
- [HttpGet]
- [Route("GetEmployeeDetail/{employeeID}")]
- public Employees GetEmployeeDetail(int employeeID) {
- return _employeeDAL.GetEmployee(employeeID);
- }
- [HttpPut]
- [Route("EditEmployee")]
- public int EditEmployee([FromBody] Employees employee) {
- return _employeeDAL.UpdateEmployee(employee);
- }
- [HttpDelete]
- [Route("DeleteEmployee/{EmployeeID}")]
- public int DeleteEmployee(int EmployeeID) {
- return _employeeDAL.DeleteEmployee(EmployeeID);
- }
- }
- }
We are done with database layer class and API controller class, now let’s work on Angular part.
Add a new services folder in app folder which is in ClientApp directory, add a new TypeScript file, and add the following code.
employeeservices.ts
- import {
- Injectable,
- Inject
- } from '@angular/core';
- import {
- Http,
- Response
- } from '@angular/http';
- import {
- Observable
- } from 'rxjs/Observable';
- import {
- Router
- } from '@angular/router';
- import 'rxjs/add/operator/map';
- import 'rxjs/add/operator/catch';
- import 'rxjs/add/observable/throw';
- @Injectable()
- export class EmployeeService {
- myAppUrl: string = "";
- constructor(private _http: Http, @Inject('BASE_URL') baseUrl: string) {
- this.myAppUrl = baseUrl;
- }
- getEmployees() {
- return this._http.get(this.myAppUrl + 'api/Employees/GetEmployees').map((response: Response) => response.json()).catch(this.errorHandler);
- }
- getEmployeeById(EmployeeID: number) {
- return this._http.get(this.myAppUrl + "api/Employees/GetEmployeeDetail/" + EmployeeID).map((response: Response) => response.json()).catch(this.errorHandler)
- }
- deleteEmployee(EmployeeID: number) {
- return this._http.delete(this.myAppUrl + "api/Employees/DeleteEmployee/" + EmployeeID).map((response: Response) => response.json()).catch(this.errorHandler);
- }
- updateEmployee(employee: any) {
- return this._http.put(this.myAppUrl + 'api/Employees/EditEmployee', employee).map((response: Response) => response.json()).catch(this.errorHandler);
- }
- saveEmployee(employee: any) {
- return this._http.post(this.myAppUrl + 'api/Employees/AddEmployee', employee).map((response: Response) => response.json()).catch(this.errorHandler)
- }
- errorHandler(error: Response) {
- console.log(error);
- return Observable.throw(error.json().error || 'Server error');
- }
- }
Add two new folders in components folder - employeedata and addemployee and add TS components and HTML files in those folders.
employeedata.components.ts
- import {
- Component,
- Inject
- } from '@angular/core';
- import {
- Http,
- Headers
- } from '@angular/http';
- import {
- EmployeeService
- } from '../../services/employeeservice'
- import {
- Router,
- ActivatedRoute
- } from '@angular/router';
- @Component({
- selector: 'employeedata',
- templateUrl: './employeedata.component.html'
- })
- export class EmployeeDataComponent {
- public employeelist: EmployeeList[];
- constructor(public http: Http, private _router: Router, private _employeeService: EmployeeService) {
- this.getEmployees();
- }
- getEmployees() {
- this._employeeService.getEmployees().subscribe(data => this.employeelist = data)
- console.log(this.employeelist);
- }
- deleteEmployee(employeeId: number) {
- var ans = confirm("Do you want to delete employee with Id: " + employeeId);
- if (ans) {
- this._employeeService.deleteEmployee(employeeId).subscribe((data) => {
- this.getEmployees();
- }, error => console.error(error))
- }
- }
- }
- interface EmployeeList {
- EmployeeID: number;
- LastName: string;
- FirstName: string;
- Title: string;
- Address: string;
- City: string;
- Region: string;
- PostalCode: string;
- Country: string;
- Notes: string;
- }
employeedata.component.html
- <h1>Employee Data</h1>
- <p>This component demonstrates fetching Employee data from the server.</p>
- <p *ngIf="!employeelist"><em>Loading...</em></p>
- <p> <a [routerLink]="['/add-employee']">Create New</a> </p>
- <table class='table table-bordered' *ngIf="employeelist">
- <thead style="background-color:green;font-weight:bold;color:white;">
- <tr>
- <th>EmployeeID</th>
- <th>Name</th>
- <th>Title</th>
- <th>City</th>
- <th>Region</th>
- <th>Zip</th>
- <th>Country</th>
- <th>Notes</th>
- <th>Actions</th>
- </tr>
- </thead>
- <tbody>
- <tr *ngFor="let emp of employeelist">
- <td>{{ emp.employeeId }}</td>
- <td>{{ emp.lastName }}, {{ emp.firstName }}</td>
- <td>{{ emp.title }}</td>
- <td>{{ emp.city }}</td>
- <td>{{ emp.region }}</td>
- <td>{{ emp.postalCode }}</td>
- <td>{{ emp.country }}</td>
- <td>{{ emp.notes }}</td>
- <td> <a [routerLink]="['/employee/edit/', emp.employeeId]">Edit</a> | <a [routerLink]="" (click)="deleteEmployee(emp.employeeId)">Delete</a> </td>
- </tr>
- </tbody>
- </table>
addemployee.component.ts
- import {
- Component,
- OnInit
- } from '@angular/core';
- import {
- Http,
- Headers
- } from '@angular/http';
- import {
- NgForm,
- FormBuilder,
- FormGroup,
- Validators,
- FormControl
- } from '@angular/forms';
- import {
- Router,
- ActivatedRoute
- } from '@angular/router';
- import {
- EmployeeDataComponent
- } from '../employeedata/employeedata.component';
- import {
- EmployeeService
- } from '../../services/employeeservice';
- @Component({
- templateUrl: './addemployee.component.html'
- })
- export class AddEmployee implements OnInit {
- employeeForm: FormGroup;
- title: string = "Create";
- employeeId: number;
- errorMessage: any;
- constructor(private _fb: FormBuilder, private _avRoute: ActivatedRoute, private _employeeService: EmployeeService, private _router: Router) {
- if (this._avRoute.snapshot.params["employeeID"]) {
- this.employeeId = this._avRoute.snapshot.params["employeeID"];
-
- }
- this.employeeForm = this._fb.group({
- employeeId: 0,
- firstName: ['', [Validators.required]],
- lastName: ['', [Validators.required]],
- title: ['', [Validators.required]],
- city: ['', [Validators.required]],
- region: ['', [Validators.required]],
- postalCode: ['', [Validators.required]],
- country: ['', [Validators.required]],
- notes: ['', [Validators.required]],
- titleOfCourtesy: ['', []],
- birthDate: ['', []],
- hireDate: ['', []],
- address: ['', []],
- homePhone: ['', []],
- extension: ['', []],
- photo: ['', []],
- reportsTo: ['', []],
- photoPath: ['', []],
- salary: ['', []],
- reportsToNavigation: ['', []],
- employeeTerritories: ['', []],
- inverseReportsToNavigation: ['', []]
- })
- }
- ngOnInit() {
- if (this.employeeId > 0) {
- this.title = "Edit";
- this._employeeService.getEmployeeById(this.employeeId).subscribe(resp => this.employeeForm.setValue(resp), error => this.errorMessage = error);
- }
- }
- save() {
- if (!this.employeeForm.valid) {
- return;
- }
- if (this.title == "Create") {
- this._employeeService.saveEmployee(this.employeeForm.value).subscribe((data) => {
- this._router.navigate(['/employee-data']);
- }, error => this.errorMessage = error)
- } else if (this.title == "Edit") {
- this._employeeService.updateEmployee(this.employeeForm.value).subscribe((data) => {
- this._router.navigate(['/employee-data']);
- }, error => this.errorMessage = error)
- }
- }
- cancel() {
- this._router.navigate(['/employee-data']);
- }
- get firstName() {
- return this.employeeForm.get('firstName');
- }
- get lastName() {
- return this.employeeForm.get('lastName');
- }
- get title1() {
- return this.employeeForm.get('title');
- }
- get city() {
- return this.employeeForm.get('city');
- }
- get region() {
- return this.employeeForm.get('region');
- }
- get postalCode() {
- return this.employeeForm.get('postalCode');
- }
- get country() {
- return this.employeeForm.get('country');
- }
- get notes() {
- return this.employeeForm.get('notes');
- }
- get titleOfCourtesy() {
- return this.employeeForm.get('titleOfCourtesy');
- }
- get birthDate() {
- return this.employeeForm.get('birthDate');
- }
- get hireDate() {
- return this.employeeForm.get('hireDate');
- }
- get address() {
- return this.employeeForm.get('address');
- }
- get homePhone() {
- return this.employeeForm.get('homePhone');
- }
- get extension() {
- return this.employeeForm.get('extension');
- }
- get photo() {
- return this.employeeForm.get('photo');
- }
- get reportsTo() {
- return this.employeeForm.get('reportsTo');
- }
- get photoPath() {
- return this.employeeForm.get('photoPath');
- }
- get salary() {
- return this.employeeForm.get('salary');
- }
- get reportsToNavigation() {
- return this.employeeForm.get('reportsToNavigation');
- }
- get employeeTerritories() {
- return this.employeeForm.get('employeeTerritories');
- }
- get inverseReportsToNavigation() {
- return this.employeeForm.get('inverseReportsToNavigation');
- }
- }
addemployee.component.html
- <!DOCTYPE html>
- <html>
-
- <head>
- <meta charset="utf-8" />
- <title></title>
- </head>
-
- <body>
- <h1>{{title}}</h1>
- <h3>Employee</h3>
- <hr />
- <form [formGroup]="employeeForm" (ngSubmit)="save()" #formDir="ngForm" novalidate>
- <div class="form-group row"> <label class=" control-label col-md-12" for="FirstName">First Name</label>
- <div class="col-md-4"> <input class="form-control" type="text" formControlName="firstName"> </div> <span class="text-danger" *ngIf="employeeForm.hasError('required', 'firstName') && formDir.submitted">
- First Name is required.
- </span> </div>
- <div class="form-group row"> <label class=" control-label col-md-12" for="LastName">Last Name</label>
- <div class="col-md-4"> <input class="form-control" type="text" formControlName="lastName"> </div> <span class="text-danger" *ngIf="employeeForm.hasError('required', 'lastName') && formDir.submitted">
- Last Name is required.
- </span> </div>
- <div class="form-group row"> <label class="control-label col-md-12" for="Title">Title</label>
- <div class="col-md-4"> <input class="form-control" type="text" formControlName="title"> </div> <span class="text-danger" *ngIf="employeeForm.hasError('required', 'title') && formDir.submitted">
- Title is required
- </span> </div>
- <div class="form-group row"> <label class="control-label col-md-12" for="City">City</label>
- <div class="col-md-4"> <input class="form-control" type="text" formControlName="city"> </div> <span class="text-danger" *ngIf="employeeForm.hasError('required', 'city') && formDir.submitted">
- City is required
- </span> </div>
- <div class="form-group row"> <label class="control-label col-md-12" for="Region">Region</label>
- <div class="col-md-4"> <input class="form-control" type="text" formControlName="region"> </div> <span class="text-danger" *ngIf="employeeForm.hasError('required', 'region') && formDir.submitted">
- Region is required
- </span> </div>
- <div class="form-group row"> <label class="control-label col-md-12" for="postalCode">Zip</label>
- <div class="col-md-4"> <input class="form-control" type="text" formControlName="postalCode"> </div> <span class="text-danger" *ngIf="employeeForm.hasError('required', 'postalCode') && formDir.submitted">
- Zip is required
- </span> </div>
- <div class="form-group row"> <label class="control-label col-md-12" for="Country">Country</label>
- <div class="col-md-4"> <input class="form-control" type="text" formControlName="country"> </div> <span class="text-danger" *ngIf="employeeForm.hasError('required', 'country') && formDir.submitted">
- Country is required
- </span> </div>
- <div class="form-group row"> <label class="control-label col-md-12" for="Notes">Notes</label>
- <div class="col-md-4"> <textarea class="form-control" type="text" formControlName="notes"></textarea> </div> <span class="text-danger" *ngIf="employeeForm.hasError('required', 'notes') && formDir.submitted">
- Notes is required
- </span> </div>
- <div class="form-group"> <button type="submit" class="btn btn-default">Save</button> <button class="btn" (click)="cancel()">Cancel</button> </div>
- </form>
- </body>
-
- </html>
app.shared.module.ts
- import {
- NgModule
- } from '@angular/core';
- import {
- EmployeeService
- } from './services/employeeservice'
- import {
- CommonModule
- } from '@angular/common';
- import {
- FormsModule,
- ReactiveFormsModule
- } from '@angular/forms';
- import {
- HttpModule
- } from '@angular/http';
- import {
- RouterModule
- } from '@angular/router';
- import {
- AppComponent
- } from './components/app/app.component';
- import {
- NavMenuComponent
- } from './components/navmenu/navmenu.component';
- import {
- HomeComponent
- } from './components/home/home.component';
- import {
- FetchDataComponent
- } from './components/fetchdata/fetchdata.component';
- import {
- CounterComponent
- } from './components/counter/counter.component';
- import {
- EmployeeDataComponent
- } from './components/employeedata/employeedata.component';
- import {
- AddEmployee
- } from './components/addemployee/addemployee.component';
- @NgModule({
- declarations: [
- AppComponent,
- NavMenuComponent,
- CounterComponent,
- FetchDataComponent,
- EmployeeDataComponent,
- HomeComponent,
- AddEmployee
- ],
- imports: [
- CommonModule,
- HttpModule,
- FormsModule,
- ReactiveFormsModule,
- RouterModule.forRoot([{
- path: '',
- redirectTo: 'home',
- pathMatch: 'full'
- }, {
- path: 'home',
- component: HomeComponent
- }, {
- path: 'counter',
- component: CounterComponent
- }, {
- path: 'fetch-data',
- component: FetchDataComponent
- }, {
- path: 'employee-data',
- component: EmployeeDataComponent
- }, {
- path: 'add-employee',
- component: AddEmployee
- }, {
- path: 'employee/edit/:employeeID',
- component: AddEmployee
- }, {
- path: '**',
- redirectTo: 'home'
- }])
- ],
- providers: [EmployeeService]
- })
- export class AppModuleShared {}
At last, if you want to add a new navigation tab, then go to in app folder and navmenu >> navmenu.component.html and add whatever name you like to add.
- <div class='main-nav'>
- <div class='navbar navbar-inverse'>
- <div class='navbar-header'> <button type='button' class='navbar-toggle' data-toggle='collapse' data-target='.navbar-collapse'>
- <span class='sr-only'>Toggle navigation</span>
- <span class='icon-bar'></span>
- <span class='icon-bar'></span>
- <span class='icon-bar'></span>
- </button> <a class='navbar-brand' [routerLink]="['/home']">CRUD Operations in .NET Core with Angular Sample by Raj</a> </div>
- <div class='clearfix'></div>
- <div class='navbar-collapse collapse'>
- <ul class='nav navbar-nav'>
- <li [routerLinkActive]="['link-active']"> <a [routerLink]="['/home']">
- <span class='glyphicon glyphicon-home'></span> Home
- </a> </li>
- <li [routerLinkActive]="['link-active']"> <a [routerLink]="['/employee-data']">
- <span class='glyphicon glyphicon-th-list'></span> Employees
- </a> </li>
- </ul>
- </div>
- </div>
- </div>
So we are done here. Now, let’s run the project to see output.
Image 8.
As you can see when click on Employees tab then data is loading in the grid, that means, our data is coming correct and functionality is working. Now, let’s click on Edit link to edit existing record.
Image 9.
You can edit whatever information you like to edit and click on save button. Now, let’s create a new record.
Image 10.
You can delete any existing record on click on Delete link.
I don’t want to make this article bigger, so I am wrapping up here. I will upload source code soon and will share the link.
Conclusion
In this article, we learned how to build CRUD functionality with .NET Core angular and Entity Framework with SQL Server database.