0

I have an Angular Material data table with CRUD operations as buttons on each row. Unfortunately the buttons aren't working. If I call the toggleActiveCourse or deleteCourse functions in ngOnInit, the data is updated as expected, but if I call them from the button in the component.html file, nothing happens. I can't figure it out. I've been fighting with it for hours. I have written many CRUD tables this way, never had this problem.

courses.component.ts

import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { Course } from 'src/app/core/models/course';
import { CoursesService } from 'src/app/core/services/courses/courses.service';
import { CoursesTableService } from 'src/app/core/tables/courses-table.service';
import { CourseFormComponent } from '../course-form/course-form.component';

@Component({
  selector: 'app-courses',
  templateUrl: './courses.component.html',
  styleUrls: ['./courses.component.css']
})
export class CoursesComponent implements OnInit, OnDestroy {
  tableSub: any;
  showActive: boolean = true;
  displayedColumns = ['name', 'section', 'guaranteedViableCurriculum', 'numberOfStudents', 'actions'];
  dataSource = new MatTableDataSource<Course>();

  @ViewChild(MatPaginator) paginator!: MatPaginator;
  @ViewChild(MatSort) sort!: MatSort;

  constructor(
    private coursesTable: CoursesTableService,
    private coursesService: CoursesService,
    private dialog: MatDialog,
  ) { }

  ngOnInit(): void {
    this.tableSub = this.coursesTable.coursesTable$
      .subscribe(courses => {
        this.dataSource.data = courses;
        this.dataSource.paginator = this.paginator;
        this.dataSource.sort = this.sort;
      })
  }

  ngOnDestroy(): void {
    this.tableSub.unsubscribe();
  }

  openFormDialog(course?: Course) {
    let dialogRef = this.dialog.open(CourseFormComponent, {
      data: {
        course: course,
      }
    })
  }

  toggleActive() {
    this.coursesTable.showActiveToggle(this.showActive); 
  }

  doFilter(filterValue: any) {
    this.dataSource.filter = filterValue.trim().toLowerCase();
  }

  toggleArchiveCourse(course: Partial<Course>) {
    let id = course.id;
    let active = !course.active;
    this.coursesService.updateCourse({id, active}).subscribe();
  }

  deleteCourse(course: Course) {
    let result = confirm("Are you sure you want to delete '" + course.name + " | " + course.section + "'?");
    if (result) this.coursesService.deleteCourse(course).subscribe();
  }
}

courses.service.ts

import { Injectable } from '@angular/core';
import { AngularFireDatabase } from '@angular/fire/compat/database';
import { concatMap, from, map, Observable } from 'rxjs';
import { Course } from '../../models/course';
import { AuthService } from '../auth/auth.service';

@Injectable({
  providedIn: 'root'
})
export class CoursesService {
  // Observable of all courses under a user
  courses$: Observable<Course[]> = this.auth.user$
    .pipe(concatMap(user => {
      return this.db.list(`${user.uid}/courses`,
      ref => ref.orderByChild('section'))
      .snapshotChanges()
      .pipe(map(docArray => {
        return docArray.map(doc => {        
          let c = doc.payload.val() as Course;
          let id = doc.payload.key as string;
          let course = {
            id: id,
            ...c,
          };
          return course;
        })
      }))
    }));

  constructor(
    private db: AngularFireDatabase,
    private auth: AuthService
  ) { }

  // Create new course
  createCourse(course: Course): Observable<any> {
    return this.auth.user$
      .pipe(concatMap(user => {
        let coursesRef = this.db.list(`${user.uid}/courses`);
        return from(coursesRef.push(course));
      }))
  }

  // Update course
  updateCourse(course: Partial<Course>): Observable<any> {
    return this.auth.user$
      .pipe(concatMap(user => {
        let courseRef = this.db.object(`${user.uid}/courses/${course.id}`);
        delete course.id;
        return from(courseRef.update(course));
      }))
  }

  // Delete course
  deleteCourse(course: Course): Observable<any> {
    return this.auth.user$
      .pipe(concatMap(user => {
        let courseRef = this.db.object(`${user.uid}/courses/${course.id}`);
        return from(courseRef.remove());
      }))
  }
}

courses.component.html

<div class="container">
    <h1>Courses</h1>
    
    <div class="table-tools" fxLayout="row" fxLayoutAlign="space-between center">
        <mat-form-field fxFlex="40%">
            <mat-label>Filter Courses</mat-label>
            <input matInput (keyup)="doFilter($any($event.target).value)">
        </mat-form-field>
        <div class="card-buttons">
            <div class="buttons" fxFlex fxLayout="row" fxLayoutGap="10px">
                <button mat-mini-fab color="primary" matTooltip="Add a New Course" (click)="openFormDialog()">
                    <mat-icon>add</mat-icon>
                </button>
                <button mat-mini-fab color="accent" matTooltip="Import Courses from Canvas">
                    <mat-icon>import_export</mat-icon>
                </button>
            </div>
        </div>
    </div>

           
    <table mat-table [dataSource]="dataSource" matSort>

        <ng-container matColumnDef="name">
            <th mat-header-cell *matHeaderCellDef mat-sort-header>
                Course Name
            </th>
            <td mat-cell *matCellDef="let course">
                {{ course.name }}
            </td>
        </ng-container>

        <ng-container matColumnDef="section">
            <th mat-header-cell *matHeaderCellDef mat-sort-header>
                Course Section
            </th>
            <td mat-cell *matCellDef="let course">
                {{ course.section }}
            </td>
        </ng-container>

        <ng-container matColumnDef="guaranteedViableCurriculum">
            <th mat-header-cell *matHeaderCellDef mat-sort-header>
                Guaranteed Viable Curriculum
            </th>
            <td mat-cell *matCellDef="let course">
                {{ course.guaranteedViableCurriculum }}
            </td>
        </ng-container>

        <ng-container matColumnDef="numberOfStudents">
            <th mat-header-cell *matHeaderCellDef mat-sort-header>
                Number of Students
            </th>
            <td mat-cell *matCellDef="let course">
                {{ course.numberOfStudents }}
            </td>
        </ng-container>

        <ng-container matColumnDef="actions">
            <th mat-header-cell *matHeaderCellDef>
                Actions
            </th>
            <td mat-cell *matCellDef="let course">
                <button mat-icon-button color="primary" #tooltip="matTooltip" matTooltip="Edit {{ course.name }} | {{ course.section }}" (click)="openFormDialog(course)">
                    <mat-icon>edit</mat-icon>
                </button>
                <button mat-icon-button color="accent" #tooltip="matTooltip" matTooltip="Archive {{ course.name }} | {{ course.section }}" (click)="toggleArchiveCourse(course)">
                    <mat-icon>archive</mat-icon>
                </button>
                <button mat-icon-button color="warn" #tooltip="matTooltip" matTooltip="Delete {{ course.name }} | {{ course.section }}" (click)="deleteCourse(course)">
                    <mat-icon>delete</mat-icon>
                </button>
            </td>
        </ng-container>

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

    
    <div class="table-footer" fxLayout="row" fxLayoutAlign="space-between center">
        <mat-checkbox class="checkbox" labelPosition="after" [(ngModel)]="showActive" (change)="toggleActive()">Show only active courses</mat-checkbox>
        <mat-paginator [pageSizeOptions]="[5, 10, 15, 20]" aria-label="Select page"></mat-paginator>
    </div>
</div>
wingej0
  • 25
  • 1
  • 7

1 Answers1

0

I had a problem with my user$ observable. Once I fixed that everything worked fine.

wingej0
  • 25
  • 1
  • 7