How to make several routes on one component and not to redraw them unnecessarily, Angular

46 Views Asked by At

There is a ChildComponent that displays a reactive form that can be filled.
After filling the form and saving it, the route should be changed by adding an id to the current route. At the moment when saving we have a complete component is rendered 2 times and there is a blinking screen, but if you add in future request to the server, the blinking will increase. How to get rid of this effect and in case of saving do not render the component 2 times, and change only route or maybe there is a way to simply not render the same component when switching routes.
Also, when we switch to a route with an incorrect id, the component is rendered 2 times, and should just reset the form and the route id disappears.

Root Routing

import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {AppComponent} from "./app.component";

const routes: Routes = [
  {
    path: '',
    component: AppComponent,
    loadChildren: () => import('./root/parent.module').then(m => m.ParentModule)
  },
  {
    path: 'child',
    loadChildren: () => import('./child/child.module').then(m => m.ChildModule)
  }
];

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

Child Routing

import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {RouterModule, Routes} from "@angular/router";
import {ChildComponent} from "./child.component";

const routes: Routes = [
  {
    path: '',
    component: ChildComponent
  },
  {
    path: ':id',
    component: ChildComponent
  }
]

@NgModule({
  declarations: [],
  imports: [
    CommonModule,
    RouterModule.forChild(routes)
  ],
  exports: [RouterModule],
})
export class ChildRoutingModule {
}

State

export interface Card {
  id: string;
  name: string;
  description: string
}

export const STATE: Card[] = [
  {
    id: '1',
    name: 'First',
    description: 'First'
  }, {
    id: '2',
    name: 'Second',
    description: 'Second'
  }
]

html ChildComponent

<a routerLink="/">to Root</a>
<div *ngFor="let item of STATE">
  <a [routerLink]="'/child/' + item.id">to {{ item.name }}</a>
</div>
<form
  (ngSubmit)="onSubmit()"
  [formGroup]="formGroup"
  style="display: flex; flex-direction: column; width: 200px"
>
  <label>Name:
    <input style="display: inline-block; width: 200px; padding: 0; margin: 0; box-sizing: border-box"
           formControlName="name">
  </label>

  <label>Description:
    <textarea style="display: inline-block; width: 200px; padding: 0; margin: 0; box-sizing: border-box; resize: none"
              formControlName="description"></textarea>
  </label>

  <button style="width: 100%" type="submit" [disabled]="formGroup.invalid">Submit</button>
</form>

component ChildComponent

import {Component, OnDestroy, OnInit} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from "@angular/forms";
import {ActivatedRoute, Router} from "@angular/router";
import {STATE} from "../state";

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrl: './child.component.css'
})
export class ChildComponent implements OnInit, OnDestroy {
  readonly STATE = STATE;
  formGroup: FormGroup = this.formBuilder.group({
    name: ['', Validators.required],
    description: ['', Validators.required]
  });

  constructor(
    private formBuilder: FormBuilder,
    private activatedRoute: ActivatedRoute,
    private router: Router) {
  }

  ngOnInit() {
    console.log('ngOnInit')
    const id = this.activatedRoute.snapshot.params['id']
    if (id) {
      const item = STATE.find(el => el.id === id)
      if (item) this.formGroup.patchValue(item)
      else this.router.navigate(['..'], {relativeTo: this.activatedRoute})
    } else {
    }
  }

  ngOnDestroy() {
    console.log('ngOnDestroy')
  }

  onSubmit() {
    const id = this.activatedRoute.snapshot.params['id']
    if (id) {
      const itemIndex = STATE.findIndex(el => el.id === id)
      if (itemIndex) {
        STATE[itemIndex].id = id.toString()
        STATE[itemIndex].name = this.formGroup.value.name
        STATE[itemIndex].description = this.formGroup.value.description
      }
    } else {
      const random = Math.floor(Math.random() * 1000000)
      STATE.push({
        id: random.toString(),
        name: this.formGroup.value.name,
        description: this.formGroup.value.description
      })

      this.router.navigate(['./', random], {relativeTo: this.activatedRoute})
    }
  }
}
1

There are 1 best solutions below

0
Ricudo On

The issue you have relates to routes reuse strategy. To solve it you have to declare your own rules for routes reusing:

import { RouteReuseStrategy } from '@angular/router';
export class CustomRouteReuseStrategy implements RouteReuseStrategy {
...
}

then provide it in your app.module providers:

providers: [
    ...
    { provide: RouteReuseStrategy, useClass: CustomRouteReuseStrategy }
]