Current Path : /www/sites/www.coderblog.in/index/
Url:

NameSizeOptions
App_DataDIRnone
backupDIRnone
cgi-binDIRnone
cssDIRnone
imgDIRnone
metaDIRnone
wp-adminDIRnone
wp-contentDIRnone
wp-includesDIRnone
.htaccess3.35 KBDEL
.htaccess.bk1.84 KBDEL
404.html0.13 KBDEL
ads.txt1.07 KBDEL
favicon.ico2.19 KBDEL
index.php0.40 KBDEL
license.txt19.44 KBDEL
php.ini0.58 KBDEL
readme.html7.25 KBDEL
service.php0.00 KBDEL
web.config2.87 KBDEL
wp-activate.php7.21 KBDEL
wp-blog-header.php1.21 KBDEL
wp-comments-post.php2.27 KBDEL
wp-config-sample.php3.26 KBDEL
wp-config.php2.98 KBDEL
wp-cron.php5.49 KBDEL
wp-links-opml.php2.44 KBDEL
wp-load.php3.84 KBDEL
wp-login.php50.21 KBDEL
wp-mail.php8.52 KBDEL
wp-settings.php29.38 KBDEL
wp-signup.php33.71 KBDEL
wp-trackback.php4.98 KBDEL
xmlrpc.php3.13 KBDEL
Javascript – Coder Blog https://www.coderblog.in Join the coding revolution! Learn, share, and grow together! Sat, 03 May 2025 01:08:32 +0000 en-US hourly 1 https://wordpress.org/?v=6.8.1 What a magic to let you easily modify any website pages https://www.coderblog.in/2025/05/what-a-magic-to-let-you-easily-modify-any-website-pages/ https://www.coderblog.in/2025/05/what-a-magic-to-let-you-easily-modify-any-website-pages/#comments Sat, 03 May 2025 01:06:55 +0000 https://www.coderblog.in/?p=1300 Introduction Do you think that you can modify any website page’s content? For example, you can easily modify

<p>The post What a magic to let you easily modify any website pages first appeared on Coder Blog.</p>

]]>
Introduction

Do you think that you can modify any website page’s content? For example, you can easily modify Microsoft’s web page. 😁

Please wait, don’t be excited, I’m not going to teach you how to hack a website, I just want to introduce a technique that you can use to do that!

You can see the demo below:

Actually that’s easy to do, just one line command that you can modify the webpage what you want.😆

How to do

It’s hard to imagine that such a cool feature only requires one command in the console:

Ok, let’s do it!

Input the below JS command in Chrome console

document.designMode="on";

and then you will find the magic:

But I also need to let you know that this approach can only modify the webpage content in your local😅

What’s the designMode

Ok, now you know how to modify the webpage on runtime, but what’s this command?

from the MDN Docs description:

document.designMode controls whether the entire document is editable. Valid values are "on" and "off". According to the specification, this property is meant to default to "off". Firefox follows this standard. The earlier versions of Chrome and IE default to "inherit". Starting in Chrome 43, the default is "off" and "inherit" is no longer supported. In IE6-10, the value is capitalized.

and you can find the browser compatibility below

With this API, we can also edit Iframe nested pages:

iframeNode.contentDocument.designMode = "on";

Association API

Other APIs associated with designMode include contentEditable and execCommand (deprecated, but still available in some browsers).
contentEditable is similar to designMode, but contentEditable can make specific DOM elements editable, while designMode can only make the entire document editable.

The document.execCommand() method allows us to format, edit, or manipulate content in a web page. It is mainly used to operate editable content on a web page (such as <textarea> or elements set by setting the contentEditable or designMode attributes to “true”), such as bolding text, inserting links, adjusting font styles, etc. Since it has been deprecated by W3C, it is no longer introduced in this article.

Conclusion

This command is not only for fun, you can quickly to modify the webpage to show a demo to your client, or help the designer to know what needs to be changed in the page. Also, if you want to refer to the design of other websites, then make some modifications based on them to suit your needs. This command can also be helpful!

Loading

<p>The post What a magic to let you easily modify any website pages first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2025/05/what-a-magic-to-let-you-easily-modify-any-website-pages/feed/ 13
Generate PDF with Free Spire.PDF in .Net Core and download in Angular https://www.coderblog.in/2023/10/generate-pdf-with-free-spire-pdf-in-net-core-and-download-in-angular/ https://www.coderblog.in/2023/10/generate-pdf-with-free-spire-pdf-in-net-core-and-download-in-angular/#comments Fri, 27 Oct 2023 12:45:24 +0000 https://www.coderblog.in/?p=1107 1. Introduction If you want to handle the PDF in .Net project, I think you would know the

<p>The post Generate PDF with Free Spire.PDF in .Net Core and download in Angular first appeared on Coder Blog.</p>

]]>
1. Introduction

If you want to handle the PDF in .Net project, I think you would know the iTextSharp, it really a powerful PDF handler library, however, it not allow to free to use in commercial after version 2.1.7, and Free Spire.PDF is a Community Edition of the Spire.PDF for .NET, which is a totally free PDF API for commercial and personal use. As a standalone .NET library, Free Spire.PDF for .NET enables developers to create, write, edit, convert, print, handle and read PDF files on any .NET( C#, VB.NET, ASP.NET, .NET Core) applications.

I will base on the previous MyDemo project to demonstrate how to do it.

2. Create the API

2.1 Install Free Spire.PDF from Nuget

First, we need to install the Free Spire.PDF into API project from Nuget

2.2 Create the Controller

Create the GeneratePDFController and action Generate and then create the PDF demo page below

1) Create the pdfDocument and page

//Create a PdfDocument object
PdfDocument doc = new PdfDocument();

//Add a page
PdfPageBase page = doc.Pages.Add();

the elements will be put in the page base on x and y coordinates, so we can define the started coordinates

//Initialize x and y coordinates
float baseX = 100;
float baseY = 30;

create two brush for different font color and create a font

PdfSolidBrush brush1 = new PdfSolidBrush(new PdfRGBColor(Color.Blue));
PdfSolidBrush brush2 = new PdfSolidBrush(new PdfRGBColor(Color.Black));

PdfFont font = new PdfFont(PdfFontFamily.TimesRoman, 12f, PdfFontStyle.Regular);

We want to put a label on left, it can use page.Canvas.DrawString to draw a label, it’s base on the font and brush, and use the PointF to control the position

page.Canvas.DrawString("TextBox:", font, brush1, new PointF(10, baseY));

and draw the TextBox element neat the label on the right, define the position with RectangleF, create the textbox with PdfTextBoxField

RectangleF tbxBounds = new RectangleF(baseX, baseY, 150, 15);
PdfTextBoxField textBox = new PdfTextBoxField(page, "textbox");
textBox.Bounds = tbxBounds;
textBox.Text = "Hello World!";
textBox.Font = font;

in the end, don’t forget add the text box element into PdfDocument, because this is a form element, so use below, and also need to update the y coordinate

doc.Form.Fields.Add(textBox);
baseY += 25;

and then, create two checkboxes in second row

//label
page.Canvas.DrawString("CheckBox:", font, brush1, new PointF(10, baseY));

RectangleF checkboxBound1 = new RectangleF(baseX, baseY, 15, 15);
PdfCheckBoxField checkBoxField1 = new PdfCheckBoxField(page, "checkbox1");
checkBoxField1.Bounds = checkboxBound1;
checkBoxField1.Checked = false;
//checkbox value description
page.Canvas.DrawString("Option 1", font, brush2, new PointF(baseX + 20, baseY));

RectangleF checkboxBound2 = new RectangleF(baseX + 70, baseY, 15, 15);
PdfCheckBoxField checkBoxField2 = new PdfCheckBoxField(page, "checkbox2");
checkBoxField2.Bounds = checkboxBound2;
checkBoxField2.Checked = false;
page.Canvas.DrawString("Option 2", font, brush2, new PointF(baseX + 90, baseY));

//add to the doc form
doc.Form.Fields.Add(checkBoxField1);
doc.Form.Fields.Add(checkBoxField2);

//update y coordinate
baseY += 25;

create the radio buttons

page.Canvas.DrawString("RadioButton:", font, brush1, new PointF(10, baseY));
PdfRadioButtonListField radioButtonListField = new PdfRadioButtonListField(page, "radio");
PdfRadioButtonListItem radioItem1 = new PdfRadioButtonListItem("option1");
RectangleF radioBound1 = new RectangleF(baseX, baseY, 15, 15);
radioItem1.Bounds = radioBound1;

page.Canvas.DrawString("Option 1", font, brush2, new PointF(baseX + 20, baseY));
PdfRadioButtonListItem radioItem2 = new PdfRadioButtonListItem("option2");
RectangleF radioBound2 = new RectangleF(baseX + 70, baseY, 15, 15);
radioItem2.Bounds = radioBound2;

page.Canvas.DrawString("Option 2", font, brush2, new PointF(baseX + 90, baseY));
radioButtonListField.Items.Add(radioItem1);
radioButtonListField.Items.Add(radioItem2);
radioButtonListField.SelectedIndex = 0;

doc.Form.Fields.Add(radioButtonListField);
baseY += 25;

create a combobox

page.Canvas.DrawString("ComboBox:", font, brush1, new PointF(10, baseY));
RectangleF cmbBounds = new RectangleF(baseX, baseY, 150, 15);
PdfComboBoxField comboBoxField = new PdfComboBoxField(page, "combobox");
comboBoxField.Bounds = cmbBounds;
comboBoxField.Items.Add(new PdfListFieldItem("Item 1", "item1"));
comboBoxField.Items.Add(new PdfListFieldItem("Item 2", "itme2"));
comboBoxField.Items.Add(new PdfListFieldItem("Item 3", "item3"));
comboBoxField.Items.Add(new PdfListFieldItem("Item 4", "item4"));
comboBoxField.SelectedIndex = 0;
comboBoxField.Font = font;

doc.Form.Fields.Add(comboBoxField);
baseY += 25;

create a signature field

page.Canvas.DrawString("Signature Field:", font, brush1, new PointF(10, baseY));
PdfSignatureField sgnField = new PdfSignatureField(page, "sgnField");
RectangleF sgnBounds = new RectangleF(baseX, baseY, 150, 80);
sgnField.Bounds = sgnBounds;
doc.Form.Fields.Add(sgnField);
baseY += 90;

you also can create a submit button

page.Canvas.DrawString("Button:", font, brush1, new PointF(10, baseY));
RectangleF btnBounds = new RectangleF(baseX, baseY, 50, 15);
PdfButtonField buttonField = new PdfButtonField(page, "button");
buttonField.Bounds = btnBounds;
buttonField.Text = "Submit";
buttonField.Font = font;

//submit to another website
PdfSubmitAction submitAction = new PdfSubmitAction("https://www.e-iceblue.com/getformvalues.php");
submitAction.DataFormat = SubmitDataFormat.Html;
buttonField.Actions.MouseDown = submitAction;
doc.Form.Fields.Add(buttonField);

save the PDF file and return to client side

var pdfFile = "PDF/FillableForms.pdf";
//Save to file
doc.SaveToFile(pdfFile, FileFormat.PDF);

byte[] pdfBytes = System.IO.File.ReadAllBytes(pdfFile);
MemoryStream stream = new MemoryStream(pdfBytes);

//Delete the file after download 
System.IO.File.Delete(pdfFile);
return new FileStreamResult(stream, "application/pdf");

2.3 Create the client for getting the PDF

We will use the MyDemo project, so just create the generate PDF component

ng g c generatepdf

Create a service for handling API method, only one method for call the API in this case

//MyDemo.Client\src\app\services\generate-pdf.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { config } from 'src/assets/config';

/**
 * ^The generate PDF service for call API to generate PDF
 */
@Injectable({
  providedIn: 'root',
})
export class GeneratePDFService {
  constructor(protected http: HttpClient) {
  }

  /**
   * Generate the TP Grade Appendix PDF
   * @returns
   */
  public generatePDF() {
    var url = config.apiUrl + "/generatePDF/generate";
    return this.http.get(url, { responseType: 'blob' });
  }
}

this is a simple page only a button for generate PDF:

<!-- MyDemo.Client\src\app\generatepdf\generatepdf.component.html -->
<ngx-loading
  #ngxLoading
  [show]="loading"
  [config]="config"
  [template]="loadingTemplate"
></ngx-loading>
<ng-template #loadingTemplate>
  <div class="loading-class">
    <h4>Please wait ...</h4>
  </div>
</ng-template>

<swal #swalBox [swalVisible]="isSwalVisible"></swal>

<h1>Generate PDF</h1>

<div class="row">
  <div class="col-sm-12">
    <button
      class="m-r-8 bg-green-700 text-light"
      mat-raised-button
      (click)="generate()"
    >
      Generate PDF
    </button>
  </div>
</div>
<p></p>

I uses the loading service and swal dialog in this page, you can find how to use them in here and here.

and implement the generate even in ts file

public generate() {
    this.loadingService.start();
    this.generatePDFService.generatePDF().subscribe(blob => {

      if (blob) {
        var downloadURL = window.URL.createObjectURL(new Blob([blob], { type: 'blob' }));
        var link = document.createElement('a');
        link.href = downloadURL;
        link.download = "Demo.pdf";
        link.click();
        this.swalOptions.icon = 'success';
        this.swalOptions.title = 'Generate PDF';
        this.swalOptions.html = `The PDF has been downloaded!`;
        this.swalOptions.showConfirmButton = true;
        this.swalOptions.showCancelButton = false;
        this.swalService.show(this.swalOptions);
      }
      else {
        this.swalOptions.icon = 'error';
        this.swalOptions.title = 'Generate Appendix';
        this.swalOptions.html = `Failed to generate PDF, please find the error in server side!`;
        this.swalOptions.showConfirmButton = true;
        this.swalOptions.showCancelButton = false;
        this.swalService.show(this.swalOptions);
      }

      this.loadingService.stop();

    }, error => {
      console.log('%c [ error ]-52', 'font-size:13px; background:pink; color:#bf2c9f;', error);
      this.loadingService.stop();
    });
  }
}

Done!

You can find the full source code below

https://github.com/coderblog-winson/MyDemo

Loading

<p>The post Generate PDF with Free Spire.PDF in .Net Core and download in Angular first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2023/10/generate-pdf-with-free-spire-pdf-in-net-core-and-download-in-angular/feed/ 11
How to create pre-loading before page load in Angular https://www.coderblog.in/2023/10/how-to-create-pre-loading-before-page-load-in-angular/ https://www.coderblog.in/2023/10/how-to-create-pre-loading-before-page-load-in-angular/#comments Tue, 03 Oct 2023 14:22:58 +0000 https://www.coderblog.in/?p=1103 1. Introduction When you access a website, it needs to load many elements or components and this will

<p>The post How to create pre-loading before page load in Angular first appeared on Coder Blog.</p>

]]>
1. Introduction

When you access a website, it needs to load many elements or components and this will take a few times (based on the network and server workload), for giving users a better experience, we can use a pre-loading to show the page is in loading status.

2. How it’s working

I have written an article for creating a loading in Angular with ngx-loading, the loading for waiting for data return from API, but this time, I will show you a different way and create a global loading with css.

The pre-loading will show before every page is loaded to avoid the case that the page is blank before loaded, so we should put the loading in one global page and don’t need to call it manual every time. So we can use any css to customize the loading style.

Ok, let’s start it!

3. Create the loading with CSS

We can put the loading code in src/index.html, that will be great for putting a global loading, and we just need to use the style sheet to custom the loading, put the below after head

<!-- MyDemo.Client\src\index.html -->

<style type="text/css">
  .global-loader {
    display: flex;
    justify-content: center;
    align-items: center;
    position: fixed;
    top: 0;
    left: 0;
    z-index: 1;
    width: 100%;
    height: 100%;
    background-color: #fff;
    opacity: 1;
    transition: opacity .5s ease-in-out;
  }

  .global-loader-fade-in {
    opacity: 0;
  }

  .global-loader-hidden {
    display: none;
  }

  .global-loader h1 {
    font-family: "Helvetica Neue", Helvetica, sans-serif;
    font-weight: normal;
    font-size: 24px;
    letter-spacing: .04rem;
    white-space: pre;
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    background-image:
      repeating-linear-gradient(
        to right,
        #f44336,
        #9c27b0,
        #3f51b5,
        #03a9f4,
        #009688,
        #8bc34a,
        #ffeb3b,
        #ff9800
      );
    background-size: 750% auto;
    background-position: 0 100%;
    animation: gradient 20s infinite;
    animation-fill-mode: forwards;
    animation-timing-function: linear;
  }

  @keyframes gradient {
    0% {
      background-position: 0 0;
    }

    100% {
      background-position: -750% 0;
    }
  }
  </style>

and put a div for loading text, so the body will be like below

<!-- MyDemo.Client\src\index.html -->

<body>
  <app-root></app-root>
  <div id="globalLoader" class="global-loader"><h1>LOADING</h1></div>
</body>

4. Create a service

The service is very common and useful in Angular, it can help to do many things in multiple components, so we also need to create a service in this case:

//MyDemo.Client\src\app\services\preloader.service.ts

import { Inject, Injectable } from '@angular/core';
import { DOCUMENT } from '@angular/common';

@Injectable({
  providedIn: 'root',
})
export class PreloaderService {
  private selector = 'globalLoader';

  constructor(@Inject(DOCUMENT) private document: Document) {}

  private getElement() {
    return this.document.getElementById(this.selector);
  }

  hide() {
    const el = this.getElement();
    if (el) {
      el.addEventListener('transitionend', () => {
        el.className = 'global-loader-hidden';
      });

      if (!el.classList.contains('global-loader-hidden')) {
        el.className += ' global-loader-fade-in';
      }
    }
  }
}

As you can see, we use the service to hide the loading, so it will show by default when the page is loading, and it will be hidden after the page is loaded

5. Use the service

We can hide the loading in app.component.ts :

import { Component, OnInit, AfterViewInit } from '@angular/core';
import { PreloaderService } from './services/preloader.service';


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit, AfterViewInit {
  constructor(private preloader: PreloaderService) {}

  title = 'MyDemo.Client';

  ngOnInit() {
  }

  ngAfterViewInit() {
    this.preloader.hide();
  }
}

The code is very simple, just handling the hide action in ngAfterViewInit event will be ok~

6. Conclusion

This is the simple way to handle page pre-loading issues, and don’t need to use other libs, you can also create any other loading styles with CSS, but this is unable to replace the ngx-loading,, if you want, you can try to use ngx-loading to replace the CSS loading 🙂

Loading

<p>The post How to create pre-loading before page load in Angular first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2023/10/how-to-create-pre-loading-before-page-load-in-angular/feed/ 14
Creating the loading in Angular https://www.coderblog.in/2023/09/creating-the-loading-in-angular/ https://www.coderblog.in/2023/09/creating-the-loading-in-angular/#comments Mon, 11 Sep 2023 01:26:23 +0000 https://www.coderblog.in/?p=1085 1. Introduction Loading is essential in a website, it can let the user know the flow is processing,

<p>The post Creating the loading in Angular first appeared on Coder Blog.</p>

]]>
1. Introduction

Loading is essential in a website, it can let the user know the flow is processing, otherwise user will not know what’s happening. So, I will introduce an excellent loading package to help you solve the loading issue!

2. ngx-loading

ngx-loading is a very nice and customizable loading spinner for Angular, there are flexible config options that let you setup different loading styles. You can install ngx-loading with the below command

npm install --save ngx-loading

And you can find the base usage in github

3. Use in the common layout

With normal usage, you need to add the below code in each html page, so in this article, I will show you how to use the ngx-loading in a common layout.

<ngx-loading
    [show]="loading"
    [config]="{ backdropBorderRadius: '3px' }"
    [template]="customLoadingTemplate"
></ngx-loading>

3.1. Create the service

The concept is we will put the ngx-loading in the common (or parent) page’s HTML layout, so we can’t control it in the child page directly, the solution is we can use a Subject.

The Subject is a special type of Observable that allows values to be multicasted to many Observers. Subjects are like EventEmitters.

So we can create a service to handle the Subject. Suppose we just need to control start and stop to show the loading in needs, so create these two methods in the service, and don’t forget to release the object, the complete code as below:

import { Injectable, OnDestroy } from "@angular/core";
import { Subject } from "rxjs";


@Injectable({
  providedIn: 'root',
})
export class LoadingService implements  OnDestroy {
  // Observable string sources
  private emitChangeSource = new Subject<boolean>();
  // Observable string streams
  changeEmitted$ = this.emitChangeSource.asObservable();

  // Start loading
  start() {
    this.emitChangeSource.next(true);
  }

  // Stop loading
  stop() {
    this.emitChangeSource.next(false);
  }

  ngOnDestroy() {
    // complete and release the subject
    this.emitChangeSource.complete();
  }
}

3.2. Add the ngx-loading to the layout

1) Import the NgxLoadingModule in your root app module

import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
import { CoreModule } from "./core/core.module";
import { NgxLoadingModule } from "ngx-loading";

@NgModule({
  //...
  imports: [
    //...
    NgxLoadingModule.forRoot({}),
  ],
  //...
})
export class AppModule {}

2) Create a loading flag and config in the main layout backend(.ts) file

const PrimaryRed = '#dd0031';
const SecondaryBlue = '#1976d2';

//...

//set the loading flag
loading = false;
//set the loading config
//reference:  https://github.com/Zak-C/ngx-loading#config-options
config = {
  animationType: ngxLoadingAnimationTypes.threeBounce,
  primaryColour: PrimaryRed,
  secondaryColour: SecondaryBlue,
  tertiaryColour: PrimaryRed,
  backdropBorderRadius: '3px',
};

3) Subscribe and update the loading status in the main layout

// Update loading status
this.loadingService.changeEmitted$.subscribe(isLoading => {
  //console.log(isLoading);
  this.loading = isLoading;
});

4) Add the loading HTML code in the main layout frontend(.html) file

<ngx-loading #ngxLoading [show]="loading" [config]="config" [template]="loadingTemplate"></ngx-loading>
  <ng-template #loadingTemplate>
    <div class="loading-class">
      <h4>Please wait ...</h4>
    </div>
</ng-template>

Ok, now, we just need to control the loading flag in other child pages with LoadingService

4. Usage in child pages

We still using this code for the demo. Apply the loading when doing login, so we set the private loadingService: LoadingService in the login component’s constructor, and call this.loadingService.start(); after submitting the login form, and then call this.loadingService.stop(); when success or failed login:

  constructor(//...
    private loadingService: LoadingService) {
  }

  //login
login() {

 //start the loading
 this.loadingService.start();

 this.auth
   .login(this.username.value, this.password.value)
   .pipe(filter(authenticated => authenticated))
   .subscribe(
     () => {
       console.log('after logined and redirect');

       //stop the loading
       this.loadingService.stop();

       this.router.navigateByUrl('/user-management');
     },
     (errorRes: HttpErrorResponse) => {

       //stop the loading
       this.loadingService.stop();

       if(errorRes.status == 401){
        //...
       }
       console.log('Error', errorRes);
     }
   );
}

After clicking the login button, it will show the loading as below

Figure 1

5. Conclusion

If we want to control the parent component in the child, we need to subscribe to the change status and update it in the child, so we can use the Subject and create a service for that! This is the same concept as this article to use the SweetAlert2 in angular.

Loading

<p>The post Creating the loading in Angular first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2023/09/creating-the-loading-in-angular/feed/ 4
Use the SweetAlert2 in Angular Common Layout https://www.coderblog.in/2023/08/use-the-sweetalert2-in-angular-common-layout/ https://www.coderblog.in/2023/08/use-the-sweetalert2-in-angular-common-layout/#comments Wed, 30 Aug 2023 07:31:24 +0000 https://www.coderblog.in/?p=1063 1. Introduction SweetAlert2 is a beautiful, responsive, customizable, accessible replacement for javascript’s popup boxes, and there is also

<p>The post Use the SweetAlert2 in Angular Common Layout first appeared on Coder Blog.</p>

]]>
1. Introduction

SweetAlert2 is a beautiful, responsive, customizable, accessible replacement for javascript’s popup boxes, and there is also a version for Angular, you can find the detail usage in official website. But, the use cases in the official doc are just for use on one page, which means you need to add the same HTML codes on every page you want to use, what if you are using the common layout? The common layout means there is a main layout including Header,Center Body, and Footer, just like this article which I introduced.

In the common layout structure, you don’t need to add the SweetAlert2 HTML code on every page, just adding it to the main layout will be ok! So in this article, I will show you how to do it.

2. Install SweetAlert2 for Angular

Cut of the day I wrote this article, the latest version is 12.2.0, but there is an error for this version, so I suggest installing version 12.1.0

npm install sweetalert2 @sweetalert2/ngx-sweetalert2@12.1.0

import the SweetAlert2 in app.module

import { SweetAlert2Module } from '@sweetalert2/ngx-sweetalert2';

...

@NgModule({
    ...
    imports:[
        ...
        SweetAlert2Module.forRoot(),
        ...
    ]
    ...
})

Every Subject is an Observable and an Observer. You can subscribe to a Subject, and you can call next to feed values as well as error and complete.

3. Create the SweetAlert2 service

If we want to put the SweetAlert2 in a common main layout, then we need to use the observer pattern, we can use a service to subscribe to an observer and then update or call the SweetAlter2 methods.

So, we create the Subject to do that, we need to subscribe 3 items:

1) SweetAlertOptions : All of the sweet alert options, because we need to dynamic to change these options in difference page, so need to subscribe to it
2) Whether Close the SweetAlter2 : This is a boolean object for detecting whether close the SweetAlter2
3) Confirm Item : This is a custom interface for handling the confirm dialog action

The code snippets as below

private swalSource = new Subject<SweetAlertOptions>();
swalEmitted$ = this.swalSource.asObservable();

private swalCloseSource = new Subject<boolean>();
swalCloseEmitted$ = this.swalCloseSource.asObservable();

private swalConfirmSource = new Subject<SwalConfirmItem>();
swalConfirmEmitted$ = this.swalConfirmSource.asObservable();


/**
 * Handle confirm action and data
 */
export interface SwalConfirmItem {
  //the confirm handler function
  fnConfirm: any;
  //The data needs to be passed to the confirm function
  confirmData: any;
  //the current context of the component
  context: any;
}

we can use the next method to execute the subject, so create the show and close method

 // Show swal with options
 // icon: SweetAlertIcon = 'success' | 'error' | 'warning' | 'info' | 'question'
 show(options: SweetAlertOptions) {
   this.swalSource.next(options);
 }

 // Close the swal
 close() {
   this.swalCloseSource.next(true);
 }

 // Set the confirm event
 setConfirm(confirmItem: SwalConfirmItem) {
   this.swalConfirmSource.next(confirmItem);
 }

and we can also create a method for displaying the HttpErrorResponse object

// Handle the HttpErrorResponse and show the error box
showErrors(error: any, options: SweetAlertOptions) {
  console.log('%c [ error ]-37', 'font-size:13px; background:pink; color:#bf2c9f;', error);
  if (error.error && error.error.errors) {
    var errors = '';
    for (var key in error.error.errors) {
      errors += error.error.errors[key] + '<br>';
    }
    options.html = error.error.title + '<br>' + errors;
  } else {
    options.html = error.error;
  }
  options.icon = 'error';
  //show the dialog
  this.swalSource.next(options);
}

maybe the error object will be different from yours (based on the API return values), you can print the error object in the console and find the correct way for looping it.

and don’t forget to destroy the Subject object at the end to void the memory lake

ngOnDestroy() {
  //Complete and release the subject
  this.swalSource.complete();
  this.swalCloseSource.complete();
  this.swalConfirmSource.complete();
}

So, grouping all together will be a SwalService

//MyDemo.Client\src\app\services\swal.service.ts

import { Injectable, OnDestroy } from "@angular/core";
import { Subject } from "rxjs";
import { SweetAlertOptions } from "sweetalert2";

//reference: https://github.com/sweetalert2/ngx-sweetalert2

@Injectable({
  providedIn: 'root',
})
export class SwalService implements OnDestroy {

  private swalSource = new Subject<SweetAlertOptions>();
  swalEmitted$ = this.swalSource.asObservable();

  private swalCloseSource = new Subject<boolean>();
  swalCloseEmitted$ = this.swalCloseSource.asObservable();

  private swalConfirmSource = new Subject<SwalConfirmItem>();
  swalConfirmEmitted$ = this.swalConfirmSource.asObservable();


  // Show swal with options
  // icon: SweetAlertIcon = 'success' | 'error' | 'warning' | 'info' | 'question'
  show(options: SweetAlertOptions) {
    this.swalSource.next(options);
  }

  // Close the swal
  close() {
    this.swalCloseSource.next(true);
  }

  // Handle the HttpErrorResponse and show the error box
  showErrors(error: any, options: SweetAlertOptions) {
    console.log('%c [ error ]-37', 'font-size:13px; background:pink; color:#bf2c9f;', error);

    if (error.error && error.error.errors) {
      var errors = '';
      for (var key in error.error.errors) {
        errors += error.error.errors[key] + '<br>';
      }
      options.html = error.error.title + '<br>' + errors;
    } else {
      options.html = error.error;
    }

    options.icon = 'error';
    this.swalSource.next(options);
  }

  // Set the confirm event
  setConfirm(confirmItem: SwalConfirmItem) {
    this.swalConfirmSource.next(confirmItem);
  }

  ngOnDestroy() {
    // complete and release the subject
    this.swalSource.complete();
    this.swalCloseSource.complete();
    this.swalConfirmSource.complete();
  }
}

/**
 * Handle confirm action and data
 */
export interface SwalConfirmItem {
  //the confirm handler function
  fnConfirm: any;
  //the data need to be pass to the confirm function
  confirmData: any;
  //the current context of the component
  context: any;
}

4. Create the Main layout

Create the main layout component with the below command

ng g c MainLayout --skip-tests

For demonstrate, we just use the simple layout below, add the SweetAlert2 in the main layout, we need to dynamic to control the visible status and the confirm action

<!-- MyDemo.Client\src\app\main-layout\main-layout.component.html -->

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Demo Testing</title>

</head>

<body>
<div>Header content</div>

<div id="wrapper">
  <router-outlet></router-outlet>
</div>

<div>Footer content</div>

<swal #swalBox  [swalVisible]="isSwalVisible" (confirm)="handleConfirm($event, swalConfirmData, swalComponentContext)"></swal>

</body>
</html>

And define the variables in the backend ts file, we use the @ViewChild to show the swal object in frontend

isSwalVisible = false;
swalConfirmData : any;
swalComponentContext: any;

@ViewChild(SwalComponent) swalBox!: SwalComponent;
swalOptions: SweetAlertOptions = {};

It need to subscribe and fire the swal from the child component, so use the swal service in the constructor

constructor(private swalService: SwalService){
  //fire the swal from child component
  this.swalService.swalEmitted$.subscribe(options => {
    if(!this.swalBox){
      //just wait for the time to load the object if can't find it
      setTimeout(()=>{
        if(this.swalBox){
          this.isSwalVisible = true;
          this.swalBox.update(options);
          this.swalBox.fire();
        }
      }, 500);
    }
    else {
      this.isSwalVisible = true;
      this.swalBox.update(options);
      this.swalBox.fire();
    }
  });

  //set the confirm function and execute the login in the child component
  this.swalService.swalConfirmEmitted$.subscribe(confirmItem => {
    this.handleConfirm = confirmItem.fnConfirm == null ? this.resetHandleConfirm : confirmItem.fnConfirm;
    this.swalConfirmData = confirmItem.confirmData;
    this.swalComponentContext = confirmItem.context;
  });

  //handle close the swal event
  this.swalService.swalCloseEmitted$.subscribe(item => {
    this.swalBox.close();
  });
}

Here we need to create the handle confirm method and it will be override by this.swalService.swalConfirmEmitted$, that’s mean will be process the logic in each child component, so just keep empty in here

handleConfirm(item: string, data: any, context: any): void {
   //this will be overwrite by this.swalService.swalConfirmEmitted$
}

Another handle method is resetHandleConfirm, this is only an empty method, but why do we need to use this?

When you call the confirm dialog once time, it will keep the handle method for next time to use, this will cause the handler will be called again and again. To avoid this case, we need to reset the handler method, just setting an empty method will be ok!

//just for reset(remove) the existing handle confirm function
resetHandleConfirm(item: string, data: any, context: any): void {
  //this will be overwrite by this.swalService.swalConfirmEmitted$
}

If the confirm function is null, then use this empty function

this.handleConfirm = confirmItem.fnConfirm == null ? this.resetHandleConfirm : confirmItem.fnConfirm;

In the end, update the app-routing.module.ts, and set all the components under the main layout, so that can be used the main layout structure

const routes: Routes = [
  {
    path: '',
    component: MainLayoutComponent,
    children: [
      { path: 'user-management', component: UserManagementComponent, canActivate: [AuthGuard] },
      { path: 'form-layout', component: FormLayoutTestingComponent, canActivate: [AuthGuard] },
      { path: 'login', component: LoginComponent }
    ]
  }
];

5. Usage

Now, we can try to use the SweetAlter2 in the child component.

5.1. Show the message box

We will show the error if login failed on login page:

1) First, inject the SwalService in the constructor

constructor(private fb: FormBuilder, 
    private router: Router, 
    private auth: AuthService,
    private swalService: SwalService) {
  }

2) Define the SweetAlertOptions and set the default icon to info

swalOptions: SweetAlertOptions = { icon : 'info' };

3) Show the error after login failed

login() {
    console.log('user name:' + this.username.value);
    this.auth
      .login(this.username.value, this.password.value)
      .pipe(filter(authenticated => authenticated))
      .subscribe(
        () => {
          console.log('after logined and redirect');
          this.router.navigateByUrl('/user-management');
        },
        (errorRes: HttpErrorResponse) => {
          if(errorRes.status == 401){

            // set the swal icon to 'error'
            this.swalOptions.icon = 'error';
            // set the message need to be show 
            this.swalOptions.html =  'User name or password is not valid!';
            // show the swal box
            this.swalService.show(this.swalOptions);
          }

          console.log('Error', errorRes);
        }
      );
  }

The result as below:

Figure 1

5.2. Show the confirm dialog

We will show the confirm dialog when try to delete a user.

To support deleting users, we need to add the button for each row in MtxGridColumn. You can find the details for how to use it in this article.

{
  header: 'Operation', field: 'operation' ,
  minWidth: 140,
  pinned: 'left',
  type: 'button',
  buttons: [
    {
      color: 'warn',
      icon: 'delete',
      text: 'Delete',
      tooltip: 'Delete',
      click: record => this.delete(record),
    },
  ],
},

Then you will find the delete icon in each row

Figure 2

If you can’t see the icon, just take a look at whether add it below in your index.html file

<!-- MyDemo.Client\src\index.html -->
<link href="assets/fonts/Material_Icons.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

Ok, we need to implement the this.delete() function, this is the delete confirm function, so we can use the SwalService to show the confirm dialog

public delete(value: any) {
  this.swalOptions.icon = 'question';
  this.swalOptions.title = 'Delete User';
  this.swalOptions.html = `Do you confirm to delete the user ${value.name}?`;
  this.swalOptions.showConfirmButton = true;
  this.swalOptions.showCancelButton = true;

  this.confirmItem.confirmData = value; //pass the data to delete confirm function
  this.confirmItem.fnConfirm = this.deleteConfirm;
  this.swalService.setConfirm(this.confirmItem);
  this.swalService.show(this.swalOptions);
}

As you can see, the this.swalOptions is the SweetAlert2 native setting value, we need to pass the current value (should be user ID in this case) to confirm data, and pass the delete confirm function to override the main layout function, the delete confirm function need to handle the confirm event, that’s mean if user confirmed to delete, will be execute the delete function and call the API to delete user

public deleteConfirm(isConfirm: string, data: any, context: any): void {
  //console.log('delete item', data);
  // call the service to delete user
  context.userManagementService.delete(data.id).subscribe((result: { success: any; message: string | HTMLElement | JQuery | undefined; }) => {
    this.swalOptions.showCancelButton = false; //just need to show the OK button
    context.confirmItem.fnConfirm = null;//reset the confirm function to avoid call this function again and again.
    this.swalService.setConfirm(context.confirmItem);
    if(result.success){
      this.swalOptions.icon = 'success'; //set the icon
      this.swalOptions.html = `The user ${data.name} has been deleted!`;
    } else {
      this.swalOptions.icon = 'error';
      this.swalOptions.html = result.message;
    }
    this.swalService.show(this.swalOptions);
    context.loadData();
  }, (error: { error: any; }) => {
    console.error(error);
    this.swalService.showErrors(error.error, this.swalOptions);
  });
}

Let’s try and see the result:

Figure 3

Figure 4

6. Conclusion

SweetAlert2 is a very nice popup message box, and I also use it in other projects, but it will be a little complex in Angular with main/children component structure, that’s why I wrote this article, and hope this can help more people!

In the end, I will provide the full source code below:

https://github.com/coderblog-winson/MyDemo

Loading

<p>The post Use the SweetAlert2 in Angular Common Layout first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2023/08/use-the-sweetalert2-in-angular-common-layout/feed/ 9
How to Implement JWT in Core API and Angular — Part 2 https://www.coderblog.in/2023/08/how-to-implement-jwt-in-core-api-and-angular-part-2/ https://www.coderblog.in/2023/08/how-to-implement-jwt-in-core-api-and-angular-part-2/#comments Fri, 25 Aug 2023 01:02:40 +0000 https://www.coderblog.in/?p=1048 This is post 2 of 2 in the series “How to Implement JWT” In this series, I will

<p>The post How to Implement JWT in Core API and Angular — Part 2 first appeared on Coder Blog.</p>

]]>

This is post 2 of 2 in the series “How to Implement JWT”

In this series, I will show you how to implement JWT in .Net Core API and communicate with Angular.

  1. How to Implement JWT in Core API and Angular – Part 1
  2. How to Implement JWT in Core API and Angular — Part 2

1. Introduction

We have introduced how to generate the JWT on the server side in previous article, so we will continue to introduce how to handle the JWT in client-side with Angular in this article.

For demonstration, we need to create a simple login page to call the login API, and create another simple page for checking the login token whether is valid, OK, let’s do it!

2. Create the Models

I will base it on the previous demo project for describing. To communicate with the API, we need to create the corresponding models to map the result, so we create a login-request to pass the login data

//MyDemo.Client\src\app\models\login-request.ts

export interface LoginRequest {
  username: string;
  password: string;
}

and create a token model

//MyDemo.Client\src\app\models\token.ts

export interface Token {
  [prop: string]: any;

  //the Jwt token return from API after login successfully
  access_token: string;
  //the current user id
  user_id?: string;
  //should be just handle the 'Bearer' type in this sample
  token_type?: string;
  //How long will be the token expired(e.g. after 30 mins). This is a timestamp format
  expires_in?: number;
  //the actually expire time, so should be the expires_in + current time, e.g. 
  //if expires_in = 30 mins, then exp would be current time + 30 mins
  exp?: number;
}

3. Create the Services

There are 3 services we need to handle:

3.1. Handle Token

First, we need to create a jwt-token to handle the token logic, put these logics into a core folder

//MyDemo.Client\src\app\core\jwt-token.ts

import { Token } from "../models/token";
import { capitalize, currentTimestamp } from "./util";

export class JwtToken {
  constructor(protected attributes: Token) {}

  get access_token(): string {
    return this.attributes.access_token;
  }

  get user_id(): string {
    return this.attributes.user_id ?? '';
  }

  get token_type(): string {
    return this.attributes.token_type ?? 'bearer';
  }

  get exp(): number | void {
    return this.attributes.exp;
  }

  valid(): boolean {
    return this.hasAccessToken() && !this.isExpired();
  }

  getBearerToken(): string {
    return this.access_token
      ? [capitalize(this.token_type), this.access_token].join(' ').trim()
      : '';
  }

  private hasAccessToken(): boolean {
    return !!this.access_token;
  }

  /**
  Check the expired time
  Unit: seconds
  */
  private isExpired(): boolean {
    return this.exp !== undefined && this.exp - currentTimestamp() <= 0;
  }

}

create the util for the common helper methods

//MyDemo.Client\src\app\core\util.ts

/**
 * Capitalize first letter
 * @param text the text wants to be capitalized
 * @returns
 */
export function capitalize(text: string): string {
  return text.substring(0, 1).toUpperCase() + text.substring(1, text.length).toLowerCase();
}

/**
 * Get the current timestamp
 * @returns
 */
export function currentTimestamp(): number {
  return Math.ceil(new Date().getTime() / 1000);
}

/**
 * Filter the Non null object to make sure the object is valid
 * @param obj filter object
 * @returns
 */
export function filterObject<T extends Record<string, unknown>>(obj: T) {
  return Object.fromEntries(
    Object.entries(obj).filter(([, value]) => value !== undefined && value !== null)
  );
}

because we need to save the token into local storage, so create a simple local storage service

//MyDemo.Client\src\app\services\local-storage.service.ts

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class LocalStorageService {
  get(key: string) {
    return JSON.parse(localStorage.getItem(key) || '{}') || {};
  }

  set(key: string, value: any): boolean {
    localStorage.setItem(key, JSON.stringify(value));

    return true;
  }

  has(key: string): boolean {
    return !!localStorage.getItem(key);
  }

  remove(key: string) {
    localStorage.removeItem(key);
  }

  clear() {
    localStorage.clear();
  }
}

in the end, create a token service to put these together

//MyDemo.Client\src\app\services\token.service.ts

import { Injectable, OnDestroy } from '@angular/core';
import { Token } from '../models/token';
import { LocalStorageService } from './local-storage.service';
import { JwtToken } from '../core/jwt-token';
import { currentTimestamp, filterObject } from '../core/util';

@Injectable({
  providedIn: 'root',
})
export class TokenService implements OnDestroy {
  private key = 'MyDemo-token';

  private _token?: JwtToken;

  constructor(private store: LocalStorageService) {}

  private get token(): JwtToken | undefined {
    if (!this._token) {
      this._token = new JwtToken(this.store.get(this.key));
    }

    return this._token;
  }

  set(token?: Token): TokenService {
    this.save(token);

    return this;
  }

  clear(): void {
    this.save();
  }

  valid(): boolean {
    return this.token?.valid() ?? false;
  }

  getUserid(): string {
    return this.token?.user_id ?? '';
  }

  getBearerToken(): string {
    return this.token?.getBearerToken() ?? '';
  }

  ngOnDestroy(): void {
  }

  /**
   * Save the token to local storage
   * @param token token model
   */
  private save(token?: Token): void {

    this._token = undefined;

    if (!token) {
      this.store.remove(this.key);
    } else {
      const value = Object.assign({ access_token: '', token_type: 'Bearer' }, token, {
        exp: token.expires_in ? currentTimestamp() + token.expires_in : null,
      });
      this.store.set(this.key, filterObject(value));
    }
  }
}

3.2. Authentication Service

We will call the API to login the system and get the Jwt token, so we need an auth service to handle the login and logout logic

import { Injectable } from '@angular/core';
import { map, tap } from 'rxjs/operators';
import { HttpClient } from '@angular/common/http';
import { config } from 'src/assets/config';
import { Token } from '../models/token';
import { TokenService } from './token.service';
import { Router } from '@angular/router';

@Injectable({
  providedIn: 'root',
})
export class AuthService {

  constructor(
    private tokenService: TokenService, private router: Router,
    protected http: HttpClient) {}

    /**
     * Call the API to login
     * @param username user name
     * @param password password
     * @returns Jwt token if login successfully
     */
    login(username: string, password: string) {

      var url = config.apiUrl + "/auth/login";
      //call the API to get token after login successfully
      return this.http.post<Token>(url, { username, password }).pipe(
        tap(token => {
          console.log('auth service logined ', token);
          //save the token into local storage
          this.tokenService.set(token);
        }),
        map(() => {
          console.log('auth service logined and map ', this.check());
          //check the token whether is valid
          return this.check();
        })
      );
    }

    /**
     * Clear the token after logout
     */
    logout(){
      this.tokenService.clear();
      this.router.navigateByUrl('/login');
    }

    check() {
      return this.tokenService.valid();
    }
}

4. Create the Login page

After creating the essential models and services, we can create the login page now. Run the below command to create a login page

ng g c Login --skip-tests

Cause we are just for demonstration, so only need to create a simple login layout :

<!-- MyDemo.Client\src\app\login\login.component.html -->

<p>Login</p>
<div class="row">
  <form class="form-field-full" [formGroup]="loginForm">
  <div class="col-sm-12">
      <mat-form-field class="col-sm-3">
        <mat-label>User Name: </mat-label>
        <input matInput formControlName="username" placeholder="User Name">
      </mat-form-field>
  </div>
  <div  class="col-sm-12">
      <mat-form-field class="col-sm-3">
        <mat-label>Password: </mat-label>
        <input matInput formControlName="password" type="password" placeholder="Password">
      </mat-form-field>

  </div>
  <div  class="col-sm-12">
    <button class="m-r-8 bg-green-700 text-light" mat-raised-button  (click)="login()">Login</button>
  </div>
  <div class="col-sm-12">
    <span style="color: red;" *ngIf="errorMsg != ''">{{ errorMsg }}</span>
  </div>
</form>
</div>

we can use the FormBuilder to get the input values and pass them to the login function

//MyDemo.Client\src\app\login\login.component.ts

import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from '../services/auth.service';
import { FormBuilder } from '@angular/forms';
import { filter } from 'rxjs/operators';
import { HttpErrorResponse } from '@angular/common/http';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss']
})
export class LoginComponent {

  constructor(private fb: FormBuilder, 
          private router: Router, 
          private auth: AuthService) {
  }

  public errorMsg: string = '';

  //init the loginForm 
  loginForm = this.fb.nonNullable.group({
    username: '',
    password: '',
  });

  get username() {
    return this.loginForm.get('username')!;
  }

  get password() {
    return this.loginForm.get('password')!;
  }

  login() {
    this.auth
      .login(this.username.value, this.password.value)
      .pipe(filter(authenticated => authenticated))
      .subscribe(
        () => {
          //redirect to user management page if login successfully
          this.router.navigateByUrl('/user-management');
        },
        (errorRes: HttpErrorResponse) => {
          //otherwise then update the error message in the page
          if(errorRes.status == 401){
            this.errorMsg = 'User name or password is not valid!';
          }
            console.log('Error', errorRes);
        }
      );
  }
}

Ok, we can take a look at the result :

1) Input the user name and password and click login

2) It will return the token (can find it in local storage ) and redirect to the user management page

Seems great, right? 🙂

But please hold on, we still have some problems. You will find that if you access the user management page directly and it also can be successful, that means the page didn’t check the login token, that does not make sense.

5. Add the guard for the user pages

We can solve the above issue with guard in Angular. The guard in Angular refers to route guards, which are interfaces that allow you to control navigation and access to routes in your Angular application. route guards allow you to check if a user can activate or deactivate a route, by implementing CanActivate or CanDeactivate interfaces. You can find more details here.

Create the route guards as below:

//MyDemo.Client\src\app\core\auth.guard.ts

import { Injectable } from '@angular/core';
import {
  ActivatedRouteSnapshot,
  CanActivate,
  CanActivateChild,
  RoutesRecognized,
  Router,
  RouterStateSnapshot,
  UrlTree,
} from '@angular/router';
import { filter, pairwise } from 'rxjs/operators';
import { AuthService } from '../services/auth.service';

@Injectable({
  providedIn: 'root',
})
export class AuthGuard implements CanActivate, CanActivateChild {

  previousUrl!: string;
  currentUrl!: string;

  constructor(private auth: AuthService, private router: Router) {
    this.router.events
      .pipe(filter((evt: any) => evt instanceof RoutesRecognized), pairwise())
      .subscribe((events: RoutesRecognized[]) => {
        this.previousUrl = events[0].urlAfterRedirects;
        this.currentUrl = events[1].urlAfterRedirects;
      });
  }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    return this.authenticate();
  }

  canActivateChild(
    childRoute: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): boolean | UrlTree {
    return this.authenticate();
  }

  private authenticate(): boolean | UrlTree {
    //check whether is login successfully
    if (this.auth.check()) {
      return true;
    }
    else{
      this.router.navigateByUrl('/login');
    }
    return false;
  }
}

and use it in app routing, add the canActivate in the routes

//MyDemo.Client\src\app\app-routing.module.ts

const routes: Routes = [
  { path: 'user-management', component: UserManagementComponent, canActivate: [AuthGuard] },
  { path: 'login', component: LoginComponent },
];

after that, logout of the page and try to access the user management page directly, you will find that will be auto redirected to the login page!

6. Create the Interceptor

We are almost done, but still, there is an issue that needs to be solved! Even if we can login successfully and redirect to the user management page, we still can’t get the user data, because there is an authorized checking with the user controller API, so we need to pass the token to the API when we get the user data.

We can append the HTTP header with Authorization when calling the get user API /api/users, but we also need to do that for every API, so this is not a good approach!

The best way that we can use the interceptor.

Interceptors in Angular are services that allow you to intercept and transform HTTP requests and responses between your application and the server. Request interceptors can modify headers, add authentication tokens, log requests, etc.

Create the token-interceptor as below

//MyDemo.Client\src\app\core\token-interceptor.ts

import { Injectable } from '@angular/core';
import {
  HttpErrorResponse,
  HttpEvent,
  HttpHandler,
  HttpInterceptor,
  HttpRequest,
} from '@angular/common/http';
import { Router } from '@angular/router';
import { Observable, throwError } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
import { TokenService } from '../services/token.service';

@Injectable()
export class TokenInterceptor implements HttpInterceptor {
  constructor(
    private tokenService: TokenService,
    private router: Router
  ) {}

  intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    const handler = () => {
      //check the url if login then just redirect to user management page after login     
      if (this.router.url.includes('/login')) {
        this.router.navigateByUrl('/user-management');
      }
    };

    if (this.tokenService.valid()) {
      //if the token is valid, then append to the http header for each request
      return next
        .handle(
          request.clone({            
            headers: request.headers.append('Authorization', this.tokenService.getBearerToken()),
            withCredentials: true,
          })
        )
        .pipe(
          catchError((error: HttpErrorResponse) => {
            //error handler
            if (error.status === 401) {
              this.tokenService.clear();
            }
            return throwError(error);
          }),
          tap(() =>{
            handler();})
        );
    }

    return next.handle(request).pipe(tap(() =>{
        handler();
      }));
  }
}

add a provider in app.module

@NgModule({
  ...
  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true },
  ],
  ...
})

after that, the TokenInterceptor will auto append the token in http header for each request.

login to the page again, you will see all of the user data as well!

7. Conclusion

Jwt is a better way to handle the API authorization, after this article, we learned how to handle the Jwt token in Angular, the flow should be as below:

1) Call API login function and get the token after successfully
2) Save the token in local storage
3) Add the checking for each user’s pages
4) Pass the token to each API request to get data

In the end, don’t forget there are two main points that create the router guard for checking login token and an interceptor for sending token to API.

Loading

<p>The post How to Implement JWT in Core API and Angular — Part 2 first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2023/08/how-to-implement-jwt-in-core-api-and-angular-part-2/feed/ 7
How to Implement JWT in Core API and Angular – Part 1 https://www.coderblog.in/2023/08/how-to-implement-jwt-in-core-api-and-angular-part-1/ https://www.coderblog.in/2023/08/how-to-implement-jwt-in-core-api-and-angular-part-1/#comments Mon, 21 Aug 2023 08:21:03 +0000 https://www.coderblog.in/?p=1015 This is post 1 of 2 in the series “How to Implement JWT” In this series, I will

<p>The post How to Implement JWT in Core API and Angular – Part 1 first appeared on Coder Blog.</p>

]]>

This is post 1 of 2 in the series “How to Implement JWT”

In this series, I will show you how to implement JWT in .Net Core API and communicate with Angular.

  1. How to Implement JWT in Core API and Angular – Part 1
  2. How to Implement JWT in Core API and Angular — Part 2

1. Introduction

JWT means JSON Web Tokens, it usually uses for API authorization with the client. JWT is an open standard that defines a way to transmit information between two parties securely as a JSON object in a compact and verifiable way. It is great for securing REST APIs and authorization. There are some common use cases of JWT include:

1) Authorization – Once the user is logged in, each subsequent request will include the JWT to allow access to routes, services, and resources.

2) Information Exchange – JWT is a good way of securely transmitting information between parties.

3) User information storage – JWT can store user details, preferences, etc in a web/mobile application.

The benefits of using JWT include compact size, url-safe, ability to be encrypted, decentralized, suitable for mobile apps, and more.

And in this post, I will show you how to use JWT for .Net Core API and Angular.

2. The flow for using JWT

Let’s take a look at the flow for how’s the JWT working:

1) Client calls the API for a login request
2) Server(API) side generates JWT after login success
3) Pass the JWT to client-side
4) Client-side needs to save the JWT
5) Client passes the JWT to the API for each request
6) Server-side needs to verify the JWT from the client’s request, if successful then return the data otherwise return auth failed message.

3. Generate the JWT

We can create a JWT service to help to do that! We will use JwtSecurityToken to generate the JWT, this method under System.IdentityModel.Tokens.Jwt namespace, let’s take a look at the signature for this method. There are 4 overwrite methods for this, but we will use the below

public JwtSecurityToken(string issuer = null, string audience = null, 
                IEnumerable<Claim> claims = null, DateTime? notBefore = null, DateTime? 
                expires = null, SigningCredentials signingCredentials = null);

suppose we can let the parameters be null, but for safer, we will use all of them. We can put these parameters into appsettings.json so that can be control

"JwtSettings": {
    "SecurityKey": "MyVeryOwnSecurityKey",
    "Issuer": "MyVeryOwnIssuer",
    "Audience": "https://localhost:4810",
    "ExpirationTimeInMinutes": 1440
  },

and use them to create the token

var jwtOptions = new JwtSecurityToken(
             issuer: _configuration["JwtSettings:Issuer"],
             audience: _configuration["JwtSettings:Audience"],
             claims: GetClaims(user),
             expires: DateTime.Now.AddMinutes(Convert.ToDouble(
                     _configuration["JwtSettings:ExpirationTimeInMinutes"])),
             signingCredentials: GetSigningCredentials());

the complete codes for the JwtService as below

using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using MyDemo.Core.Data.Entity;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace MyDemo.Core.Services;

public class JwtService
{
    private readonly IConfiguration _configuration;
    public JwtService(IConfiguration configuration)
    {
        //inject the configuration for getting the setting values
        _configuration = configuration;
    }
    public JwtSecurityToken GetToken(User user)
    {
        //create the Jwt token from setting values
        var jwtOptions = new JwtSecurityToken(
                        issuer: _configuration["JwtSettings:Issuer"],
                        audience: _configuration["JwtSettings:Audience"],
                        claims: GetClaims(user),
                        expires: DateTime.Now.AddMinutes(Convert.ToDouble(
                                _configuration["JwtSettings:ExpirationTimeInMinutes"])),
                        signingCredentials: GetSigningCredentials());
        return jwtOptions;
    }
    //generate signing credentials
    private SigningCredentials GetSigningCredentials()
    {
        var key = Encoding.UTF8.GetBytes(_configuration["JwtSettings:SecurityKey"]);
        var secret = new SymmetricSecurityKey(key);
        return new SigningCredentials(secret, SecurityAlgorithms.HmacSha256);
    }
    //create the claim base current user name and email
    private List<Claim> GetClaims(User user)
    {
        var claims = new List<Claim> { new Claim(ClaimTypes.Name, user.Email) };
        return claims;
    }
}

4. Create the Login function

We need to create an API login function to return the JWT to the client.

4.1 Create the DTO

Before we start, we need to receive the login request object and a result object for return to the client, so we need to create two DTOs for the login function. In this way, I will simply introduce what’s DTO.

DTO stands for Data Transfer Object. It is a design pattern used to transfer data between different layers of an application, such as between the UI layer and the business logic layer. The main purpose of a DTO is to transfer data, so they don’t contain any behavior except for storage, retrieval, serialization, and deserialization of data.

We can create a DTO folder under the Data folder and create the LoginRequest.cs in this folder

//MyDemo.Core/Data/DTO/LoginRequest.cs

namespace MyDemo.Core.Data.DTO;

public class LoginRequest
{
    public string Username { get; set; }
    public string Password { get; set; } 
}

and the result object for returning the token

//MyDemo.Core/Data/DTO/LoginToken.cs

namespace MyDemo.Core.Data.DTO;

public class LoginToken
{
    /// <summary>
    /// The JWT token if the login attempt is successful, or NULL if not
    /// </summary>
    public string? access_token { get; set; }
    public string? user_id { get; set; }
    public string? token_type { get; set; }
    /// <summary>
    /// expires time in seconds
    /// </summary>
    /// <value></value>
    public int? expires_in { get; set; }
}

4.2. Create the Auth API

Create the AuthController to handle the login function, and we need to inject the below instance first

[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
    private readonly IUserRepository _userRepository;
    private readonly JwtService _jwtService;
    private readonly IConfiguration _configuration;
    private readonly ILogger<AuthController> _logger;

    public AuthController(IUserRepository userRepository,
                        JwtService jwtService,
                        IConfiguration configuration,
                        ILogger<AuthController> logger)
    {
        _userRepository = userRepository;
        _jwtService = jwtService;
        _configuration = configuration;
        _logger = logger;
    }
}

create the login API to receive a LoginRequest

[HttpPost("Login")]
public async Task<IActionResult> Login(LoginRequest loginRequest)
{
    //todo...
}

check the user name and password for login, if successful then generate the token, otherwise, return the null data.

var user = await _userRepository.GetItemWithConditionAsync(
    u => u.Name == loginRequest.Username && u.Password == loginRequest.Password);
if (user == null)
{
    return Unauthorized(new LoginToken()
    {
        access_token = null,
        user_id = null,
        expires_in = 0,
        token_type = "Bearer"
    });
}
//generate a token by user information and appsettings values
var secToken = _jwtService.GetToken(user);
//serializes the token into a JWT format
var jwt = new JwtSecurityTokenHandler().WriteToken(secToken);
//get the expiration times and pass them to the client
var expires_sec = Convert.ToInt32(_configuration["JwtSettings:ExpirationTimeInMinutes"]) * 60;
return Ok(new LoginToken()
{
    access_token = jwt,
    user_id = user.Id.ToString(),
    expires_in = expires_sec,
    token_type = "Bearer"
});

by the way, this is only for a demo, so I didn’t encrypt the password, you should encrypt it in a real project.

and we should use try...catch to handle exceptions in a complete function

[HttpPost("Login")]
public async Task<IActionResult> Login(LoginRequest loginRequest)
{
    try
    {
        var user = await _userRepository.GetItemWithConditionAsync(
            u => u.Name == loginRequest.Username && u.Password == loginRequest.Password);
        if (user == null)
        {
            return Unauthorized(new LoginToken()
            {
                access_token = null,
                user_id = null,
                expires_in = 0,
                token_type = "Bearer"
            });
        }
        //generate a token by user information and appsettings values
        var secToken = _jwtService.GetToken(user);
        //serializes the token into a JWT format
        var jwt = new JwtSecurityTokenHandler().WriteToken(secToken);
        //get the expiration times and pass them to the client
        var expires_sec = Convert.ToInt32(_configuration["JwtSettings:ExpirationTimeInMinutes"]) * 60;
        return Ok(new LoginToken()
        {
            access_token = jwt,
            user_id = user.Id.ToString(),
            expires_in = expires_sec,
            token_type = "Bearer"
        });
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Login Error");
        return StatusCode(500);
    }
}

5. Setup Program.cs

In the end, we also need to add the service to program.cs file. Before that, we need to install the below package to API project from Nuget first

Microsoft.AspNetCore.Authentication.JwtBearer

and add the below codes

// Add Authentication services & middlewares
builder.Services.AddAuthentication(opt =>
{
    opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        RequireExpirationTime = true,
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = builder.Configuration["JwtSettings:Issuer"],
        ValidAudience = builder.Configuration["JwtSettings:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.
                GetBytes(builder.Configuration["JwtSettings:SecurityKey"]))
    };
});

builder.Services.AddScoped<JwtService>();

Ok, let’s take a look at the result in the swagger UI

Figure

Great! We got the token after login successfully!

6. Authorize the User Controller

We have created the login token but we still need to add the authorization to the user controller, otherwise, the token will be useless, just adding the attribute to the user controller will be ok

[Authorize()]
public class UserController : ControllerBase
{
    //...
}

7. Conclusion

We have learned how to generate and handle the Jwt on the server side (API), the main port that uses the JwtSecurityToken to generate a token and serialize it into a JWT format and return it to the client. Because that’s more complex for implementing on the client side, so I split to part 2 to describe how to handle it on the client side in the next article! 🙂

Loading

<p>The post How to Implement JWT in Core API and Angular – Part 1 first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2023/08/how-to-implement-jwt-in-core-api-and-angular-part-1/feed/ 15
Using formly for creating dynamic form in Angular https://www.coderblog.in/2023/08/using-formly-for-creating-dynamic-form-in-angular/ https://www.coderblog.in/2023/08/using-formly-for-creating-dynamic-form-in-angular/#comments Thu, 17 Aug 2023 00:36:39 +0000 https://www.coderblog.in/?p=986 1. Introduction The web form is necessary for a website. And there are 2 types of forms in

<p>The post Using formly for creating dynamic form in Angular first appeared on Coder Blog.</p>

]]>
1. Introduction

The web form is necessary for a website. And there are 2 types of forms in Angular


Reactive forms : Provide direct, explicit access to the underlying form’s object model. Compared to template-driven forms, they are more robust: they’re more scalable, reusable, and testable. If forms are a key part of your application, or you’re already using reactive patterns for building your application, use reactive forms.

Template-driven forms : Rely on directives in the template to create and manipulate the underlying object model. They are useful for adding a simple form to an app, such as an email list signup form. They’re straightforward to add to an app, but they don’t scale as well as reactive forms. If you have very basic form requirements and logic that can be managed solely in the template, template-driven forms could be a good fit.

And today, I will show you a 3 party form existence that can help you to create a nice dynamic form, it’s Formly and built on top of Reactive Forms.

Formly is a powerful extension for generating forms, you can dynamic to pass the data for creating a web form and don’t need to write the HTML in frontend. For example, you can get the JSON from API and create the form, all of the settings can be done in a JSON format! And there is a bunch of themes you can use!

Ok, let’s start it!

2. Base Form Layout

2.1. Install Formly

There are 7 themes that support for Formly,

bootstrap
material
ng-zorro-antd
ionic
primeng
kendo
nativescript

you can install with one of the themes as below command, and I just want to use material 🙂

npm install @angular/forms @ngx-formly/core @ngx-formly/material --save

2.2. Create the base form layout component

Create the form layout component without a test file

ng g c FormLayoutTesting --skip-tests

add the below module in app.moudle.ts

@NgModule({

    imports: [
        ReactiveFormsModule,
        FormlyModule,
        FormlyMaterialModule
    ]
})

set the fields with FormlyFieldConfig, this is a JSON format to config the fields data, and use the model for setting the default value

//form-layout-testing.component.ts

  form = new FormGroup({});
  model = { email: 'email@gmail.com' };

  public fields: FormlyFieldConfig[] = [
    {
      key: 'name',
      type: 'input',
      props: {
        label: 'User Name',
        placeholder: 'Enter user name',
        required: true,
      }
    },
    {
      key: 'email',
      type: 'input',
      props: {
        label: 'Email address',
        placeholder: 'Enter email',
        required: true,
      }
    }
  ];

put the formly-form inside the form tag in HTML code

<!-- form-layout-testing.component.html -->
<form [formGroup]="form">
  <formly-form [form]="form" [fields]="fields" [model]="model"></formly-form>
  <div mat-dialog-actions>
  <button mat-raised-button color="primary">Submit</button>
</div>
</form>

then you will see the below screen after running the project

Figure 1

3. Advance usages

The formly is very powerful, I will show you how it’s working 🙂

3.1. Validations

We can easy to check the email format with regular express in the form with pattern , and set the error message

{
  key: 'email',
  type: 'input',
  props: {
    label: 'Email address',
    placeholder: 'Enter email',
    pattern: /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/,
    required: true,
  },
  validation: {
    messages: {
      pattern: (error: any, field: FormlyFieldConfig) => `"${field?.formControl?.value}" is not a valid email Address`,
    },
  },
}

you can also create a custom validation function. For example, I want to check and make sure the website name must be coder blog, then create a custom validator function up on @Comonent

export function webSiteNameValidator(control: AbstractControl): ValidationErrors {
  if (control.value) {
    var name = control.value; //get the input value for checking
    console.log(control.value);
    if (name != null && name != "coder blog") {
      return { error: { message: 'The website name must be "coder blog"!' } };
    }
  }
  return {};
}

@Component({
  selector: 'app-form-layout-testing',
  templateUrl: './form-layout-testing.component.html',
  styleUrls: ['./form-layout-testing.component.scss']
})

set the validation method

{
  key: 'website_name',
  type: 'input',
  props: {
    label: 'Website Name',
    placeholder: 'Enter website name',
    required: true,
  },
  validators: {
    validation: [webSiteNameValidator]
  }
},

and then must be input code blog in the frontend, otherwise it would not valid :smirk:

Figure 2

4. Preset Fields

This is also a great function that lets you preset the common fields so that you don’t need to input the same settings in each component. For example, the Is Active should be applied for each table, so we can set it to a preset field:

a) Importing the FormlyPresetModule into app.module.ts

import { FormlyPresetModule } from '@ngx-formly/core/preset';

...

@NgModule({
    imports: [
        FormlyPresetModule
    ]
})

b) Set the presets property

@NgModule({
    imports: [
        FormlyPresetModule,
        FormlyModule.forRoot({
        presets: [
            {
            name:'isActive',
            config: {
                key: 'isActive',
                type: 'checkbox',
                templateOptions: {
                label: 'Is Active',
                },
            }
            }
        ],
        })
    ]
})

c) Using it in component

 public fields: FormlyFieldConfig[] = [
    { type: '#isActive'},
    ...
 ];

and you will find it in the form

Figure 3

5. Field Wrappers

As you can see in the above case, the checkbox field has been put in an input field style, this is very strange, we can fix it by changing the field wrapper to formly-wrapper-div (default is formly-wrapper-mat-form-field )

a) Create a wrapper component (put all of the formly-related codes into formly folder)

//app/formly/wrappers.ts

import { Component } from '@angular/core';
import { FieldWrapper } from '@ngx-formly/core';

/**
 * This is just an example of wrapping field into a div
 */
@Component({
  selector: 'formly-wrapper-div',
  template: `
    <div>
      <ng-container #fieldComponent></ng-container>
    </div>
  `,
})
export class FormlyWrapperDivComponent extends FieldWrapper {}

as you can see, we set the selector to formly-wrapper-div and put the field container into a div

b) Add the wrappers in formly config and set the wrapper to the field, below is the preset field

FormlyModule.forRoot({
      presets: [
        {
          name:'isActive',
          config: {
            key: 'isActive',
            type: 'checkbox',
            templateOptions: {
              label: 'Is Active',
            },
            wrappers: ['div']
          }
        },
      ],
      wrappers: [
        {
          name: 'div',
          component: FormlyWrapperDivComponent,
        },
      ],
    })

c) After that, we can take a look at the result, it’s better now 🙂

Figure 4

6. Custom Field

The other powerful feature is that you can create custom fields. For example, we will create an upload file field:

a) Create the custom field component by the below command (you need to create the app/formly/custom-fields folder first)

ng g c formly/custom-fields/formly-field-file --skip-tests

b) extends the component from FieldType<FieldTypeConfig>

//formly/custom-fields/formly-field-file.component.ts

import { Component } from '@angular/core';
import { FieldTypeConfig } from '@ngx-formly/core';
import { FieldType } from '@ngx-formly/material';

@Component({
  selector: 'app-formly-field-file',
  templateUrl: './formly-field-file.component.html',
  styleUrls: ['./formly-field-file.component.scss']
})
export class FormlyFieldFileComponent extends FieldType<FieldTypeConfig> {}

c) add the below html into formly-field-file.component.html, this is the file type html template

<input type="file" accept=".pdf,.zip,.jpg,.png" [formControl]="formControl" [formlyAttributes]="field">

d) create a custom form control directive for handling the on-change event, so that can trigger the validate functions, implement ControlValueAccessor

import { Directive } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';

@Directive({
  // eslint-disable-next-line
  selector: 'input[type=file]',
  host: {
    '(change)': 'onChange($event.target.files)',
    '(blur)': 'onTouched()',
  },
  providers: [
    { provide: NG_VALUE_ACCESSOR, useExisting: FileValueAccessor, multi: true },
  ],
})

export class FileValueAccessor implements ControlValueAccessor {
  value: any;
  onChange = (_: any) => {};
  onTouched = () => {};

  writeValue(_value: any) {}
  registerOnChange(fn: any) {
    this.onChange = fn;
  }
  registerOnTouched(fn: any) {
    this.onTouched = fn;
  }
}

e) add the field types into app.module

FormlyModule.forRoot({
      ...
      types: [
        {
          name: 'file',
          component: FormlyFieldFileComponent,
          wrappers: ['form-field']
        }
      ],
    })

f) create the custom validation to make sure support the correct files

export function uploadFileValidator(control: AbstractControl): ValidationErrors {
  if (control.value) {
    var fileName = control.value[0].name;
    console.log(fileName);
    var fileExt = fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length).trim() || fileName;
    if (fileExt.toUpperCase() != "PDF" && fileExt.toUpperCase() != "ZIP" &&
    fileExt.toUpperCase() != "JPG" && fileExt.toUpperCase() != "PNG") {
      return { attachment: { message: 'Only support .pdf,.zip,.jpg,.png file!' } };
    }
  }
  return {};
}

apply to the field

{
  key: 'attachment',
  type: 'file',
  templateOptions: {
    required: true,
  },
  validators: {
    validation: [uploadFileValidator]
  },
},

Ok, let’s take a look at the result

Figure 5

click the file field, will popup a choose file dialog box, because we already set the input accept=".pdf,.zip,.jpg,.png", so just can see these files in the dialog box

Figure 6

if we choose another file then will be an error

Figure 7

7. Summarize

The form is very important in a website, if you need to use many forms on your site, you should use a better form package or framework to improve your work, so we learned how to use Formly in this post, but this is only a few parts of it, you can go to the official website for more details.

If you have any problems, welcome to discuss them with me :smiley:

Loading

<p>The post Using formly for creating dynamic form in Angular first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2023/08/using-formly-for-creating-dynamic-form-in-angular/feed/ 4
How to integrate HTML template into an Angular project https://www.coderblog.in/2023/08/how-to-integrate-html-template-into-an-angular-project/ https://www.coderblog.in/2023/08/how-to-integrate-html-template-into-an-angular-project/#comments Sun, 06 Aug 2023 00:54:52 +0000 https://www.coderblog.in/?p=933 1. Introduction That will be easy if we just want to simple to integrate the HTML layout into

<p>The post How to integrate HTML template into an Angular project first appeared on Coder Blog.</p>

]]>
1. Introduction

That will be easy if we just want to simple to integrate the HTML layout into Angular, just create the header,center and footer component and put the corresponding HTML to each component will be ok, but, I don’t like it, because I think the well design structure is very important for a project, in my concept, I think there are at least two part layouts for a complete website, one is frontend for presentation and the other is backend for admin management. So we should need to support two difference layouts in this situation.

By the way, for this article, I just want to demo how to create the Angular project’s infrastructure and integrate the HTML layout, so there is no any function for data operation. Ok, let’s start it!

2. About the HTML layout

Of course you can design the HTML layout yourself if you can, but I am nor a designer so I will download the existing open source and free HTML template 🙂 For this demo, I will use the free HTML template for frontend and the other one for backend.

3. Design the project infrastructure

3.1. Create base folders structure

There are several sections need to be use in this project

1) backend (admin) layout
2) authentication user login for admin management
3) frontend layout
4) core section for common logic and services
5) shared section for common components

So, we can create the below project structure

size200

and we need to handle two difference layouts, so don’t need the app.component.html and app.component.scss file, we just need to put the router-outlet in full page, so put it in the app.components.ts will be ok:

@Component({
  selector: 'app-root',
  template: '<router-outlet></router-outlet>',
})

3.2. Create the frontend layout

For well management and maintain, we can use module for each section. So we will create a frontend module under the frontend folder

ng g m frontend

3.2.1. Create each page for frontend

we can take a look the frontend HTML template, there are many sections in this site

Figure 1

after analyzed the HTML template, we know that we should create the below pages (components) base on frontend module, and we don’t need to generate the unit test file, so use --skip-tests parameter

ng g c frontend/header -m=frontend --skip-tests 
ng g c frontend/home -m=frontend --skip-tests 
ng g c frontend/about -m=frontend --skip-tests 
ng g c frontend/contact -m=frontend --skip-tests 
ng g c frontend/portfolio -m=frontend --skip-tests 
ng g c frontend/pricing -m=frontend --skip-tests 
ng g c frontend/services -m=frontend --skip-tests 
ng g c frontend/footer -m=frontend --skip-tests 
ng g c frontend/frontend-layout -m=frontend --skip-tests 

the lase frontend/frontend-layout is the main layout for frontend, just like the app.component.html, because we need to handle two difference layouts, so also need to create two difference main layouts.

3.2.2. Create the frontend routing

We need to create the pages routing for frontend section, for more clear and easy maintain, we create a frontend-routing.module.ts for handle the routing

import { NgModule, Component } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AboutComponent } from './about/about.component';
import { HomeComponent } from './home/home.component';
import { PortfolioComponent } from './portfolio/portfolio.component';
import { PricingComponent } from './pricing/pricing.component';
import { ServicesComponent } from './services/services.component';

const routes: Routes = [
  { path: '', redirectTo: 'home', pathMatch: 'full' },
  { path: 'about', component: AboutComponent },
  { path: 'home', component: HomeComponent },
  { path: 'portfolio', component: PortfolioComponent },
  { path: 'pricing', component: PricingComponent },
  { path: 'services', component: ServicesComponent }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class FrontEndRoutingModule { }

here we set the path for each page (component), so that we can use the URL for access them, and we need to add the routing to frontend.module

@NgModule({
  declarations: [
    HeaderComponent,
    HomeComponent,
    AboutComponent,
    ContactComponent,
    PortfolioComponent,
    PricingComponent,
    ServicesComponent,
    FooterComponent,
    FrontendLayoutComponent
  ],
  imports: [
    CommonModule,
    FrontEndRoutingModule  //add the routing here
  ]
})
export class FrontendModule { }

in the end, we also need to add the frontend module to app.module

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    FrontendModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

3.2.3. Split the HTML to each section

We can find the HTML template structure as below

Figure 2

the <div id="wrapper">...<div> section is the dynamic content area, so we need to replace to router-outlet, and we also need to split the header and footer

so we can copy the HTML template file index.html codes into frontend-layout.component.html and replace the <div id="wrapper">...<div> to below

<div id="wrapper">
  <app-header></app-header>
  <router-outlet></router-outlet>
  <app-footer></app-footer>
</div>

and then copy the HTML codes to corresponding component, for example, copy the <header>...</header> HTML to header.component.html, and <footer>...<footer> to footer.component.html, the middle of content’s HTML between header and footer to home.component.html

Figure 3

the header and footer are static and should be fixed, so we just need to copy the middle content to each component, for example:

The middle of content about.html to about.component.html
The middle of content contact.html to contact.component.html
The middle of content portfolio.html to portfolio.component.html
The middle of content pricing.html to pricing.component.html
The middle of content services.html to services.component.html

After that, we need to copy all of the HTML template’s NoN HTML files (e.g img,css,fonts,js) into /MyDemo.Client/src/assets/frontend folder, and update all of the links in frontend-layout.component.html , so the page should be like below

Figure 4

But please wait! Because angular is not allow to put any script in the component, so if we do so, these js files will be ignored, but we also can’t put these js files into root index.html, because we need to support multiple layouts with their own javascript and css files, so how should we do?

Another approach that we can dynamic load the scripts when component loaded, let’s update frontend-layout.component.ts as below

create a function for dynamic load script

 loadScripts(path: string){
  let node = document.createElement('script');
  node.src = path;//Change to your js file
  node.type = 'text/javascript';
  node.async = true;
  node.charset = 'utf-8';
  document.getElementsByTagName('head')[0].appendChild(node);
 }

implements the OnInit event and call the loadScripts, we need to put all js files in an array and loop them

ngOnInit(): void {
    var scripts = [
      "/assets/frontend/js/jquery.js",
      "/assets/frontend/js/jquery.easing.1.3.js",
      "/assets/frontend/js/bootstrap.min.js",
      "/assets/frontend/js/portfolio/jquery.quicksand.js",
      "/assets/frontend/js/portfolio/setting.js",
      "/assets/frontend/js/jquery.flexslider.js",
      "/assets/frontend/js/animate.js",
      "/assets/frontend/js/custom.js"
    ];

    scripts.forEach(script => {
      this.loadScripts(script);
    });
 }

after that, we can use these js files, but maybe this is not the best way, so please let me know if you have other better approach 🙂

Ok, now we can try to run the project and take a look:

Figure 5

Oh, unfortunately, the page was broken, what happen? After checked it, we missed an important thing that’s update the app routing for frontend module, so update the app-routing.module.ts as below

const routes: Routes = [{
  path: '',
  component: FrontendLayoutComponent,
  children: [{
    path:'', loadChildren: () => import('./frontend/frontend.module').then(m => m.FrontendModule)
  }]
}];

it’s better now

Figure 6

but still there was a problem that the images were missed, and we can see the errors in console

Figure 7

so don’t forget to change the images path in HTML, we update the image’s path in home.component.html as below

...
<img src="assets/frontend/img/slides/1.jpg" alt="" />
...
<img src="assets/frontend/img/slides/2.jpg" alt="" />
...
<img src="assets/frontend/img/slides/3.jpg" alt="" />

For now, we review the page again, and seems good! But we still have something need to do, the Menu!

Yes, we can’t navigate to other pages without the menu. Let’s do it!

4. Add the Menu

We still need to implement the header and footer layout, just copy the <header> codes to header.component.html and don’t forget to change the image’s path, and for the page link, we need to use routerLink property, so the codes will be like

<!-- start header -->
<header>
  <div class="navbar navbar-default navbar-static-top">
    <div class="container">
      <div class="navbar-header">
        <button
          type="button"
          class="navbar-toggle"
          data-toggle="collapse"
          data-target=".navbar-collapse"
        >
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
        </button>
        <a class="navbar-brand" href="/"
          ><img src="/assets/frontend/img/logo.png" alt="logo"
        /></a>
      </div>
      <div class="navbar-collapse collapse">
        <ul class="nav navbar-nav">
          <li routerLinkActive="active"><a routerLink="home">Home</a></li>
          <li><a routerLink="about">About Us</a></li>
          <li><a routerLink="services">Services</a></li>
          <li><a routerLink="portfolio">Portfolio</a></li>
          <li><a routerLink="pricing">Pricing</a></li>
          <li><a routerLink="contact">Contact</a></li>
        </ul>
      </div>
    </div>
  </div>
</header>
<!-- end header -->

also, copy the <footer> codes into footer.component.html, these only the hardcode data, so don’t need to change anything.

After that, just review the page again and will be nice now 🙂

Figure 8

5. Summarize

Well, we tried to integrate a full HTML template to angular, there are many things need to do:

1) Design or download the HTML template
2) Split the layout to multiple components
3) Handle the multiple layouts in angular
4) Setup the routing
5) Dynamic to load the javascript files
6) Change the hyperlink in menu for support angular routing

We just demo how to integrate the frontend layout this time, actually the admin layout should be same, you can try it yourself if you are interested :satisfied:

Loading

<p>The post How to integrate HTML template into an Angular project first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2023/08/how-to-integrate-html-template-into-an-angular-project/feed/ 10
Create Angular Project and Communicate with .Net Core https://www.coderblog.in/2023/08/create-angular-project-and-communicate-with-net-core/ https://www.coderblog.in/2023/08/create-angular-project-and-communicate-with-net-core/#comments Wed, 02 Aug 2023 06:14:23 +0000 https://www.coderblog.in/?p=891 This is post 6 of 8 in the series “Create a website with ASP.NET Core and Angular” In

<p>The post Create Angular Project and Communicate with .Net Core first appeared on Coder Blog.</p>

]]>

This is post 6 of 8 in the series “Create a website with ASP.NET Core and Angular”

In this series, I will show you how to create the web API with .Net core and communicate with Angular in frontend, and I will keep to update it!

  1. How to setup VS Code environment for ASP.NET Core Project
  2. Use code first to connect database in .Net Core API
  3. Use the Repository pattern in .Net core
  4. Create .Net Core Api controller for CRUD with repository
  5. Use the Serilog in .Net Core
  6. Create Angular Project and Communicate with .Net Core
  7. Create Paging Data from .Net Core Api
  8. How to do Unit Testing for the Core Api Project with Repository

We have created the ASP.NET Core Api project, the Api only for backend data handler, now we need to create the client side project to call the Api and present the website layout for end user. Here I will use Angular for client side project.

1. Why Angular

There are some of key advantages of using Angular

Component architecture – Angular promotes building applications using components. This makes the code modular, reusable and maintainable.
Declarative templates – The templates in Angular use HTML to define the UI layout declaratively. This makes it easier to visualize the UI.
Data binding – Angular handles data binding automatically between components, their templates and the application data. This reduces boilerplate code.
TypeScript support – Angular uses TypeScript which brings static typing to JavaScript. This improves code quality and maintainability.
Dependency injection – Angular comes with a built-in dependency injector that makes the code loosely coupled and easy to test.
Testing – Angular was designed with testing in mind. It has great unit and end-to-end testing support.
Cross platform – Angular can be used to build web, mobile and desktop applications from the same codebase.
Community and Google support – Angular is actively developed and maintained by Google. It also has a large community.
A lot of 3rd party extensions – There are a lot of 3rd party extensions support, this can save many of times for help us to create the project.
Features – Angular ships with a lot of built-in features like routing, forms handling, HTTP services, etc. This reduces boilerplate code.
Stable releases – The Angular team focuses on stability and quality with planned major releases every 6 months.

So, Angular simplifies web development by providing a robust framework to build declarative, component-based applications. With its strong features, community support and Google backing, it is a reliable choice for building modern web applications.

2. Create Angular Project

2.1 Install Angular

If you are first time for using Angular, you need to install the CLI, you can execute below command in VS Code terminal as below

npm install -g @angular/cli

Base on our previous Api project, so we goto Demo folder in VS Code terminal and execute below command for creating a new Angular project

ng new MyDemo.Client

After that, the folder structure should be like below

size300

2.2 Running the project

We can try to run the Angular project first, for easy debug, I will suggest using the HMR for running it, the HMR means Hot Module Replacement, which is a development feature that allows you to update the code of your running application without having to reload the entire page. This can greatly speed up the development process by allowing you to see changes in your code immediately, without having to wait for the application to rebuild and reload.

In Angular, you can enable HMR by using the ng serve command with the –hmr flag, like this:

ng serve --hmr

but for convenient to use, we can add the below command to /MyDemo.Client/package.json file under scripts section

"scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test",
    "hmr": "ng serve --hmr --disable-host-check --port 4810"
  },

and you can run the command as below for start the project

npm run hmr

after that, you will find there is an Url in console, and the port that’s what you set in the package.json

Figure 1

and you can press Ctrl + C for terminating.

3. Create an user page

3.1. Create user component

For this sample, we just need to create an user page for demonstrate how to do get data from .Net Core Api. Use the below command for create an user-management component

ng generate component user-management

it will generate 4 files in an user-management folder

user-management.component.html : the html layout for displaying
user-management.component.scss : cause we use the scss for handle the style when we create the project, so it will auto generate the scss style sheet file
user-management.component.spect.ts : the unit test file for testing the component
user-management.component.ts : you can think that’s the code-behind file for handle the business logic with typescript

by the way, if you don’t want to generate so many files (actually the unit test file should be use for a few times), you can also use the command for control it, for example the below command will skip the test and style file

ng generate component user-management  --skip-tests --inline-style 

and for easier to use, you also can use below command for the same result

ng g c user-management  --skip-tests --inline-style 

3.2 Update the routing

Update the /src/app/app-routing.module.ts, add the user management route, so that we can access from the url

const routes: Routes = [
  { path: 'user-management', component: UserManagementComponent },
];

Cause we just want a simple page for demo, so update the app.component.html , remove other html and just keep the style and base structure, put the router outlet to the main content, so we can remove all of the html code after <h2>Resources</h2> and the result should be like below

Figure 2

3.3 About the router-outlet:

The router-outlet directive is used to display the components that correspond to the current route. When a route is activated, the component that corresponds to that route is displayed within the router-outlet tag.

It’s typically included in the root component of your application, such as app.component.html. When a user navigates to a route in your application, Angular will dynamically replace the contents of the router-outlet tag with the component that corresponds to that route.

So in our case, it will show the user management component’s content, after that, you will see the below result

Figure 3

4. Create the page layout

4.1.1 Create the models

As we need to show the user data from Api, so we also need to create a same user model in client side map to Api. Create models folder under app folder, and create the user.ts model. We can use interface for the model in angular:

//MyDemo.Client/src/app/models/user.ts

/**
 * ~ User model for mapping to Api
 */
export interface User {
  id?: number;
  isActive?: boolean;
  isDeleted?: boolean;
  name?: string;
  password?: string;
  email?: string;
  createdAt?: Date;
  updatedAt?: Date;
}

and we also need to handle the Api result, so should be there is a same model for mapping

//MyDemo.Client/src/app/models/api-result.ts

/**
 * ~ The common API result model
 */
export interface ApiResult<T> {
  data?: T;
  success: boolean;
  message: string;
}

in the end, we want to show data in a table list layout (grid), so it’s need to support some paging values, we can create a model like below

//MyDemo.Client/src/app/models/data-list-result.ts

/**
 * ~The common data list result model
 */
export interface DataListResult<T> {
  data: T[];
  pageIndex: number;
  pageSize: number;
  totalCount: number;
  totalPages: number;
  sortColumn: string;
  sortOrder: string;
  filterColumn: string;
  filterQuery: string;
}

the data will be a dynamic object, so we use the generic type.

4.2. Use the extensions (or packages)

One of the advantage it there are a lot of great extensions for Angular, so we can use them to easy to create a pretty layout! For this sample, I will use @ng-matero/extensions for create the grid layout, run below command for install it

npm install @ng-matero/extensions --save

For using ng-matero/extensions, we need to add the module to app.module.ts, and because this module base on angular material, so we also need to add some material modules

//MyDemo.Client/src/app/app.module.ts

//import the base form and material modules
import { FormsModule } from '@angular/forms';

import { MatFormFieldModule } from '@angular/material/form-field';
import { MatButtonModule } from '@angular/material/button';
import { MatInputModule} from '@angular/material/input';
import {TextFieldModule} from '@angular/cdk/text-field';

//import the grid module
import { MtxGridModule } from '@ng-matero/extensions/grid';

and set these modules into imports in @NgModule

@NgModule({
  declarations: [
    AppComponent,
    UserManagementComponent
  ],
  imports: [
    FormsModule,
    MatInputModule,
    MatButtonModule,
    TextFieldModule,
    MatFormFieldModule,
    MtxGridModule,
    BrowserModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

4.3. Create the user management page

4.3.1. Frontend layout

if you used the angular CLI to create the user management component, it will auto add the reference in your app.module, but we still need to set the app routing for user management

//MyDemo.Client/src/app/app-routing.module.ts

const routes: Routes = [
  { path: 'user-management', component: UserManagementComponent },
];

after that, we can use the url http://localhost:4801/user-management to access.

Ok, let’s update the user management page layout. We will use the material theme for the layout and ng-matero/extensions grid for showing data, so the frontend html codes should be like below

<!-- user-management.component.html -->

<h1>User Management </h1>

<div class="row">
  <div class="col-sm-12">
      <!-- use the material form field and button -->
      <mat-form-field class="col-sm-3">
        <input matInput [(ngModel)]="query.q" name="q" (keyup.enter)="search()" placeholder="Filter by user name">
      </mat-form-field>
      <button class="m-r-8 bg-green-700 text-light" mat-raised-button  (click)="search()">Search</button>
      <button class="m-r-8 bg-red-700 text-light" mat-raised-button (click)="reset()">Reset</button>
  </div>
</div>
<p></p>

<!-- use the ng-matero grid -->
<mtx-grid [data]="list"
          [columns]="columns"
          [length]="total"
          [loading]="isLoading"
          [hideRowSelectionCheckbox]="false"
          [multiSelectable]="true"
          [rowSelectable]="true"
          [columnResizable]="true"
          [columnSortable] = "true"
          [pageOnFront]="false"
          [toolbarTitle]="'Show Columns'"
          [showToolbar]="true"
          [rowHover]="true"
          [rowStriped]="true"
          [pageIndex]="query.page"
          [pageSize]="query.per_page"
          [pageSizeOptions]="[5,10,20]"
          (page)="getNextPage($event)"
          (sortChange)="sortingChange($event)"
          [sortOnFront] = "false">
</mtx-grid>

you can goto here for the details how to use the grid.

4.3.2. Backend grid

We also need to update the backend ts file

//user-management.component.ts

//import the grid and material paginator, we need to support paging
import { PageEvent } from '@angular/material/paginator';
import { MtxGridColumn } from '@ng-matero/extensions/grid';

define the variable for paging default values

public defaultFilterColumn: string = "name";
public defaultPageIndex: number = 0;
public defaultPageSize: number = 10;
public defaultSortColumn: string = "name";
public defaultSortOrder: "asc" | "desc" = "asc";
public isLoading = true;
public list: any[] = []; //model listing data
public total = 0;   //the total records
public filterQuery?: string; //filter query for search 

define the grid’s column, it will be apply to frontend html value

public columns: MtxGridColumn[] = [ 
  { header: 'Id', field: 'id', hide: true },
  { header: 'Active', field: 'isActive', sortable: true,
      type: 'tag',
      tag: {
        true: { text: 'Yes', color: 'red-100' },
        false: { text: 'No', color: 'green-100' },
      },
  },
  { header: 'User Name', field: 'name', sortable: true, minWidth: 250 },
  { header: 'Email', field: 'email', minWidth: 250 ,sortable: true },
  { header: 'Created Date', field: 'createdAt' , minWidth: 150, sortable: true},
  { header: 'Updated Date', field: 'updatedAt' , minWidth: 160, sortable: true},
];

you can see the column field name should be same with the user model’s field name.

4.3.3. Create service

For support get data from Api, we can create a service for handle it. Create a services folder and user-management.service.ts as below

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { debounceTime, Observable, tap } from 'rxjs';
import { DataListResult } from '../models/data-list-result';
import { ApiResult } from '../models/api-result';
import { User } from '../models/user';

@Injectable({
  providedIn: 'root',
})
export class UserManagementService {

  constructor(public http: HttpClient) { }

  public getData(
    pageIndex: number,
    pageSize: number,
    sortColumn: string,
    sortOrder: string,
    filterColumn: string | null,
    filterQuery: string | null
  ): Observable<ApiResult<DataListResult<User>>> {
    var url = "http://localhost:5293/api/UserManagement/users";

    //pass the params to api for get data
    var params = new HttpParams()
      .set("pageIndex", pageIndex.toString())
      .set("pageSize", pageSize.toString())
      .set("sortColumn", sortColumn)
      .set("sortOrder", sortOrder);
    if (filterColumn && filterQuery) {
      params = params
        .set("filterColumn", filterColumn)
        .set("filterQuery", filterQuery);
    }

    //call the api and return the result map to DataListResult and User model
    return this.http.get<ApiResult<DataListResult<User>>>(url, { params });
  }
}

As you can see, we need to pass many parameters to Api for support paging, and the Api should return the DataListResult<T> object. But for the previous article, our user Api didn’t support these parameters, don’t worry, we will update the Api later, for now , let’s complete the user-management.component.ts first!

4.3.4. Handle get data and filter methods

For using the user service, we need to pass it into constructor, so update the user-management.component.ts as below

constructor(private userManagementService: UserManagementService,
      private dialog: MtxDialog) { }

and also use the MtxDialog for showing error messages.

Next, we need to define a query object for save the filter and paging data

public query = {
  q: '',
  sort: 'name',
  order: 'desc',
  page: 0,
  per_page: this.defaultPageSize,
};

and then, we can create a getData call the service method

public getData() {
  //get sort column
  var sortColumn = (this.query.sort)
    ? this.query.sort
    : this.defaultSortColumn;
  //get the order method
  var order = (this.query.order)
    ? this.query.order
    : this.defaultSortOrder;
  //get the filter query
  var filterQuery = (this.query.q)
    ? this.query.q
    : null;
  //call service to get data from Api
  this.userManagementService.getData(
    this.query.page,
    this.query.per_page,
    sortColumn,
    order,
    this.defaultFilterColumn,
    filterQuery).subscribe(res => {
        //console.log('res', res);
        if(res.success){
          this.list = res.data ? res.data.data : [] ;
          this.total = res.data? res.data.totalCount : 0;
        } else {
          this.dialog.alert(`Failed to get data!`);
        }
      }
    );
}

after that, we can handle the search and paging methods

public search() {
  this.query.page = 0;
  this.getData();
}
public reset() {
  this.query.q = '';
  this.query.page = 0;
  this.getData();
}
//goto next page event
public getNextPage(e: PageEvent) {
  this.query.page = e.pageIndex;
  this.query.per_page = e.pageSize;
  this.getData();
}
//sorting change event
public sortingChange(e: Sort) {
    //console.log('sortingChange', e);
    this.query.sort = e.active;
    this.query.order = e.direction;
    this.getData();
}
//for load data when first time access the page
public loadData(query?: string) {
  var pageEvent = new PageEvent();
  pageEvent.pageIndex = this.defaultPageIndex;
  pageEvent.pageSize = this.defaultPageSize;
  this.filterQuery = query;
  this.getData();
}
//load data when access the page
public ngOnInit() {
  this.loadData();
}

So far, we finished the client side codes, we can just take a look the page

Figure 4

Ok, I know, there is a last important thing we still need to do, otherwise we can’t get any data from our Api. But because there are also many things need to do, so I will describe the details in the next article 🙂

Loading

<p>The post Create Angular Project and Communicate with .Net Core first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2023/08/create-angular-project-and-communicate-with-net-core/feed/ 9