Aug 20

Written by: Michael Washington
8/20/2017 1:34 PM  RssIcon

You can easily create an Angular 4 application using Microsoft .Net Core 2.0 that has an application menu shell and authentication.

This example also uses the open source component, PrimeNG.

Install

You will first need to install:

Create The Project

image

Open Visual Studio 2017 and create a new Project.

image

Create an ASP.NET Core Web Application.

image

Use the Angular template.

image

The application will be created.

image

Note you will have to wait about two minutes for the node packages to be restored before the application is ready to run.

You can monitor the progress by looking at the “node_modules” directory (you will have to go directly into the windows file manager under your project root to see it).

image

You will know it is complete when the Installing packages complete message appears.

image

Select Build then Rebuild Solution.

image

Select Debug, then Start Without Debugging to run the application.

image

The application will display.

Install PrimeNG

image

We want to add the following lines to package.json to add PrimeNG for Angular 4 (and it’s dependency, font-awesome):

    "font-awesome": "^4.7.0",
    "primeng": "^4.0.1"

When we save the file, PrimeNG will be installed and added to the node_modules folder.

image

Open the webpack.config.vendor.js file and add:

    'font-awesome/css/font-awesome.css',
    'primeng/primeng',
    'primeng/resources/themes/omega/theme.css',
    'primeng/resources/primeng.min.css', 

image

Also, in rules/test, add:

    |gif, 

Save the file.

image

At this time, PrimeNg does not support pre-rendering so in ..\Views\Home\Index.cshtml, change:

<app asp-prerender-module="ClientApp/dist/main-server">Loading...</app>

to:

<app>Loading...</app>

Save the file.

image

We altered the webpack.config.vendor.js file (to add PrimeNg and Font Awesome) but it is not updated by the normal build process. We have to run its configuration manually whenever we alter it.
In a command prompt, at the project root, run:

webpack --config webpack.config.vendor.js

(Note: If you don’t already have the webpack tool installed (for example you get an error when you run the code above), you’ll need to run: npm install -g webpack first)

Set Up The Database

image

Create a new database.

image

Run the script at this link to create the tables:

http://lightswitchhelpwebsite.com/Downloads/DatabaseSetUp.txt

Add Authentication

For the remaining steps, you will need to copy code from the project in the downloads on this site.

image

Copy all the files from the Models folder into your project.

(note that the namespace is set to DotNetComplete. You will need to change this in the files if your project name is different)

image

Open the Startup.cs file.

Add the following namespaces:

 

// **************
// Authentication
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using DotNetComplete.Data;

 

Add the following code to the ConfigureServices method:

 

    // **************
    // Authentication
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

 

Add the following code to the env.IsDevelopment() section (in the Configure method):

 

    // **************
    // Authentication
    app.UseBrowserLink();
    app.UseDatabaseErrorPage();

 

Add the following under the app.UseStaticFiles() line:

 

    // **************
    // Authentication
    app.UseAuthentication();

 

image

Open the appsettings.json file and add the following connection string:

image

(note that it mist be altered to connect to the database you created earlier)

image

Create a folder called Data and add the following files and code:

 

ApplicationDbContext.cs:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace DotNetComplete.Data
{
    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
            // Customize the ASP.NET Identity model and override the defaults if needed.
            // For example, you can rename the ASP.NET Identity table names and more.
            // Add your customizations after calling base.OnModelCreating(builder);
        }
    }
}

 

ApplicationUser.cs:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
namespace DotNetComplete.Data
{
    // Add profile data for application users by adding properties to the ApplicationUser class
    public class ApplicationUser : IdentityUser
    {
    }
}

 

Angular Code

image

Replace all the code in the ClientApp folder with the code from the download.

This creates the application structure including the application shell and menu that is covered here:

Create Your Own Angular 4 Application Shell Using PrimeNG

This also makes extensive use of Angular Observables covered here:

Using Observables To Communicate Between Angular Components

image

Also, copy the WebApi folder, and its contents, to your project.

The remainder of the article will cover how the Registration (creating a  new user) and the Login code works.

Registration

image

The register code is contained in the register folder and in the services folder.

image

The register.component.html file uses the following code to create the markup:

 

<div *ngIf='!showWaitGraphic'>
    <form [formGroup]="registerForm" (ngSubmit)="Register($event)">
        <input formControlName="username" pInputText size="30"
               type="text" placeholder="Username">
        <br /><br />
        <input formControlName="firstname" pInputText size="30"
               type="text" placeholder="First Name">
        <br /><br />
        <input formControlName="lastname" pInputText size="30"
               type="text" placeholder="Last Name">
        <br /><br />
        <input formControlName="email" pInputText size="30"
               type="email" placeholder="Email">
        <br /><br />
        <input formControlName="password" pInputText size="30"
               type="password" placeholder="password">
        <br /><br />
        <button pButton type="submit" label="Register" style="float: right;"></button>
    </form>
</div>
<div class="spinner" *ngIf='showWaitGraphic'>
    <div class="rect1"></div>
    <div class="rect2"></div>
    <div class="rect3"></div>
    <div class="rect4"></div>
    <div class="rect5"></div>
</div>

 

The associated register.component.ts file uses the following code:

 

import { Component, OnInit, OnDestroy, Input } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { FormBuilder, Validators } from '@angular/forms';
import { Subscription } from 'rxjs/Subscription';
import {
    DialogModule,
    InputTextModule,
    PasswordModule,
    ButtonModule
} from 'primeng/primeng';
import { IUser } from '../classes/user';
import { IRegister } from '../classes/register';
import { ILoginStatus } from '../classes/loginStatus';
import { IAuthentication } from '../classes/authentication';
import { UserService } from '../services/user.service';
import { RegisterService } from '../services/register.service';
import { IRegisterStatus } from '../classes/registerStatus';
@Component({
    selector: 'register-dialog',
    templateUrl: './register.component.html',
    styleUrls: ['./register.component.css']
})
export class RegisterComponent implements OnInit, OnDestroy {
    UserSubscription: Subscription;
    showWaitGraphic: boolean = false;
    user: IUser;
    register: IRegister;
    errorMessage: string;
    public registerForm = this.fb.group({
        username: ["", Validators.required],
        firstname: ["", Validators.required],
        lastname: ["", Validators.required],
        email: ["", Validators.required],
        password: ["", Validators.required]
    });
    // Register the service
    constructor(
        private _userService: UserService,
        private _registerService: RegisterService,
        public fb: FormBuilder) {
    }
    ngOnInit(): void {
        // Subscribe to the User service
        this.UserSubscription = this._userService.getCurrentUser().subscribe(
            user => {
                this.user = user;
            },
            error => {
                this.errorMessage = <any>error;
                alert(this.errorMessage);
            });
        this.showWaitGraphic = false;
    }
    Register() {
        // Get the form values
        let formData = this.registerForm.value;
        let username = formData.username;
        let email = formData.email;
        let password = formData.password;
        let firstName = formData.firstname;
        let lastName = formData.lastname;
        let Register: IRegister = {
            userName: username,
            email: email,
            password: password,
            firstName: firstName,
            lastName: lastName
        };
        // Call the service
        this.showWaitGraphic = true;
        this._userService.registerUser(Register).subscribe(
            RegisterStatus => {
                this.showWaitGraphic = false;
                // Was Register successful?
                if (!RegisterStatus.isSuccessful) {
                    alert(RegisterStatus.status);
                } else {
                    // Close the Register Dialog
                    this._registerService.setVisibility(false);
                }   
                
            },
            error => {
                this.errorMessage = <any>error;
                alert(this.errorMessage);
            });
    }
    ngOnDestroy(): void {
        // Important - Unsubscribe from any subscriptions
        this.UserSubscription.unsubscribe();
    }
}

 

This calls the following method in the user.service.ts file:

 

    registerUser(Register: IRegister): Observable<IRegisterStatus> {
        var _Url = 'api/Register';
        // This is a Post so we have to pass Headers
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });
        // Make the Angular Post
        return this._http.post(_Url, JSON.stringify(Register), options)
            .map((response: Response) => <IRegisterStatus>response.json())
            .catch(this.handleError);
    }

 

image

This calls the server side WebApi controller contained in the RegisterController.cs file:

 

using DotNetComplete.Data;
using DotNetComplete.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System;
namespace DotNetComplete.Controllers
{
    //api/Register
    [Route("api/[controller]")]
    public class RegisterController : Controller
    {
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly SignInManager<ApplicationUser> _signInManager;
        public RegisterController(
            UserManager<ApplicationUser> userManager,
            SignInManager<ApplicationUser> signInManager)
        {
            _userManager = userManager;
            _signInManager = signInManager;
        }
        // ********************************************************
        // Register
        // api/Register
        #region public IActionResult Index([FromBody]RegisterDTO Register)
        [HttpPost]
        [AllowAnonymous]
        public IActionResult Index([FromBody]RegisterDTO Register)
        {
            // RegisterStatus to return
            RegisterStatus objRegisterStatus = new RegisterStatus();
            objRegisterStatus.status = "Registration Failure";
            objRegisterStatus.isSuccessful = false;
            // Get values passed
            var paramUserName = Register.userName.Trim();
            var paramPassword = Register.password.Trim();
            var paramFirstName = Register.firstName.Trim();
            var paramLastName = Register.lastName.Trim();
            var paramEmail = Register.email.Trim();
            // Validation ****************************
            EmailValidation objEmailValidation = new EmailValidation();
            if (!objEmailValidation.IsValidEmail(paramEmail))
            {
                objRegisterStatus.status = "This Email is not valid.";
                objRegisterStatus.isSuccessful = false;
                return Ok(objRegisterStatus);
            }
            if ((paramUserName == null) || (paramUserName.Length < 1))
            {
                objRegisterStatus.status = "This Username is not long enough.";
                objRegisterStatus.isSuccessful = false;
                return Ok(objRegisterStatus);
            }
            // Create Account ****************************
            try
            {
                var user = new ApplicationUser { UserName = paramUserName, Email = paramEmail };
                var result = _userManager.CreateAsync(user, paramPassword).Result;
                if (result.Succeeded)
                {
                    // Sign the User in
                    var SignInResult = _signInManager.PasswordSignInAsync(
                        paramUserName, paramPassword, false, lockoutOnFailure: false).Result;
                    if (!SignInResult.Succeeded)
                    {
                        // Return the error
                        objRegisterStatus.status = $"Could not sign user {paramUserName} in.";
                        objRegisterStatus.isSuccessful = false;
                        return Ok(objRegisterStatus);
                    }
                }
                else
                {
                    // Create user failed
                    // Return the errors from the Memberhip API Creation
                    string strErrors = "";
                    foreach (var Error in result.Errors)
                    {
                        strErrors = strErrors + "\n" + Error.Description;
                    }
                    objRegisterStatus.status = strErrors;
                    objRegisterStatus.isSuccessful = false;
                    return Ok(objRegisterStatus);
                }
                objRegisterStatus.status = "Success";
                objRegisterStatus.isSuccessful = true;
                return Ok(objRegisterStatus);
            }
            catch (Exception ex)
            {
                objRegisterStatus.status = ex.Message;
                objRegisterStatus.isSuccessful = false;
                return Ok(objRegisterStatus);
            }
        }
        #endregion
    }
}

Login

image

The code for the login control is contained in the login folder.

The following markup for the control is contained in the login.component.html file:

 

<div class="spinner" *ngIf='showWaitGraphic'>
    <div class="rect1"></div>
    <div class="rect2"></div>
    <div class="rect3"></div>
    <div class="rect4"></div>
    <div class="rect5"></div>
</div>
<div *ngIf='!isMigrateMode'>
    <div *ngIf='!showWaitGraphic'>
        <form [formGroup]="loginForm" (ngSubmit)="logIn($event)">
            <input formControlName="username" pInputText size="30"
                   type="text" placeholder="Your Username">
            <br /><br />
            <input formControlName="password" pInputText size="30"
                   type="password" placeholder="Your password">
            <br /><br />
            <button pButton type="submit" label="Log In" 
                    class="ui-button-success" style="float: right;"></button>
        </form>
    </div>
</div>

 

The associated login.component.ts file uses the following code:

 

import { Component, OnInit, OnDestroy, Input } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { FormBuilder, Validators } from '@angular/forms';
import { Subscription } from 'rxjs/Subscription';
import {
    DialogModule,
    InputTextModule,
    PasswordModule,
    ButtonModule
} from 'primeng/primeng';
import { IUser } from '../classes/user';
import { ILoginStatus } from '../classes/loginStatus';
import { IAuthentication } from '../classes/authentication';
import { UserService } from '../services/user.service';
import { LoginService } from '../services/login.service';
@Component({
    selector: 'login-dialog',
    templateUrl: './login.component.html',
    styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit, OnDestroy {
    UserSubscription: Subscription;
    LoginSubscription: Subscription;
    showWaitGraphic: boolean = false;
    user: IUser;
    isMigrateMode: boolean = false;
    username: string = "";
    password: string = "";
    passwordNew: string = "";
    errorMessage: string;
    public loginForm = this.fbLogin.group({
        username: ["", Validators.required],
        password: ["", Validators.required]
    });
    // Register the service
    constructor(
        private _userService: UserService,
        private _loginService: LoginService,
        public fbLogin: FormBuilder) {
    }
    ngOnInit(): void {
        // Subscribe to the User service
        this.UserSubscription = this._userService.getCurrentUser().subscribe(
            user => {
                this.user = user;
            },
            error => {
                this.errorMessage = <any>error;
                alert(this.errorMessage);
            });
        this.showWaitGraphic = false;
        // Subscribe to the LoginSubscription Service
        this.LoginSubscription = this._loginService.getVisbility().subscribe(
            (visibility: boolean) => {
                if (visibility == true) {
                    // If the Login Dialog is true
                    // it means the Login button was pressed
                    // Set this form to the Login form
                    this.isMigrateMode = false;
                }
            });
    }
    logIn() {
        // Get the form values
        let formData = this.loginForm.value;
        this.username = formData.username;
        this.password = formData.password;
        let Authentication: IAuthentication = {
            userName: this.username,
            password: this.password
        };
        // Call the service
        this.showWaitGraphic = true;
        this._userService.loginUser(Authentication).subscribe(
            LoginStatus => {
                this.showWaitGraphic = false;
                // Was Login successful?
                if (!LoginStatus.isLoggedIn) {                    
                        alert(LoginStatus.status);
                } else {
                    // Close the Login Dialog
                    this._loginService.setVisibility(false);
                }
            },
            error => {
                this.errorMessage = <any>error;
                alert(this.errorMessage);
            });
    }
    ngOnDestroy(): void {
        // Important - Unsubscribe from any subscriptions
        this.UserSubscription.unsubscribe();
        this.LoginSubscription.unsubscribe();
    }
}

 

This calls the following method in the user.service.ts file:

 

    loginUser(Authentication: IAuthentication): Observable<ILoginStatus> {
        var _Url = 'api/Login';
        // This is a Post so we have to pass Headers
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });
        // Make the Angular Post
        return this._http.post(_Url, JSON.stringify(Authentication), options)
            .map((response: Response) => <ILoginStatus>response.json())
            .catch(this.handleError);
    }

 

image

This calls the server side WebApi controller contained in the LoginController.cs file:

 

using DotNetComplete.Data;
using DotNetComplete.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
namespace DotNetComplete.Controllers
{
    //api/Login
    [Route("api/[controller]")]
    public class LoginController : Controller
    {
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly SignInManager<ApplicationUser> _signInManager;
        public LoginController(
            UserManager<ApplicationUser> userManager,
            SignInManager<ApplicationUser> signInManager)
        {
            _userManager = userManager;
            _signInManager = signInManager;
        }
        // ********************************************************
        // Login
        // api/Login
        #region public IActionResult CurrentUser()
        [HttpGet]
        [AllowAnonymous]
        public IActionResult CurrentUser()
        {
            // User to return
            User objUser = new User();
            // See if the user is logged in
            if (this.User.Identity.IsAuthenticated)
            {
                // They are logged in
                objUser.userName = this.User.Identity.Name;
                objUser.isLoggedIn = true;
            }
            else
            {
                // They are not logged in
                objUser.userName = "Not Logged in";
                objUser.isLoggedIn = false;
            }
            // Return the result
            return Ok(objUser);
        }
        #endregion
        // (POST) api/Login 
        #region public IActionResult Index([FromBody]DTOAuthentication Authentication)
        [HttpPost]
        [AllowAnonymous]
        public IActionResult Index([FromBody]DTOAuthentication Authentication)
        {
            // LoginStatus to return
            LoginStatus objLoginStatus = new LoginStatus();
            objLoginStatus.isLoggedIn = false;
            // Get values passed
            var paramUserName = Authentication.userName;
            var paramPassword = Authentication.password;
            if ((paramUserName != null) && (paramPassword != null))
            {
                // This doesn't count login failures towards account lockout
                // To enable password failures to trigger account lockout, 
                // set lockoutOnFailure: true
                var result = _signInManager.PasswordSignInAsync(paramUserName, 
                    paramPassword, false, lockoutOnFailure: false).Result;
                if (result.Succeeded)
                {
                    objLoginStatus.status = "Success";
                    objLoginStatus.isLoggedIn = true;
                    return Ok(objLoginStatus);
                }
                if (result.RequiresTwoFactor)
                {
                    objLoginStatus.status = "RequiresVerification";
                    return Ok(objLoginStatus);
                }
                if (result.IsLockedOut)
                {
                    objLoginStatus.status = "IsLockedOut";
                    return Ok(objLoginStatus);
                }
            }
            objLoginStatus.status = "Authentication Failure";
            return Ok(objLoginStatus);
        }
        #endregion
    }
}

Links

Create Your Own Angular 4 Application Shell Using PrimeNG

Using Observables To Communicate Between Angular Components

Angular 2-4

Upgrading JavaScriptServices and PrimeNG From Angular 2 to Angular 4+

PrimeNG

Microsoft Dot Net Core 2.0

Visual Studio 2017

Download

The project is available at http://lightswitchhelpwebsite.com/Downloads.aspx

You must have Visual Studio 2017 (or higher) installed to run the code.

Note: You will need to create a database, run the setup script in the database (to create the tables), and alter the appsettings.json file, in the project, to point to the database, and build the project before trying to run it.

10 comment(s) so far...


Gravatar

Re: An Angular 4 .Net Core 2.0 Example With Application Shell and Authentication

very nice article, you should do some more.

By mohammad on   10/9/2017 4:24 AM
Gravatar

Re: An Angular 4 .Net Core 2.0 Example With Application Shell and Authentication

Thanks for useful information.
The example works perfectly.
I don't way after successful Authentication, side menu did not update like Demo Video. In my opinion Demo Video from another Example.
Best regards Oleg.

By Oleg Gorlov on   11/21/2017 2:02 PM
Gravatar

Re: An Angular 4 .Net Core 2.0 Example With Application Shell and Authentication

@Oleg Gorlov - The video shows what it looks like if you make your web browser "smaller". Re-size it so it is not very wide. This will simulate a "mobile view" and then the menu will change and you will see a menu button in the upper right hand corner of the app.

By Michael Washington on   11/21/2017 2:04 PM
Gravatar

Re: An Angular 4 .Net Core 2.0 Example With Application Shell and Authentication

You are absolutely right. Works exactly as Video Demo. My question is it possible to dynamically update "side-menu" component after Authentication. 1.Menu Public Pages not Authentication Users 2.Menu Private Pages Authenticated Users
Best regards Oleg.

By Oleg Gorlov on   11/21/2017 4:26 PM
Gravatar

Re: An Angular 4 .Net Core 2.0 Example With Application Shell and Authentication

@Oleg Gorlov - In menu.service.ts I am 'dynamically' altering the menu based on your login status.

By Michael Washington on   11/21/2017 4:28 PM
Gravatar

Re: An Angular 4 .Net Core 2.0 Example With Application Shell and Authentication

@Oleg Gorlov - You have to trigger the menu to be reset after a person logs in. You have to reset both the normal full width menu and the mobile menu. You can use a service like the one covered here: http://lightswitchhelpwebsite.com/Blog/tabid/61/EntryId/4311/Using-Observables-To-Communicate-Between-Angular-Components.aspx Otherwise I have no other examples, sorry.

By Michael Washington on   11/21/2017 6:43 PM
Gravatar

Re: An Angular 4 .Net Core 2.0 Example With Application Shell and Authentication

very nice article,Thanks for useful information. I am new to angular, i have followed the above steps to create and i have copied the files as mentioned above but running with and issue, please help me in resolving it. The error is "CS0246 C# The type or namespace name could not be found (are you missing a using directive or an assembly reference?)
Thanks in advance

By naresh on   11/23/2017 5:44 AM
Gravatar

Re: An Angular 4 .Net Core 2.0 Example With Application Shell and Authentication

@naresh - Please download the sample code from the Downloads page on this site and compare it to your own.

By Michael Washington on   11/23/2017 5:45 AM
Gravatar

Re: An Angular 4 .Net Core 2.0 Example With Application Shell and Authentication

Hi Michael, i have downloaded the source, but still running out with the issue "error CS0246 The type or namespace name 'RegisterStatus' could not be found (are you missing a using directive or an assembly reference?)" please help me out in resolving the issue

By naresh on   11/23/2017 7:03 AM
Gravatar

Re: An Angular 4 .Net Core 2.0 Example With Application Shell and Authentication

@naresh - I re-downloaded my version from the Downloads page and confirmed it is still working as expected. I am sorry but this is the best I can do. To resolve your issue I recommend that you make a post to Stack Overflow (https://stackoverflow.com/questions/tagged/c%23).

By Michael Washington on   11/23/2017 7:04 AM

Your name:
Gravatar Preview
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Title:
Comment:
Security Code
CAPTCHA image
Enter the code shown above in the box below
Add Comment   Cancel 
Microsoft Visual Studio is a registered trademark of Microsoft Corporation / LightSwitch is a registered trademark of Microsoft Corporation