Skip to content

onRouterReuse implementation #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions src/app/app-route-reuse-strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ export class AppRouteReuseStrategy implements RouteReuseStrategy {
if (DEBUG) {
console.log('%cDEBUG[retrieve]', 'background:green', {path});
}

// @ts-ignore
handle.componentRef.instance.onRouterReuse()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handle type isn't implemented correctly so I'm wrapping it in @ts-ignore.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. I'm curious as to how you found this private API (via debugger?)

I can only access the public API with my IDE (IntelliJ points me to d.ts files)

If you have tips to get access the underlying Angular code I'm all ears :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just familiar with bind-query-param repo... You can see it with debuggers or just look into its source code...

Of course that this is just a POC... You need to open a detailed issue for bind-query-param repo explaining the issue and asking for making it public (Calling it directly won't work for controls that uses a special URL parser...)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got ya, thanks :)


return handle;
}

Expand Down
2 changes: 1 addition & 1 deletion src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
</li>
</ul>

<router-outlet appRouteReuseLifecycle></router-outlet>
<router-outlet></router-outlet>
5 changes: 1 addition & 4 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,22 @@ import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import {MyFormComponent} from './components/my-form.component';
import {MyListComponent} from './components/my-list.component';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {ReactiveFormsModule} from '@angular/forms';
import {RouteReuseStrategy} from '@angular/router';
import {AppRouteReuseStrategy} from './app-route-reuse-strategy';
import {SpinnerComponent} from './components/spinner.component';
import {RouteReuseLifeCycleDirective} from './directives/route-reuse-life-cycle.directive';

@NgModule({
declarations: [
AppComponent,
MyFormComponent,
MyListComponent,
SpinnerComponent,
RouteReuseLifeCycleDirective,
],
imports: [
BrowserModule,
AppRoutingModule,
ReactiveFormsModule,
FormsModule,
],
providers: [
{provide: RouteReuseStrategy, useClass: AppRouteReuseStrategy},
Expand Down
41 changes: 16 additions & 25 deletions src/app/components/my-form.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,21 @@ import {Component, OnDestroy} from '@angular/core';
import {FormControl, FormGroup} from '@angular/forms';
import {BindQueryParamsFactory} from '@ngneat/bind-query-params';
import {of} from 'rxjs';
import {delay, tap} from 'rxjs/operators';
import {OnAttach} from '../directives/route-reuse-life-cycle.directive';
import {delay, filter, take, tap} from 'rxjs/operators';
import {OnRouterReuse} from "../onRouterReuse";
import {NavigationEnd, Router} from "@angular/router";

@Component({
template: `

<h2>My form!</h2>
<label for="keep-data">
Keep form data
<input style="display: inline"
[(ngModel)]="keepFormData"
id="keep-data" type="checkbox">
</label>

<h3>Loaded at: {{loadedAt}}</h3>
<h4>Slow request:
{{ slowRequest$ | async }}
<app-spinner *ngIf="showSpinner"></app-spinner>
</h4>

<p style="font-family: 'Courier New',sans-serif">
{{form.value | json}} keep={{ keepFormData }}
{{form.value | json}}
</p>

<form [formGroup]="form" autocomplete="off">
Expand Down Expand Up @@ -67,9 +60,8 @@ import {OnAttach} from '../directives/route-reuse-life-cycle.directive';
`
]
})
export class MyFormComponent implements OnDestroy, OnAttach {
export class MyFormComponent implements OnDestroy, OnRouterReuse {
showSpinner = true;
keepFormData = true;

slowRequest$ = of('A SERVER MESSAGE').pipe(
delay(5000),
Expand All @@ -87,23 +79,22 @@ export class MyFormComponent implements OnDestroy, OnAttach {
]).connect(this.form);
loadedAt: number;

constructor(private factory: BindQueryParamsFactory) {
constructor(private factory: BindQueryParamsFactory,
private router: Router) {
this.loadedAt = Date.now();
}

ngOnDestroy(): void {
this.manager.destroy();
onRouterReuse(): void {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a callback function named onRouterReuse that it been called every time the route is reused.
We also use router.events because we can't change the urlParams before the route completes, Otherwise it mess the route change.

Then I call a private function updateQueryParams with the form value.
We might need ti change this method to be public in bind-query-param repo.

this.router.events.pipe(filter((e): e is NavigationEnd => e instanceof NavigationEnd),
take(1))
.subscribe(() => {
// @ts-ignore
this.manager.updateQueryParams(this.form.value);
})
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice I like it! It'd be great if a default implementation could be provided somehow (to reduce the boilerplate later)

 onRouterReuse(): void {
    this.router.events.pipe(filter((e): e is NavigationEnd => e instanceof NavigationEnd),
      take(1))
      .subscribe(() => {
        // yield here, code specific to a component
      })
  }

I'm not sure how to go about this. The closest I got was this ugly thing:

// OnRouterReuseImpl.ts
export class OnRouterReuseImpl {
  fn?: () => void;
  constructor(private router: Router) {
  }

  onRouterReuse(): void {
    this.router.events.pipe(filter((e): e is NavigationEnd => e instanceof NavigationEnd),
      take(1))
      .subscribe(() => {
        if (this.fn) {
          this.fn();
        }
      })
  }

}

// my-form.component.ts
private routeReuse: OnRouterReuseImpl;
constructor(private factory: BindQueryParamsFactory,
            private router: Router) {
    this.routeReuse = new OnRouterReuseImpl(router);
    this.routeReuse.fn = () => {
      // @ts-ignore
      this.manager.updateQueryParams(this.form.value);
    }
  }

  onRouterReuse(): void {
    this.routeReuse.onRouterReuse();
  }

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, I didn't manage to add Router to app-route-reuse-strategy.ts because of circular dependencies...
I think that this whole function should be inside bind-query-param repo so you won't repeat yourself, Or just keep it as you did with some kind of common service that handles it.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

I’m finding using a custom route reuse strategy quite tricky in practice.

And having two events (attached/detached) seems to be quite useful for the app I’m working on.

Thanks for your input though, I’ll have to think it some more :)


onAttach(): void {
console.log('ON ATTACH!!', this.form.value);
// this.manager.syncAllDefs(); // does not work

if (this.keepFormData) {
this.form.setValue(this.form.value); // forces form<>URL sync
} else {
this.form.reset(); // Or reset the form
}
ngOnDestroy(): void {
this.manager.destroy();
}

refreshNav() {
Expand Down
55 changes: 0 additions & 55 deletions src/app/directives/route-reuse-life-cycle.directive.ts

This file was deleted.

3 changes: 3 additions & 0 deletions src/app/onRouterReuse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export declare interface OnRouterReuse {
onRouterReuse: () => void;
}