Friday, 16 May 2025

Sorting in Angular Material Table

In the last post, we saw how to add paging in an Angular Material table. Now, we will see how to add sorting feature. Angular Material used here is version 19.

First we add MatSortModule in our AppModule:
. . .
import { MatSortModule } from '@angular/material/sort';

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

Then add matSort and mat-sort-header directives to the table on component page. Also we will add a function for matSortChange event:
<table mat-table [dataSource]="dataSource" matSort (matSortChange)="onSortChange($event)">

  <!-- First Name Column -->
  <ng-container matColumnDef="firstName">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> First Name </th>
    <td mat-cell *matCellDef="let student"> {{student.firstName}} </td>
  </ng-container>

  <!-- Last Name Column -->
  <ng-container matColumnDef="lastName">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Last Name </th>
    <td mat-cell *matCellDef="let student"> {{student.lastName}} </td>
  </ng-container>

  . . .

  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="onRowClicked(row)"></tr>
</table>

Finally, modify the component class file:
. . .
import { MatTableDataSource } from '@angular/material/table';
import { MatPaginator } from '@angular/material/paginator';

// import MatSort and Sort directives
import { MatSort, Sort } from '@angular/material/sort';
. . .

export class SearchComponent implements OnInit {
  @ViewChild(MatPaginator) paginator!: MatPaginator;
  
  // add the MatSort component
  @ViewChild(MatSort) sort!: MatSort;
  . . .
  // make sure we use MatTableDataSource type for our data
  dataSource = new MatTableDataSource<Student>([]);
  totalRecords = 0;
  pageSize = 5;  
  
  ngOnInit(): void {
     . . .
     this.dataSource.paginator = this.paginator;       
	 
     // connect sorting to the data source
     this.dataSource.sort = this.sort;
  }
    
  findStudents() {

    // some logic to set or get sort field and direction 
    var sortField = 'studentId';
    var sortDirection = 'asc';
    if (this.sort?.active && this.sort?.direction) {
      sortField = this.sort.active;
      sortDirection = this.sort.direction;
    }

    var pageIndex = 0;
    var pageSize = 5;
    if (this.paginator?.pageIndex && this.paginator?.pageSize) {
      pageIndex = this.paginator.pageIndex;
      pageSize = this.paginator.pageSize;
    }
	
    this.studentService.findStudents(this.model, pageIndex, pageSize, sortField, sortDirection)
      .subscribe({
        next: (response: PaginatedResult<Student>) => {
          this.dataSource.data = response.data;
          this.totalRecords = response.totalRecords;
		  . . .
        }
      });
  }
  
  // respond to emitted events from 'matSortChange' when user is interacting with sort order
  onSortChange(sort: Sort) {
    this.paginator.pageIndex = 0;
    this.findStudents();
  }

  onPageChange(event: any) {
    this.findStudents();
  }
}

Monday, 24 March 2025

Paging in Angular Material Table

We will see how to add paging in Angular Material table. Angular Material used here is version 19.

First we add MatPaginatorModule in our AppModule:
. . .
import { MatPaginatorModule } from '@angular/material/paginator';

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

Then add paginator element on the component page:
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8 alternate-row-table">
   . . .
</table>
<mat-paginator [length]="totalRecords" [pageSize]="pageSize" [pageSizeOptions]="[5, 25, 50]" (page)="onPageChange($event)">
</mat-paginator>

Modify the component class file as well:
// import MatPaginator and MatTableDataSource
import { MatTableDataSource } from '@angular/material/table';
import { MatPaginator } from '@angular/material/paginator';
. . .

export class SearchComponent implements OnInit {
  // add the MatPaginator component
  @ViewChild(MatPaginator) paginator!: MatPaginator;
  . . .
  // make sure we use MatTableDataSource type for our data
  dataSource = new MatTableDataSource<Student>([]);
  totalRecords = 0;
  pageSize = 5;  
  
  ngOnInit(): void {
     . . .
     // assign the paginator when initialising the component
     this.dataSource.paginator = this.paginator;       
  }
    
  findStudents(pageIndex: number, pageSize: number) {

    this.studentService.findStudents(this.model, pageIndex, pageSize)
      .subscribe({
        next: (response: PaginatedResult<Student>) => {
          this.dataSource.data = response.data;
          this.totalRecords = response.totalRecords;
		  . . .
        }
      });
  }

  // respond to paging event
  onPageChange(event: any) {
    this.findStudents(event.pageIndex, event.pageSize);
  }
}

And this is an example of a new type for paginated result:
export class PaginatedResult<T> {
  data!: T[];
  totalRecords!: number;
}

Friday, 14 March 2025

Entity Splitting in Entity Framework Core

We will see how to have an entity with some fields come from different table or view. EF Core used in this writing is version 9.0.0.

We have Student entity:
public partial class Student
{
    // these fields come from Student table
    public long StudentId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string StudentNumber { get; set; }
}
Then we would like to add a few fields that come from a different view:
// to have these fields from ViewStudentDetails
public string Email { get; set; }
public string Address { get; set; }

First, we create another Student partial class so that it is not overwritten whenever we run scaffold command again. We could name the file 'Student_Partial.cs'.
public partial class Student
{
    public string Address { get; set; }
    public string Email { get; set; }
}

Then create another partial database context file (name it 'Partial_StudentContext.cs') if we don't have one already. Then add these configurations in OnModelCreatingPartial method:
public partial class StudentContext
{
   partial void OnModelCreatingPartial(ModelBuilder modelBuilder)
   {
      modelBuilder.Entity<Student>(entity =>
      {
         entity.SplitToTable("ViewStudentDetails", x =>
         {
            x.Property(e => e.Email).HasColumnName("Email");
            x.Property(e => e.Address).HasColumnName("Address");
         });
      });
   }
}

Friday, 7 February 2025

Manually Map Read Only Entity in Entity Framework Core

We will see how to map a Database Table to EF Core without creating a View. EF Core used in this writing is version 9.0.0.

First we create the entity that we want to map:
public class ReadOnlyCampus
{
    public long UniversityCampusId { get; set; }
    public long UniversityId { get; set; }
    public string CampusName { get; set; }
    public bool IsActive { get; set; }
}

Then we create a new partial class for Database Context file. We could save this file as 'Partial_StudentContext.cs' if the main partial class (generated one) is 'StudentContext.cs'. This is so that our manual mappings and configurations will not be overwritten when we run 'Scaffold-DbContext' command next time.
public partial class StudentContext
{
	// add the DbSet for the entity
    public virtual DbSet<ReadOnlyCampus> ReadOnlyCampuses { get; set; }

	// this is extension from the partial method OnModelCreatingPartial(ModelBuilder modelBuilder) declared in the main context file
    partial void OnModelCreatingPartial(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<ReadOnlyCampus>(entity =>
        {
            entity.HasKey(e => e.UniversityCampusId);
            //entity.HasNoKey(); // if this entity does not have a primary key
            
            entity.ToTable("UniversityCampus"); // map to the Database Table 'UniversityCampus'

			// map the properties that we need
            entity.Property(e => e.UniversityCampusId).HasColumnName("UniversityCampusId");
            entity.Property(e => e.UniversityId).HasColumnName("UniversityId");
            entity.Property(e => e.CampusName).HasColumnName("CampusName");
            entity.Property(e => e.IsActive).HasColumnName("IsActive");
        });
    }

    public override int SaveChanges()
    {
		// we make sure that this entity cannnot be changed
        foreach (var entry in ChangeTracker.Entries<ReadOnlyCampus>())
        {
            switch (entry.State)
            {
                case EntityState.Added:
                case EntityState.Modified:
                case EntityState.Deleted:
                    entry.State = EntityState.Unchanged;
                    break;
            }
        }

        return base.SaveChanges();
    }
}

Then we will be able to query this entity like other normal EF entity.

Friday, 13 December 2024

Simple Angular Template Driven Form

Angular provides two types of forms in Angular namely Template Driven Form and Reactive Form. On this post, we will see a simple example of a Template Driven Form. Angular version used here is 19.

First, add FormsModule in app.module.ts:
import { FormsModule } from '@angular/forms';
. . .

@NgModule({
  declarations: [. . .],
  imports: [
    . . .
    FormsModule,
    . . .
  ],
  providers: [. . .  ]
})

Then the HTML elements:
<form #studentForm="ngForm" (ngSubmit)="submitStudentForm()">
  <div>
    <label for="firstName">Student Name:</label>
    <input type="text" id="firstName" [(ngModel)]="model.firstName" name="firstName" placeholder="First Name" />
    <input type="text" id="lastName" [(ngModel)]="model.lastName" name="lastName" placeholder="Last Name" />
  </div>
  <div>
    <label for="email">Email:</label>
    <input type="text" id="email" [(ngModel)]="model.email" name="email" />
  </div>
  <button type="submit">Update</button>
</form>
Note that we need to add a template reference variable, '#theFormName' and set its value to ngForm, in our example is <form #studentForm="ngForm">. In the example we also use [(ngModel)] on input fields for two ways data binding. name attribute for each input is also required by ngForm, otherwise it won't work.

Finally on the .ts file:
export class StudentComponent {

  model: Student = {
    firstName: 'John',
    lastName: 'Doe',
    email: 'john.doe@example.com'
  };

  submitStudentForm(): void {
	. . .
  }
}

Friday, 28 June 2024

Simple Example of Angular Material Stepper

This post will show a simple example of using Angular Material Stepper. Angular Material used is version 15. This example have two reactive forms with simple validations.
<mat-stepper linear #stepper>
  <mat-step [stepControl]="firstFormGroup" label="Fill out your name">
    <form [formGroup]="firstFormGroup">
      <div>
        <mat-form-field>
          <mat-label>Name</mat-label>
          <input matInput formControlName="name" required>
          <mat-error *ngIf="firstFormGroup.controls['name'].hasError('required')">
            Name is required!
          </mat-error>
        </mat-form-field>
      </div>
      <div>
        <button mat-button matStepperNext>Next</button>
      </div>
    </form>
  </mat-step>
  <mat-step [stepControl]="secondFormGroup" label="Fill out your address">
    <form [formGroup]="secondFormGroup">
      <div>
        <mat-form-field>
          <mat-label>Address</mat-label>
          <input matInput formControlName="address" required>
          <mat-error *ngIf="secondFormGroup.controls['address'].hasError('required')">
            Address is required!
          </mat-error>
        </mat-form-field>
      </div>
      <div>
        <button mat-button matStepperPrevious>Back</button>
        <button mat-button matStepperNext>Next</button>
      </div>
    </form>
  </mat-step>
  <mat-step label="Done">
    <p>You are now done.</p>
    <div>
      <button mat-button matStepperPrevious>Back</button>
      <button mat-button (click)="stepper.reset()">Reset</button>
    </div>
  </mat-step>
</mat-stepper>

And the TypeScript file:
export class MyComponent {
  firstFormGroup: FormGroup;
  secondFormGroup: FormGroup;

  constructor(private _formBuilder: FormBuilder) {
    this.firstFormGroup = this._formBuilder.group({
      name: ['', Validators.required]
    });

    this.secondFormGroup = this._formBuilder.group({
      address: ['', Validators.required]
    });
  }
}

Friday, 10 November 2023

HTTP Interceptor Library for JSON Web Token Authorisation

I was looking for an interceptor library for HTTP requests made from my Angular web app to backend services. The idea is to inject every request with Authorization header containing user JWT access token. Found a popular one in GitHub, https://github.com/auth0/angular2-jwt.

First install the library:
npm install @auth0/angular-jwt

Then in app.module.ts file, add the codes below. Note that my app has an authentication class (AuthService) that manages all the authentication functionalities, including retrieving user JWT access token.
. . .
import { JwtModule, JWT_OPTIONS } from '@auth0/angular-jwt';
import { AuthService } from './services/auth.service';

export function jwtOptionsFactory(authService: AuthService) {
  return {
    tokenGetter: () => {
      return authService.getUserAccessToken();
    },
    allowedDomains: ["localhost:5555"]  // my local dev environment
  }
}


@NgModule({
  imports: [
    JwtModule.forRoot({
      jwtOptionsProvider: {
        provide: JWT_OPTIONS,
        useFactory: jwtOptionsFactory,
        deps: [AuthService]
      }
    }),
    . . .
  ],
  . . .
})

export class AppModule { }

Now all the HTTP requests will have 'Authorization: Bearer [TOKEN]' added in the headers.