0

TL;DR

I want to set up an angular (v15 atm) app that acts as an advent calendar. This would:

  • Allow me to route to a specific year (e.g. <url>/calendar/2021)
  • Allow me to open windows in individual years

I've got this working. However! In my current version, if I open day 1 in 2021, then switch to 2022, day 1 in 2022 will already be open!

I'm trying to figure out how to clear the memory of what I've done on previous versions of a component, which possibly means creating that component fresh each time. Can any of you help?


Background

Lets say I'm making an advent calendar in Angular, and I want it to span multiple years.

I might have an app-routing.module.ts that looks like this:

const routes: Routes = [
  { path: '', redirectTo: '/dashboard', pathMatch: 'full' },
  { path: 'dashboard', component: DashboardComponent },
  { path: 'calendar/:year', component: CalendarComponent }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

...and a calendar.component.ts that looks like this:

@Component({
  selector: 'app-calendar',
  templateUrl: './calendar.component.html',
  styleUrls: ['./calendar.component.scss']
})
export class CalendarComponent {
  title = environment.appName;

  showYear = 0;
  showDays = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25];

  constructor(
    private route: ActivatedRoute) {
    this.route.params.subscribe(params => {
      this.showYear = params['year'];
      this.title = environment.appName + ': ' + params['year'];
    });
  }
}

...with html:

<div class="window-wrap">
  <div class="window-place" *ngFor="let day of showDays">
    <aoc-window [year]="showYear"
                [day]="day">
    </aoc-window>
  </div>
</div>

The window.component.ts looks like the following:

@Component({
  selector: 'aoc-window',
  templateUrl: './window.component.html',
  styleUrls: ['./window.component.scss']
})
export class WindowComponent {
  @Input() year: number = 0;
  @Input() day: number = 0;
  aocResponse?: AOCResponse | undefined;
  isOpen: boolean = false;

  constructor(private aocService: WindowService) {  }

  open() {
    this.isOpen = true;
    this.getAOCResponse();
  }

  getAOCResponse() {
    let callPath = environment.aocPath + '/' + this.year + '/' + this.day

    this.aocService.getAOCAnswer(callPath)
      .subscribe(aocResponse => {
        this.aocResponse = aocResponse;
      }, error => {
        console.error(error);
        this.aocResponse = { answer: 'Failed' }
      });
  }
}

...with html:

<div class="aoc-window">
  <button id="windowClosed" *ngIf="!isOpen" (click)="open()">{{ day }}</button>
  <div *ngIf="isOpen">
    <div class="window-answer" *ngIf="aocResponse">{{ aocResponse.answer }}</div>
    <div class="window-failure" *ngIf="!aocResponse">Loading answer...</div>
  </div>
</div>

Question

So far, everything works. I can navigate to 2021, and get answers for that page. However, if I open doors 1-3 in 2021, then switch to 2022, those doors are still open.

How do I get angular components to "forget" what they know?

There might be two answers:

  • Close all open panels loading 2022 (i.e. forget everything)
  • Understand the difference between 2021 & 2022 panels

I'm fine with either answer!

Thanks!


Additional files

homepage.component.html

<mat-sidenav-container class="app-main-display">
  <mat-sidenav #sidenav mode="side" opened class="app-sidenav">
    <mat-nav-list>

      <a mat-list-item [routerLink]="'/dashboard'"> Dashboard </a>
      <a mat-list-item [routerLink]="'/calendar/2021'"> 2021 </a>
      <a mat-list-item [routerLink]="'/calendar/2022'"> 2022 </a>
      <a mat-list-item [routerLink]="'/calendar/2023'"> 2023 (future) </a>

    </mat-nav-list>
  </mat-sidenav>
  <mat-sidenav-content class="app-router-content">
    <div>
      <router-outlet></router-outlet>
    </div>
  </mat-sidenav-content>
</mat-sidenav-container>

app.module.ts

@NgModule({
  declarations: [
    HomepageComponent,
    WindowComponent,
    DashboardComponent,
    CalendarComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    AppRoutingModule,
    HttpClientModule,
    MaterialModule,
    MatNativeDateModule
  ],
  providers: [],
  bootstrap: [HomepageComponent]
})
export class AppModule { }
Tim L
  • 141
  • 9
  • 1
    I think your problem has to do with reloading the current route: https://stackoverflow.com/questions/40983055/how-to-reload-the-current-route-with-the-angular-2-router – kellermat Dec 27 '22 at 17:26
  • @kellermat - I don't think that *quite* works. That's about reloading the current route; i.e. if I am on calendar/2022, and click it again. My problem is that I don't refresh even when I load different pages (e.g. calendar/2022 to calendar/2021). – Tim L Dec 27 '22 at 19:49
  • I've got to stop for now, and might be away for a couple of days, but I'll check this when I'm back; getting the page to reload *feels* like the right, non-messy way to handle this, but I can't figure it out. – Tim L Dec 27 '22 at 19:51
  • Went down a rabbit hole (https://stackoverflow.com/questions/38971660/angular-2-reload-route-on-param-change); I can tell the router to not reuse routes, but that seems pretty heavy handed. Right option might be to just create onInits that reset the components, but I'll look into it when I'm back! – Tim L Dec 27 '22 at 20:03
  • 1
    I think there is also the possibility to subscribe to specific router-events. These events could be used as a trigger to execute your own refresh-method. – kellermat Dec 27 '22 at 20:08

1 Answers1

1

If i got it right, the problem is that your inputs are remaining with the 2021 values when you change the url to 2022 is that correct? If that´s it, you can change your inputs() to "input() set", that way you can get new values every time it gets updated. So it wourld be something like this:

Change

@Input() year: number = 0;
@Input() day: number = 0;

To

@Input() set year(year: number) {
  this.internalYear = year;
}
private internalYear: number = 0;
@Input() set day(day: number) {
  this.internalDay = day;
}
private internalDay: number = 0;

This way when you update the 'showYear' property with the new year, your input will update its value as well.

Matheus Carvalho
  • 415
  • 7
  • 13
  • Amazing, yes! This does pretty much exactly what I wanted to - "set" allows me to basically reset the page. (I needed to change a couple of other things, such as setting the windows to closed, but that only took a couple of extra lines) Thanks so much! – Tim L Dec 27 '22 at 19:20