Skip to content
  • iImagine
  • Register
  • Log In

Web Development School

Learning made easy.

  • Books
    • Beginning Web Development with ASP.Net Core & Client-Side Technologies
      • TOC
      • Part 1
        • Chapter 1: Static HTML – Designing the landing page
      • Part 2
        • Chapter 2: ASP.Net Core – Let’s talk Dynamic
        • Chapter 3: Introduction to ASP.Net Core MVC
          [ASP.Net Core v9]
      • Part 4
        • Chapter 7: Using Server Side & Client Side technologies together
          [ASP.Net Core v7 & Angular 15]
  • Environment Setup
    • Installing Angular
    • Installing Visual Studio 2022
    • Installing SQL Server 2022 Express
    • Installing Postman
    • Installing Git for Windows
  • Blog
  • iImagine WebSolutions
  • Events
  • Learning Videos
  • Toggle search form

Progress Spinner: Final Fix

Before we move on to creating other features in the web application, let’s make one final fix to the Progress Spinner behavior.

Note: The pathway I am using for development in this module is: C:\Development\FredsCars\FullStack\Module32.

We first swapped out the original text “loading…” message for the Angular Material Component Progress Spinner in module 17, Loading: Using a Progress Spinner.

After a while it became apparent that it was acting a little buggy.

The areas in the Vehicles component TypeScript where we used Angular ngIf Directives and the [hide] attribute to show and hide table elements and controls like the pager are sometimes a little buggy. Sometimes it works really well. But sometimes when there is some wait time for the data to come back and initialize the MatTable datasource the Progress Spinner just seems to keep spinning and spinning.

I’m going to just chalk this up to the fact that maybe that ngIf and [hide] don’t play well with the MatTable datasource.

Let’s see if we can fix this up real quick once and for all.

Table Of Contents
  1. Modify the Vehicles TypeScript
  2. Modify the Vehicles HTML
  3. What's Next

Modify the Vehicles TypeScript

Open the vehicles.component.ts file and make the modifications shown below in bold blue font.

FredsCars/src/app/vehicles/vehicles.component.ts


/*** existing code ***/

@Component({
  selector: 'app-vehicles',
  templateUrl: './vehicles.component.html',
  styleUrls: ['./vehicles.component.scss']
})
export class VehiclesComponent implements OnInit {
  // debug JSON var for HTML
  public json = JSON;
  public vehicles!: MatTableDataSource<Vehicle>
  public dataReady: boolean = false;
  
  columnsToDisplay: string[] = ['id', 'vehicleType', 'status', 'year', 'make', 'model', 'color', 'price'];

  /*** existing code ***/
  
  getVehicleData(event: PageEvent) {
    // get rid of third empty click state for MatSort
    if (this.sort && this.sort.direction == "") {
      this.sort.direction = "asc";
    }

    var url = environment.baseUrl + 'api/vehicles';
    var params = new HttpParams()
      .set("pageIndex", event.pageIndex.toString())
      .set("pageSize", event.pageSize.toString())
      .set("sortColumn", (this.sort)
        ? this.sort.active
        : this.defaultSortColumn)
      .set("sortOrder", (this.sort)
        ? this.sort.direction
        : this.defaultSortOrder);

    if (this.filterValue) {
      params = params
        .set("filterColumns", this.defaultFilterColumns)
        .set("filterValue", this.filterValue);
    }

    if (this.search) {
      // search on categories
      let categories: string = "";
      if (!(this.categoryAll.selected)
        && this.categoryAll.categories?.some(c => {
          return c.selected
        }))
      {
        this.categoryAll.categories?.forEach(c => {
          if (c.selected) {
            categories += `${c.name},`
          }
        })
        categories = categories.substr(0, categories.length - 1)

        params = params
          .set("searchValues", categories)
          .set("searchColumns", "vehicleType");
       }

      // search on other columns.
      // -- here...
    }

    this.http.get<any>(url, { params })
      .subscribe(result => {
        this.paginator.length = result.totalCount;
        this.paginator.pageIndex = result.pageIndex;
        this.paginator.pageSize = result.pageSize;
        this.search = result.searched;
        this.vehicles.data = result.data;
        this.dataReady = true;
      }, error => console.error(error));
  }

  /*** existing code ***/

In the code above we made a new TypeScript class variable called dataReady of type boolean initialized to false to act as a flag for when we think the component should stop displaying the Progress Spinner and display the table elements and controls.

Then at the bottom of the getVehicleData() method at the end of the http.get() call we set dataReady to true. We can use this flag in the HTML within the same ngIf directives and [hide] attributes we had before.

Modify the Vehicles HTML

Open the vehicles.component.html file and make the modifications shown below in bold blue font.

FredsCars/src/app/vehicles/vehicles.component.html

<mat-sidenav-container>
  <mat-sidenav mode="side" #sidenav>
    <div style="margin-right: 10px;">
      <p><b>Search Panel</b></p>
      <hr />
      <p><b>Categories</b></p>
      <mat-checkbox color="primary"
                    [checked]="allCategoriesSelected"
                    [indeterminate]="someSearchCategoriesSelected()"
                    (change)="setAllCategoriesSelected($event.checked)">
        {{ categoryAll.name }}
      </mat-checkbox>
      <ul>
        <li *ngFor="let category of categoryAll.categories">
          <mat-checkbox color="accent"
                        [(ngModel)]="category.selected"
                        (ngModelChange)="updateAllCategoriesSelected()">
            {{category.name}}
          </mat-checkbox>
        </li>
      </ul>
      <button mat-raised-button color="primary"
              (click)="loadData(null, true)">
        Search
      </button>
    </div>

    <!-- debug -->
    <!--
    <div style="margin-top: 40px;">
      <b>this.search:</b><br />
      {{ this.search }}<br />
      <b>categoryAll:</b><br />
      {{ categoryAll.selected }}<br />
      <b>Categories:</b>
      <ul>
        <li *ngFor="let category of categoryAll.categories">
          {{category.name}}: {{ category.selected }}
        </li>
      </ul>

      <b>categories (stringified):</b><br />
      {{ categoryAll.categories | json }}
      
    </div>
    -->
    <!-- debug -->
  </mat-sidenav>
  
  <mat-sidenav-content>
    <p *ngIf="!dataReady">
      <mat-spinner style="margin: 0 auto;"></mat-spinner>
    </p>


    <div *ngIf="dataReady"
         style="margin-bottom: 10px;">
      <button mat-fab extended
              color="primary"
              (click)="sidenav.toggle()">
        <mat-icon>
          search
        </mat-icon>
        Advanced Search
      </button>
    </div>

    <mat-form-field [ngClass]="{'hide-results' : !dataReady}">
      <mat-label>Quick Search</mat-label>
      <input matInput #filter (keyup)="loadData(filter.value)"
             placeholder="Filter by Make or Model" />
    </mat-form-field>

    <table mat-table [dataSource]="vehicles"
           matSort
           [matSortActive]="defaultSortColumn"
           [matSortDirection]="defaultSortOrder"
           (matSortChange)="loadData(filter.value, this.search)"
           *ngIf="dataReady">

      <ng-container matColumnDef="vehicleType">
        <th mat-header-cell *matHeaderCellDef mat-sort-header>Category</th>
        <td mat-cell *matCellDef="let item">
          <b>{{ item.vehicleType }}</b>
        </td>
      </ng-container>

      <ng-container matColumnDef="id">
        <th mat-header-cell *matHeaderCellDef mat-sort-header>ID</th>
        <td mat-cell *matCellDef="let item">
          {{ item.id }}
        </td>
      </ng-container>

      <ng-container matColumnDef="status">
        <th mat-header-cell *matHeaderCellDef mat-sort-header>Status</th>
        <td mat-cell *matCellDef="let item">
          {{ item.status }}
        </td>
      </ng-container>

      <ng-container matColumnDef="year">
        <th mat-header-cell *matHeaderCellDef mat-sort-header>Year</th>
        <td mat-cell *matCellDef="let item">
          {{ item.year }}
        </td>
      </ng-container>

      <ng-container matColumnDef="make">
        <th mat-header-cell *matHeaderCellDef mat-sort-header>Make</th>
        <td mat-cell *matCellDef="let item">
          {{ item.make }}
        </td>
      </ng-container>

      <ng-container matColumnDef="model">
        <th mat-header-cell *matHeaderCellDef mat-sort-header>Model</th>
        <td mat-cell *matCellDef="let item">
          {{ item.model }}
        </td>
      </ng-container>

      <ng-container matColumnDef="color">
        <th mat-header-cell *matHeaderCellDef mat-sort-header>Color</th>
        <td mat-cell *matCellDef="let item">
          {{ item.color }}
        </td>
      </ng-container>

      <ng-container matColumnDef="price">
        <th mat-header-cell *matHeaderCellDef mat-sort-header>Price</th>
        <td mat-cell *matCellDef="let item">
          <b>{{ item.price | currency:"USD" }}</b>
        </td>
      </ng-container>

      <!-- No VIN on vehicles page.  We will show this on the details page -->
      <!-- Header Template -->
      <tr mat-header-row *matHeaderRowDef="columnsToDisplay"></tr>
      <!-- Row Template -->
      <tr mat-row *matRowDef="let row; columns: columnsToDisplay"></tr>
    </table>

    <mat-paginator (page)="getVehicleData($event)"
                   [pageSize]="10"
                   [pageSizeOptions]="[3, 5, 10]"
                   showFirstLastButtons
                   [ngClass]="{'hide-results' : !dataReady}">
    </mat-paginator>

  </mat-sidenav-content>
</mat-sidenav-container>

In the HTML above we’ve simply replaced all the ngIf directive and [hide] attribute expressions with dataReady or !dataReady rather then vehicles.datasource.length > 0 or !vehicles.datasource.length > 0.

What’s Next

< Prev
Next >

Leave a ReplyCancel reply

Chapter 1: Static HTML – Designing the landing page.

  • Static HTML – Designing the landing page.
  • Let’s get started!
  • Mock your site with HTML
  • Make CSS easy with Bootstrap
  • Mock your content
  • Introducing JavaScript
  • JavaScript Code Improvements
  • Results Data
  • Images and the HTML Image Element.
  • Revisiting Reusability for CSS and JavaScript
  • Reuse for HTML: PART 1
  • Reuse for HTML: PART 2
  • Details Page – Using a Bootstrap Component
  • Creating Links
  • Chapter One Conclusion

Chapter 2: ASP.Net Core – Let’s talk Dynamic

  • Introduction to ASP.Net Core
  • What is .Net?
  • What is ASP.Net
  • Introduction to Entity Framework Core

Chapter 3: ASP.Net MVC Core – Models, Views, and Controllers [ASP.Net Core v9]

  • Introduction to ASP.Net Core MVC
  • Create the project: ASP.Net Core MVC
  • Explore the ASP.Net Core Empty Web Project Template
  • Configure the Application for MVC
  • Create a Controller: Home Controller
  • Create a View: Index View for the Home Controller
  • Install Bootstrap using Libman
  • Create the Layout template
  • Create the Model
  • Install EF Core & Create the Database
  • Seed the Database: Loading test data
  • DI (Dependency Injection): Display a List of Vehicles
  • Repository Pattern: The Vehicles Repo
  • Unit Test 1: Home Controller Can Use Vehicle Repository
  • Unit Test 2: Vehicle Repository Can Return List
  • Add the ImagePath Migration and Thumbnail images to results
  • Pagination: Create a Custom Tag Helper
  • Sorting
  • Category Filter
  • Partial View: Break out the vehicle results
  • View Component: Create dynamic category buttons
  • Create the Details page
  • Create the Create Page

Chapter 7: Using Server Side & Client Side technologies together. [ASP.Net Core v7 & Angular v15]

  • Intro to Full Stack Development
  • Fred’s Cars – Full Stack Development
  • Prepare the environment
  • Create the Visual Studio Solution
  • Add the ASP.Net Core Web API project
  • Add the Angular Project
  • Wire it up!
  • WeatherForecast: Understanding the basics
  • Vehicles API Controller: Mock Data
  • Vehicles Angular Component: Consuming Data
  • Routing and Navigation
  • Using a Component Library: Angular Material
  • Our first Angular Material Component: MatToolbar
  • Configuring for Saas: CSS with superpowers
  • Create the Header & Footer components
  • Displaying Results with MatTable
  • Loading: Using a Progress Spinner
  • MatTable: Client-Side Paging and Sorting
  • MatSidenav: Create a Search Sidebar
  • MatCheckbox: Category Search UI
  • Adding an image to the welcome page
  • Create the database with Entity Framework Core migrations
  • MatPaginator & PageEvent: Custom Server-Side Paging
  • Unit Testing: Custom Server-Side Paging
  • Repository Pattern: VehicleRepository
  • Unit Test: Paging in the Vehicles controller
  • Server-Side Sorting
  • Unit Tests: Sorting
  • Filter (Quick Search)
  • Unit Tests: Filter feature
  • Advanced Search: Categories
  • Unit Tests: Search by category
  • Progress Spinner: Final Fix

TOC

  • What were WebForms?
  • Enter MVC
    • Understanding MVC
    • Advantages of MVC
  • ASP.Net Core MVC – A total rewrite
  • ASP.Net Core 2 MVC – Here come Razor Pages
    • Understanding Razor Pages
  • ASP.Net Core 3 – Dropping the MVC reference
    • Understanding Blazor
  • Dropping the MVC reference
  • Hello .Net 5!
  • What’s Next? – Here comes .Net 6.

Recent Posts

  • Angular Commands Cheat Sheet
  • Installing Git for Windows
  • Installing Postman
  • Installing SQL Server 2022 Express
  • Installing Visual Studio 2022

Recent Comments

No comments to show.

Archives

  • November 2023
  • October 2023
  • June 2023
  • October 2021

Categories

  • Angular
  • ASP.Net
  • Environment Setup
  • See All
  • SQL Server
  • Visual Studio
  • Web API & Rest Services

WordPress Theme Editor

Copyright © 2025 Web Development School.

Powered by PressBook Blog WordPress theme