Home > Software engineering >  Angular 15 - forgetting existing input when routing between pages using the same component
Angular 15 - forgetting existing input when routing between pages using the same component

Time:12-28

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 >
  <div  *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 >
  <button id="windowClosed" *ngIf="!isOpen" (click)="open()">{{ day }}</button>
  <div *ngIf="isOpen">
    <div  *ngIf="aocResponse">{{ aocResponse.answer }}</div>
    <div  *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 >
  <mat-sidenav #sidenav mode="side" opened >
    <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 >
    <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 { }

CodePudding user response:

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.

  • Related