skip to Main Content

I’m a Jr talking about Angular, I have a question:

Context: A Teacher can own multiple Students.

  1. Requirement # 1.- They want to be able to open a new tab from the Teacher Admin page for each Student. This is working
  2. Requirement # 2.- When you click on Save on one of the Students Admin, the Teachter Admin page should refreshed.

Is requirement 2 technically possible?

Student Api Service

export class StudentApiService extends BaseService {
protected endPoint = "student/student";

private lastStudentUpdate$$: Subject<StudentUpdate> = new Subject<StudentUpdate>();
public lastStudentUpdate$: Observable<StudentUpdate> = this.lastStudentUpdate$$.asObservable();

getStudent(id: number) : Observable<any>
{
    return this.get( {id: id});
}

getStudents(teacherId: number): Observable<any> {
    return this.get({ teacherId: teacherId });
}

createPendingStudent(model: StudentCreate) :  Observable<any>
{
    return this.post(model);
}

updateStudent(model: StudentUpdate) : Observable<any>
{
    this.lastStudentUpdate$$.next(model);
    return this.put(model);
}

}

teacher-wizard.component.ts

@Component({


 selector: 'app-teacher-wizard',
  templateUrl: './teacher-wizard.component.html',
  styleUrls: ['./teacher-wizard.component.scss']
})
export class TeacherWizardComponent implements OnInit, OnDestroy {

  id: number;
  lastStudentUpdate$: Observable<StudentUpdate>;
  
  constructor(
    private route: ActivatedRoute,
    private studentApiService: StudentApiService,
    private router: Router
  ) { }
  
  checkParams(params: ParamMap) {
    const teacherId = 'teacherId';
    if (!params.has(teacherId)) {
      return;
    }
    this.id = +params.get(teacherId);
  }

  ngOnInit() {

    let sub = this.route.parent.url.pipe(
      withLatestFrom(this.route.parent.paramMap)
    ).subscribe(([url, paramMap]) => {
      this.checkParams(paramMap);
      this.getRoutes();
    });
    this.subs.push(sub);
    this.subs.push(routingEventSub);
    this.lastStudentUpdate$ = this.studentApiService.lastStudentUpdate$
    .pipe(startWith(undefined));

    this.lastStudentUpdate$.subscribe((x)=>{
      console.log(x);
    })
  }

    getRoutes() {
    const cr: Routes = this.route.snapshot.routeConfig.children.filter(x => x.path.length > 0);
    this.childRoutes = cr.map(x => (
      {
        exact: !x.children,
        display: x.data.display,
        path: x.path,
        iconClass: x.data?.iconClass,
        tooltip: x.data?.tooltip,
        component: x.component,
        displayOnlyOnRoute: x.data?.displayOnlyOnRoute === true,
        isExclusiveOperation: x.data?.isExclusiveOperation === true,
      }));
  }

  ngOnDestroy(): void {
    this.formService.destroyFormGroup(this.formGroup);
    this.subs.forEach(sub => sub.unsubscribe());
  }
}

student-wizard.component.ts

@Component({
selector: 'student-wizard',
  templateUrl: './student-wizard.component.html',
  styleUrls: ['./student-wizard.component.scss'],
})
export class StudentWizardComponent implements OnInit, OnDestroy {
  id: number;
  student: string;
  private subs: Subscription[] = [];
  formGroup: UntypedFormGroup;
  childRoutes: Array<any>;
  lastStudentUpdate$: Observable<StudentUpdate>;

  constructor(
    private _router: Router,
    private route: ActivatedRoute,
    private studentApiService: StudentApiService,
    private formService: StudentEditFormService
  ) { }

  checkParams(params: ParamMap) {
    const studentId = 'studentId';
    if (!params.has(studentId)) {
      return;
    }
    this.id = +params.get(studentId);
  }

  ngOnInit() {
    this.currentlySaving = false;
    const sub1 = this.route.params.pipe(
      withLatestFrom(this.route.parent.paramMap),
      switchMap(([url, paramMap]) => {
        this.checkParams(paramMap);
        this.getRoutes();
        return this.studentApiService.getStudent(this.id)
      }),
      switchMap(x => {
        this.student = x.student;
        return this.formService.createFormGroup(x)
      })
    ).subscribe(fg => {
      this.formGroup = fg;
      new FormDirtyCheckerService(fg);
    });

    this.subs = [sub1];

    this.lastStudentUpdate$ = this.studentApiService.lastStudentUpdate$
    .pipe(startWith(undefined));

    this.lastStudentUpdate$.subscribe((x)=>{
      console.log(x);
    })
  }
ngOnDestroy(): void {
    this.subs.forEach(s => s.unsubscribe());
    this.formService.destroyFormGroup(this.formGroup);
  }

  getLastSegment() {
    return window.location.pathname.split('/').slice(-1)[0];
  }

  save($event) {
    this.doSave(false);
  }

  saveAndBack($event) {
    this.doSave(true);
  }

  doSave(returnToAdmin) {
    if (!this.formGroup.dirty) {
      //No changes detected;
      return;
    };
    this.currentlySaving = true;
    return this.studentApiService.updateStudent(model);
  }


  hasWarnings(formGroup : UntypedFormGroup) {
    if (!formGroup?.controls) return false;
    const found = Object.keys(formGroup.controls).find(key  => {
      const ctrl : AbstractControlExtended = formGroup.get(key);
      return ctrl.warnings !== undefined && ctrl.warnings !== null;
    });
    return found !== undefined;
  }
 }
}

with this example code I have the next behavior:

  1. I Open Teacher Admin Page, I have listed the Students, I can Open New Tabs for each Students

  2. I added an observable to Student Api Service (lastStudentUpdate)

  3. When I click save in Student Admin Page I do see in Student Admin Page console:

    undefined

    lastUpdatedStudent info

    undefined

  4. I dont see lastUpdatedStudent info in Teacher Admin Page Console, only an undefined due subscription startwith
    undefined

any idea?

I’m using

  • Visual Studio Code
  • NodeJS 20.15.1
  • Angular CLI: 16.2.11
  • Node: 22.7.0 (Unsupported)
  • Package Manager: npm 9.5.0
  • Angular: 16.2.12
  • typescript 4.9.5

2

Answers


  1. you can use localStorage or sessionStorage for this when you click on the save button set the flag in storage to true and listen storage event in first component and after listening set the flag false or remove the item use this code in teacher admin.

    Note- teacher and student admin should be form same domain

    window.addEventListener('storage', event => {   if (event.key === 'refreshFirstTab' && event.newValue === 'true') {
        // Refresh the page
        location.reload();
        // Clear the flag
        localStorage.removeItem('refreshFirstTab');   } });
    
    Login or Signup to reply.
  2. Are you talking about a tabbed UI, or a new browser tab per student?

    If using a tabbed UI I would simply reinitialise the teacher tab component each time it becomes active, therefore making a new API call and then having the latest DB state (calculate last updated by record timestamp).

    If its a browser tab then I would suggest using a behaviour subject subscription available from a service to trigger a new API call from the teacher tab each time a student record is successfully saved, again frontend logic can determine lastUpdated by the records timestamp and display accordingly.

    Both of these approaches assume a fast DB -> backend -> API response time. If your API is not so performant you could restrict the refresh call to get studentById, rather than the complete student list, and only refresh that part of the view.

    
    // student.service.ts
    import { Injectable } from '@angular/core';
    import { BehaviorSubject } from 'rxjs';
    
    @Injectable({
      providedIn: 'root'
    })
    export class StudentService {
      private lastUpdatedStudentSubject = new BehaviorSubject<string | null>(null);
      lastUpdatedStudent$ = this.lastUpdatedStudentSubject.asObservable();
    
      constructor() { }
    
      setSavedStatus(studentId: string): void {
        this.studentDetailsSavedSubject.next(studentId);
      }
    
      resetSavedStatus(): void {
        this.studentDetailsSavedSubject.next(null);
      }
    }
    
    // teacher-admin.component
    
    private studentService = inject(StudentSevice);
    private destroyRef = inject(DestroyRef);
    
    ngOnInit(): void {
      this.studentService.lastUpdatedStudent$
        .pipe(takeUntilDestroyed(this.destroyRef)
        .subscribe((id: string | null) => {
          if(id){
            // do your refresh logic here
          }
        })
    
    // student-tab.component
    
    private studentService = inject(StudentService);
    
    // ...inside your save method on success
    this.studentService.setSavedStatus(student.id);
    
    
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search