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.HasNoKey(); // indicate that 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.