Click here to Skip to main content
15,867,330 members
Articles / Web Development / ASP.NET / ASP.NET Core

CRUD Operation using ASP.NET CORE 2 and Angular 4 with EntityFramework Core, primeng Component and toastr-ng2

Rate me:
Please Sign up or sign in to vote.
4.67/5 (32 votes)
1 Aug 2017CPOL5 min read 76K   3.5K   39   10
CRUD Operation using ASP.NET CORE 2 and Angular 4 with EntityFramework Core, primeng component and toastr-ng2

Introduction

In this article, I would like to create an ASP.NET Core 2.0 application. Here, I will show you how to build Angular4 application with entity framework core and primeng component in Visual Studio 2017 Preview 1.

Prerequisites

Make sure you have installed all the prerequisites in your computer. If not, then download and install all, one by one.

First, download and install Visual Studio 2017 Preview 1 from this link.

Download and install NET Core 2.0 SDK

Create new Angular4Core2 Application using Visual Studio 2017 Preview 1

Open VS 2017 and create a new project using the new .NET Core template (please see the following screenshot).

Image 1

The following new window is open when the user clicks on the above ok button.

Image 2

Here, we need to select the “ASP.NET Core 2.0” from the highlighted dropdown and the Angular template to create ASP.NET Core 2.0 with Angular 4 application

Image 3

Have a look at new ASP.NET Core 2.0 with Angular 4 structure. It creates a new folder named “ClientApp” where we have an actual Angular 4 project and “wwwroot”, which is a special folder used to hold all of our live web files .

Just clean and build the application, it will restore all the relevant dependencies. (Please see the following screenshot.)

Image 4

We can use class library project in this web application. First, you need to create two .NET core library projects - one for business logic and another for data access layer.

Image 5

Follow the same step for creating a data access layer library project. Finally, our project will be as follows:

Image 6

Here, I am using Entity Framework Core using database first approach. To use this approach, we need to create one sample database table in SQL Server. Please run the following script in your SQL Server:

SQL
USE [master]
GO
/****** Object:  Database [ContactDB]    Script Date: 07/23/2017 21:48:54 ******/
CREATE DATABASE [ContactDB]
 CONTAINMENT = NONE
 ON  PRIMARY
( NAME = N'ContactDB', _
FILENAME = N'C:\Program Files (x86)\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\DATA\ContactDB.mdf',_
 SIZE = 5120KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB )
 LOG ON
( NAME = N'ContactDB_log', _
FILENAME = N'C:\Program Files _
(x86)\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\DATA\ContactDB_log.ldf' , _
SIZE = 2048KB , MAXSIZE = 2048GB , FILEGROWTH = 10%)
GO
ALTER DATABASE [ContactDB] SET COMPATIBILITY_LEVEL = 110
GO
IF (1 = FULLTEXTSERVICEPROPERTY('IsFullTextInstalled'))
begin
EXEC [ContactDB].[dbo].[sp_fulltext_database] @action = 'enable'
end
GO
ALTER DATABASE [ContactDB] SET ANSI_NULL_DEFAULT OFF
GO
ALTER DATABASE [ContactDB] SET ANSI_NULLS OFF
GO
ALTER DATABASE [ContactDB] SET ANSI_PADDING OFF
GO
ALTER DATABASE [ContactDB] SET ANSI_WARNINGS OFF
GO
ALTER DATABASE [ContactDB] SET ARITHABORT OFF
GO
ALTER DATABASE [ContactDB] SET AUTO_CLOSE OFF
GO
ALTER DATABASE [ContactDB] SET AUTO_SHRINK OFF
GO
ALTER DATABASE [ContactDB] SET AUTO_UPDATE_STATISTICS ON
GO
ALTER DATABASE [ContactDB] SET CURSOR_CLOSE_ON_COMMIT OFF
GO
ALTER DATABASE [ContactDB] SET CURSOR_DEFAULT  GLOBAL
GO
ALTER DATABASE [ContactDB] SET CONCAT_NULL_YIELDS_NULL OFF
GO
ALTER DATABASE [ContactDB] SET NUMERIC_ROUNDABORT OFF
GO
ALTER DATABASE [ContactDB] SET QUOTED_IDENTIFIER OFF
GO
ALTER DATABASE [ContactDB] SET RECURSIVE_TRIGGERS OFF
GO
ALTER DATABASE [ContactDB] SET  DISABLE_BROKER
GO
ALTER DATABASE [ContactDB] SET AUTO_UPDATE_STATISTICS_ASYNC OFF
GO
ALTER DATABASE [ContactDB] SET DATE_CORRELATION_OPTIMIZATION OFF
GO
ALTER DATABASE [ContactDB] SET TRUSTWORTHY OFF
GO
ALTER DATABASE [ContactDB] SET ALLOW_SNAPSHOT_ISOLATION OFF
GO
ALTER DATABASE [ContactDB] SET PARAMETERIZATION SIMPLE
GO
ALTER DATABASE [ContactDB] SET READ_COMMITTED_SNAPSHOT OFF
GO
ALTER DATABASE [ContactDB] SET HONOR_BROKER_PRIORITY OFF
GO
ALTER DATABASE [ContactDB] SET RECOVERY FULL
GO
ALTER DATABASE [ContactDB] SET  MULTI_USER
GO
ALTER DATABASE [ContactDB] SET PAGE_VERIFY CHECKSUM  
GO
ALTER DATABASE [ContactDB] SET DB_CHAINING OFF
GO
ALTER DATABASE [ContactDB] SET FILESTREAM( NON_TRANSACTED_ACCESS = OFF )
GO
ALTER DATABASE [ContactDB] SET TARGET_RECOVERY_TIME = 0 SECONDS
GO
USE [ContactDB]
GO
/****** Object:  Table [dbo].[Contacts]    Script Date: 07/23/2017 21:48:54 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Contacts](
    [ContactId] [int] IDENTITY(1,1) NOT NULL,
    [Email] [varchar](50) NULL,
    [FirstName] [varchar](50) NULL,
    [LastName] [varchar](50) NULL,
    [Phone] [varchar](50) NULL,
 CONSTRAINT [PK_Contacts] PRIMARY KEY CLUSTERED
(
    [ContactId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, _
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO
SET ANSI_PADDING OFF
GO
USE [master]
GO
ALTER DATABASE [ContactDB] SET  READ_WRITE
GO

Now we can generate the models using the above existing database. But before that, we need to install the following packages in our data access class library project using Package Manager Console.

  1. Microsoft.EntityFrameworkCore.Tools
  2. Microsoft.EntityFrameworkCore.Design
  3. Microsoft.EntityFrameworkCore.SqlServer.Design

Run the following command in our data access library project to create a model from the existing database (please see the following screenshot):

Please refer to the below link to create models from existing database in more details:

Image 7

Once the above command is successfully executed, then one folder name “Models” is created in our data access layer project which contains the database entities. (See the following screenshot.)

Image 8

ContactDBContext.cs Class

using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;

namespace DataAccessLibrary.Models
{
    public partial class ContactDBContext : DbContext
    {
        public virtual DbSet<Contacts> Contacts { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
               // #warning To protect potentially sensitive information in your connection string, you should move it out of source code. See http://go.microsoft.com/fwlink/?LinkId=723263 for guidance on storing connection strings.
                optionsBuilder.UseSqlServer(@"Server=yourservername;Database=ContactDB;User Id=youruser;Password=yourpassword;Trusted_Connection=True;");
            }
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Contacts>(entity =>
            {
                entity.HasKey(e => e.ContactId);

                entity.Property(e => e.Email)
                    .HasMaxLength(50)
                    .IsUnicode(false);

                entity.Property(e => e.FirstName)
                    .HasMaxLength(50)
                    .IsUnicode(false);

                entity.Property(e => e.LastName)
                    .HasMaxLength(50)
                    .IsUnicode(false);

                entity.Property(e => e.Phone)
                    .HasMaxLength(50)
                    .IsUnicode(false);
            });
        }
    }
}

Contacts.cs

C#
using System;

namespace DataAccessLibrary.Models
{
    public partial class Contacts
    {
        public int ContactId { get; set; }
        public string Email { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Phone { get; set; }
    }
}

Here, we are done with our data access layer. Now add your data access reference in business layer project and add your business layer reference in your web project.

Image 9

Image 10

Now let's start to create a repository class in business layer library project which is used for crud operation.

Create a new folder (i.e., "Repository") inside business layer project and create one interface (i.e., "IContactRepository.cs") and one class (i.e. "ContactRepository") within that folder which contains few methods for our crud operation.

IContactRepository.cs

C#
using BusinessLibrary.Model;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace BusinessLibrary
{
    public interface IContactRepository
    {
        Task<List<Contact>> GetAllContact();
        Task<bool> SaveContact(Contact model);
        Task<bool> DeleteContactByID(int id);
    }
}

using DataAccessLibrary.Models;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using BusinessLibrary.Model;
using System.Linq;

namespace BusinessLibrary
{
    public class ContactRepository : IContactRepository
    {
        public async Task<bool> DeleteContactByID(int id)
        {
            using (ContactDBContext db = new ContactDBContext())
            {

                Contacts contact = db.Contacts.Where(x => x.ContactId == id).FirstOrDefault();
                if (contact != null)
                {
                    db.Contacts.Remove(contact);
                }
                return await db.SaveChangesAsync() >= 1;
            }
        }

        public async Task<List<Contact>> GetAllContact()
        {
            using (ContactDBContext db = new ContactDBContext())
            {
                return await (from a in db.Contacts
                              select new Contact
                              {
                                  ContactId = a.ContactId,
                                  FirstName = a.FirstName,
                                  LastName = a.LastName,
                                  Email = a.Email,
                                  Phone = a.Phone
                              }).ToListAsync();
            }
        }

        public async Task<bool> SaveContact(Contact model)
        {
            using (ContactDBContext db = new ContactDBContext())
            {
                Contacts contact = db.Contacts.Where
                        (x => x.ContactId == model.ContactId).FirstOrDefault();
                if (contact == null)
                {
                    contact = new Contacts()
                    {
                        FirstName = model.FirstName,
                        LastName = model.LastName,
                        Email = model.Email,
                        Phone = model.Phone
                    };
                    db.Contacts.Add(contact);

                }
                else
                {
                    contact.FirstName = model.FirstName;
                    contact.LastName = model.LastName;
                    contact.Email = model.Email;
                    contact.Phone = model.Phone;
                }

                return await db.SaveChangesAsync() >= 1;
            }
        }
    }
}

We are done with our business layer project. Now we need to register the above repository in our web project's startup.cs class.

Startup.cs Class

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SpaServices.Webpack;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using BusinessLibrary;

namespace Angular4Core2
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.AddTransient<IContactRepository, ContactRepository>();
        }

        // This method gets called by the runtime. 
        // Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
                {
                    HotModuleReplacement = true
                });
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseStaticFiles();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");

                routes.MapSpaFallbackRoute(
                    name: "spa-fallback",
                    defaults: new { controller = "Home", action = "Index" });
            });
        }
    }
}

I have added ContactController class and injected our repository as a dependency, so that we can perform CRUD operations using the repository.

ContactController.cs Class

C#
using BusinessLibrary;
using BusinessLibrary.Model;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

namespace Angular4Core2.Controllers
{
    public class ContactController : Controller
    {
        public IContactRepository ContactRepo;

        public ContactController(IContactRepository contactRepo)
        {
            ContactRepo = contactRepo;
        }

        [HttpGet, Produces("application/json")]
        public async Task<IActionResult> GetContacts()
        {
            var data = await ContactRepo.GetAllContact();
            return Json(new { result = data });
        }

        [HttpPost, Produces("application/json")]
        public async Task<IActionResult> SaveContact([FromBody] Contact model)
        {
            return Json(await ContactRepo.SaveContact(model));
        }

        [HttpDelete]
        public async Task<IActionResult> DeleteContactByID(int id)
        {
            return Json(await ContactRepo.DeleteContactByID(id));
        }

    }
}

Now, we need to modify the "package.json" file by adding the following dependencies.

package.json

JavaScript
{
  "name": "Angular4Core2",
  "version": "0.0.0",
  "scripts": {
    "test": "karma start ClientApp/test/karma.conf.js"
  },
  "dependencies": {
    "@angular/animations": "4.1.2",
    "@angular/common": "4.1.2",
    "@angular/compiler": "4.1.2",
    "@angular/core": "4.1.2",
    "@angular/forms": "4.1.2",
    "@angular/http": "4.1.2",
    "@angular/platform-browser": "4.1.2",
    "@angular/platform-browser-dynamic": "4.1.2",
    "@angular/platform-server": "4.1.2",
    "@angular/router": "4.1.2",
    "@types/node": "7.0.18",
    "angular2-template-loader": "0.6.2",
    "aspnet-prerendering": "^2.0.5",
    "aspnet-webpack": "^1.0.29",
    "awesome-typescript-loader": "3.1.3",
    "bootstrap": "3.3.7",
    "css": "2.2.1",
    "css-loader": "0.28.1",
    "es6-shim": "0.35.3",
    "event-source-polyfill": "0.0.9",
    "expose-loader": "0.7.3",
    "extract-text-webpack-plugin": "2.1.0",
    "file-loader": "0.11.1",
    "html-loader": "0.4.5",
    "isomorphic-fetch": "2.2.1",
    "jquery": "3.2.1",
    "json-loader": "0.5.4",
    "preboot": "4.5.2",
    "raw-loader": "0.5.1",
    "reflect-metadata": "0.1.10",
    "rxjs": "5.4.0",
    "style-loader": "0.17.0",
    "to-string-loader": "1.1.5",
    "typescript": "2.3.2",
    "url-loader": "0.5.8",
    "webpack": "2.5.1",
    "webpack-hot-middleware": "2.18.0",
    "webpack-merge": "4.1.0",
    "zone.js": "0.8.10",
    "primeng": "1.0.0",
    "toastr-ng2": "4.1.1"
  },
  "devDependencies": {
    "@types/chai": "3.5.2",
    "@types/jasmine": "2.5.47",
    "chai": "3.5.0",
    "jasmine-core": "2.6.1",
    "karma": "1.7.0",
    "karma-chai": "0.1.0",
    "karma-chrome-launcher": "2.1.1",
    "karma-cli": "1.0.1",
    "karma-jasmine": "1.1.0",
    "karma-webpack": "2.0.3"
  }
}

PrimeNG Components

In above package.json file, we are using primeng components (such as datatable which is used to display the data in tabular format) which is a collection of rich UI components and it is a open source component. Please see the following link for more details:

toastr-ng2 NPM

We are also using toastr-ng2 package to display the toast for a specific crud operation. Please refer to the following link for the additional features and installation steps:

Angular 4

Now, let's start to create client app in our application using Angular 4.

Here, I have created a client side model inside our app folder which contains the properties as per the server side model which we defined in the above section:

Image 11

contact.ts:

JavaScript
export interface Contact {
    contactId?;
    firstName?;
    lastName?;
    email?;
    phone?;
}

index.ts:

JavaScript
export * from './contact';

Now the next step is to create a client service which provides the functionality for transferring the data between the client and server by Angular Http client.

Image 12

contact.service.ts:

JavaScript
import { Injectable } from '@angular/core';
import { Http, Response, Headers, RequestOptions } from '@angular/http';
import { Contact } from '../_models/index';
import { Observable } from 'rxjs/Observable';
import "rxjs/Rx";

@Injectable()
export class ContactService {

    private _getContactsUrl = "/Contact/GetContacts";
    public _saveUrl: string = '/Contact/SaveContact/';
    public _updateUrl: string = '/Contact/UpdateContact/';
    public _deleteByIdUrl: string = '/Contact/DeleteContactByID/';

    constructor(private http: Http) { }

    getContacts() {
        var headers = new Headers();
        headers.append("If-Modified-Since", "Tue, 24 July 2017 00:00:00 GMT");
        var getContactsUrl = this._getContactsUrl;
        return this.http.get(getContactsUrl, { headers: headers })
            .map(response => <any>(<Response>response).json());
    }

    //Post Savd and Update operation
    saveContact(contact: Contact): Observable<string> {
        let body = JSON.stringify(contact);
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });

        return this.http.post(this._saveUrl, body, options)
            .map(res => res.json().message)
            .catch(this.handleError);
    }

    //Delete Operation
    deleteContact(id: number): Observable<string> {
        //debugger
        var deleteByIdUrl = this._deleteByIdUrl + '/' + id

        return this.http.delete(deleteByIdUrl)
            .map(response => response.json().message)
            .catch(this.handleError);
    }

    private handleError(error: Response) {
        return Observable.throw(error.json().error || 'Opps!! Server error');
    }

}

index.ts:

JavaScript
export * from './contact.service';

Now we need to define our contact component along with its view details which is responsible for handling all the crud operations in our application.

Image 13

contact.component.ts:

JavaScript
import { Component, OnInit } from '@angular/core';
import { Contact } from '../../_models/index';
import { ContactService } from '../../_services/index';
import { ToastrService } from 'toastr-ng2';
import { InputTextModule, DataTableModule, ButtonModule, DialogModule } from 'primeng/primeng';

class ContactInfo implements Contact {
    constructor(public contactId?, public firstName?, public lastName?, public email?, public phone?) { }
}

@Component({
    selector: 'contact',
    templateUrl: './contact.component.html'
})
export class ContactComponent implements OnInit {

    private rowData: any[];
    displayDialog: boolean;
    displayDeleteDialog: boolean;
    newContact: boolean;
    contact: Contact = new ContactInfo();
    contacts: Contact[];
    public editContactId: any;
    public fullname: string;

    constructor(private contactService: ContactService, private toastrService: ToastrService) {

    }

    ngOnInit() {
        this.editContactId = 0;
        this.loadData();
    }

    loadData() {
        this.contactService.getContacts()
            .subscribe(res => {
                this.rowData = res.result;
            });
    }

    showDialogToAdd() {
        this.newContact = true;
        this.editContactId = 0;
        this.contact = new ContactInfo();
        this.displayDialog = true;
    }


    showDialogToEdit(contact: Contact) {
        this.newContact = false;
        this.contact = new ContactInfo();
        this.contact.contactId = contact.contactId;
        this.contact.firstName = contact.firstName;
        this.contact.lastName = contact.lastName;
        this.contact.email = contact.email;
        this.contact.phone = contact.phone;
        this.displayDialog = true;
    }

    onRowSelect(event) {
    }

    save() {
        this.contactService.saveContact(this.contact)
            .subscribe(response => {
                this.contact.contactId > 0 ? this.toastrService.success('Data updated Successfully') :
                    this.toastrService.success('Data inserted Successfully');
                this.loadData();
            });
        this.displayDialog = false;
    }

    cancel() {
        this.contact = new ContactInfo();
        this.displayDialog = false;
    }


    showDialogToDelete(contact: Contact) {
        this.fullname = contact.firstName + ' ' + contact.lastName;
        this.editContactId = contact.contactId;
        this.displayDeleteDialog = true;
    }

    okDelete(isDeleteConfirm: boolean) {
        if (isDeleteConfirm) {
            this.contactService.deleteContact(this.editContactId)
                .subscribe(response => {
                    this.editContactId = 0;
                    this.loadData();
                });
            this.toastrService.error('Data Deleted Successfully');
        }
        this.displayDeleteDialog = false;
    }
}

contact.component.html (which contains the primeng components):

HTML
<table style="width:80%;margin-left: 100px">
    <tr>
        <td>
           <h2>Contact Details</h2>
        </td>
    </tr>
    <tr>
        <td>
            <button type="button" pButton icon="fa-plus" 
             style="float:left" (click)="showDialogToAdd()" label="Add"></button>
        </td>
    </tr>
    <tr>
        <td>
            <br />
            <div class="ContentSideSections Implementation">
                <p-dataTable [value]="rowData"  [rows]="10" [paginator]="true" 
                         [pageLinks]="3" [rowsPerPageOptions]="[5,10,20]">
                    <header>Contact Details</header>
                    <p-column field="contactId" [style]="{'width':'50px'}" 
                       header="ID" [sortable]="true"></p-column>
                    <p-column field="firstName" 
                    header="FirstName" [sortable]="true"></p-column>
                    <p-column field="lastName" 
                    header="LastName" [sortable]="true"></p-column>
                    <p-column field="email" [style]="{'width':'200px'}" 
                           header="Email" [sortable]="true"></p-column>
                    <p-column field="phone" 
                    header="Phone No" [sortable]="true"></p-column>
                    <p-column header="Edit">
                        <ng-template let-col let-contact="rowData" 
                        pTemplate type="body">
                            <button type="button" pButton icon="fa-check" 
                              (click)="showDialogToEdit(contact)" 
                              label="Edit"></button>
                        </ng-template>
                    </p-column>
                    <p-column header="Delete">
                        <ng-template let-col let-contact="rowData" 
                        pTemplate type="body">
                            <button type="button" pButton icon="fa-close" 
                             (click)="showDialogToDelete(contact)" 
                             label="Delete"></button>
                        </ng-template>
                    </p-column>
                    <footer><div class="ui-helper-clearfix" 
                    style="width:100%"></div></footer>
                </p-dataTable>

                <p-dialog header="Contact Details" [(visible)]="displayDialog" 
                             [responsive]="true" 
                             showEffect="fade" [modal]="true">
                    <div class="ui-grid ui-grid-responsive ui-fluid ui-grid-pad">
                        <div class="ui-grid-row">
                            <div class="ui-grid-col-4"><label 
                            for="firstname">FirstName</label></div>
                            <div class="ui-grid-col-8"><input pInputText id="vin" 
                                           [(ngModel)]="contact.firstName" /></div>
                        </div>
                        <div class="ui-grid-row">
                            <div class="ui-grid-col-4"><label 
                            for="lastname">LastName</label></div>
                            <div class="ui-grid-col-8"><input pInputText id="lastname" 
                                   [(ngModel)]="contact.lastName" /></div>
                        </div>
                        <div class="ui-grid-row">
                            <div class="ui-grid-col-4"><label 
                            for="email">Email</label></div>
                            <div class="ui-grid-col-8"><input type="email" 
                                 pInputText id="email" [(ngModel)]="contact.email" /></div>
                        </div>
                        <div class="ui-grid-row">
                            <div class="ui-grid-col-4"><label 
                            for="mobile">Phone</label></div>
                            <div class="ui-grid-col-8"><input pInputText id="mobile" 
                                    [(ngModel)]="contact.phone" /></div>
                        </div>
                    </div>
                    <footer>
                        <div class="ui-dialog-buttonpane ui-widget-content ui-helper-clearfix">
                            <button type="button" pButton icon="fa-close" 
                                   (click)="cancel()" 
                                   label="Cancel"></button>
                            <button type="button" pButton icon="fa-check" 
                                   (click)="save()" *ngIf="newContact" 
                                   label="Save"></button>
                            <button type="button" pButton icon="fa-check" 
                                   (click)="save()" *ngIf="!newContact" 
                                   label="Update"></button>
                        </div>
                    </footer>
                </p-dialog>
                <p-dialog header="Confirm Deletion" [(visible)]="displayDeleteDialog" 
                                     modal="modal" showEffect="fade">
                    <p>
                        Are you sure to delete the following contact?
                    </p>
                    <p>
                        <strong>{{ fullname }}</strong><br />
                    </p>
                    <footer>
                        <div class="ui-dialog-buttonpane ui-widget-content ui-helper-clearfix">
                            <button type="button" pButton icon="fa-close" 
                                     (click)="okDelete(false)" label="No"></button>
                            <button type="button" pButton icon="fa-check" 
                                     (click)="okDelete(true)" label="Yes"></button>
                        </div>
                    </footer>
                </p-dialog>
            </div>
        </td>
    </tr>
</table>

Application Module file.

Now we need to modify our application module file (i.e., "app.module.shared.ts") which is used to organize and bootstrap our Angular 4 application.

Image 14

app.module.shared.ts:

JavaScript
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { RouterModule } from '@angular/router';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { Headers, RequestOptions, BaseRequestOptions } from '@angular/http';
import { APP_BASE_HREF, CommonModule, Location, LocationStrategy, HashLocationStrategy } 
             from '@angular/common';
// third party module to display toast
import { ToastrModule } from 'toastr-ng2';
//PRIMENG - Third party module
import { InputTextModule, DataTableModule, ButtonModule, DialogModule } from 'primeng/primeng';

import { AppComponent } from './components/app/app.component';
import { NavMenuComponent } from './components/navmenu/navmenu.component';
import { ContactComponent } from './components/contact/contact.component';

import { ContactService } from './_services/index';

class AppBaseRequestOptions extends BaseRequestOptions {
    headers: Headers = new Headers();
    constructor() {
        super();
        this.headers.append('Content-Type', 'application/json');
        this.body = '';
    }
}

@NgModule({
    declarations: [
        AppComponent,
        NavMenuComponent,
        ContactComponent
    ],
    providers: [ContactService,
        { provide: LocationStrategy, useClass: HashLocationStrategy },
        { provide: RequestOptions, useClass: AppBaseRequestOptions }],
    imports: [
        CommonModule,
        HttpModule,
        FormsModule,
        BrowserAnimationsModule,
        ToastrModule.forRoot(),
        InputTextModule, DataTableModule, ButtonModule, DialogModule,
        RouterModule.forRoot([
            { path: '', redirectTo: 'contact', pathMatch: 'full' },
            { path: 'contact', component: ContactComponent },
            { path: '**', redirectTo: 'contact' }
        ])
    ]
})
export class AppModuleShared {
}

I have slightly modify the navigation template for this application which is as follows:

navmenu.component.html

HTML
<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]="['/contact']">Angular4Core2</a>
        </div>
        <div class='clearfix'></div>
        <div class='navbar-collapse collapse'>
            <ul class='nav navbar-nav'>
                <li [routerLinkActive]="['link-active']">
                    <a [routerLink]="['/contact']">
                        <span class='glyphicon glyphicon-home'></span> Contact
                    </a>
                </li>
            </ul>
        </div>
    </div>
</div>

That's it. Now we are ready to run our application using IIS Express.

Let's create a new contact first by clicking the add button.

Image 15

When we click on "Add" button, the following model pop-up will display with the relevant fields with save and cancel button.

Image 16

When we clicks on Save button, the information is saved successfully in database and it shows a successful toast message to user.

Image 17

Let's update the above information with the "Edit" button in grid. When we click on edit button, the same model dialog will appear with the save information. Here, I have changed a little information such as email and phone details and click on update button.

Image 18

It shows the updated information with updated toast message (please see the following screenshot):

Image 19

For delete operation, we have "Delete" button in the grid. When we click on delete button, a confirmation dialog will appear for the user with Yes/No option. (Please see the following screenshot.)

Image 20

When we click on Yes button, then the information will be deleted from the database and it will show a delete toast to the user. (Please see the following screenshot.)

Image 21

That's all! We are done with our crud operation using ASP.NET Core 2 and Angular 4.

Hope you liked the article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead
India India
My name is Prashant Ramteke. I have 11+ years of experience in Microsoft technologies. Currently working as a Senior Team Lead.
Currently focusing on C#, ASP.NET Core, EntityFramework core, JavaScript, Angular,React.js,Redux and Microservices.

Comments and Discussions

 
QuestionVery good article! Pin
Muhammad Imran Shahid22-Jan-18 17:46
Muhammad Imran Shahid22-Jan-18 17:46 
Questionerror Unable to get property 'apply' Pin
hassan_bht14-Nov-17 22:30
hassan_bht14-Nov-17 22:30 
QuestionDon't use Microsoft.EntityFrameworkCore.SqlServer.Design Pin
Sami L9-Nov-17 23:13
Sami L9-Nov-17 23:13 
Questionhow to add MS Identity in Project Pin
Sheikh Muhammad Munir6-Sep-17 22:10
professionalSheikh Muhammad Munir6-Sep-17 22:10 
QuestionID Attributes Pin
Bhautik Bhavsar28-Aug-17 19:40
Bhautik Bhavsar28-Aug-17 19:40 
QuestionGood but .. Pin
ovisariesdk21-Aug-17 3:23
ovisariesdk21-Aug-17 3:23 
QuestionI know I am most likely missing the point but .. Pin
L Viljoen1-Aug-17 22:01
professionalL Viljoen1-Aug-17 22:01 
GeneralRe: I know I am most likely missing the point but .. Pin
Motlatsij4-Aug-17 0:37
Motlatsij4-Aug-17 0:37 
QuestionHow do you add authentication to this? Pin
Layinka1-Aug-17 1:00
professionalLayinka1-Aug-17 1:00 
AnswerRe: How do you add authentication to this? Pin
Member 1341937116-Oct-17 0:54
Member 1341937116-Oct-17 0:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.