11import { DOCUMENT } from '@angular/common' ;
22import { Inject , Injectable } from '@angular/core' ;
33import { NavigationEnd , NavigationStart , Router } from '@angular/router' ;
4- import { BehaviorSubject , EMPTY , forkJoin , Observable } from 'rxjs' ;
5- import { filter , first , map , pluck , switchMap , tap } from 'rxjs/operators' ;
4+ import { BehaviorSubject , forkJoin , NEVER , Observable , of } from 'rxjs' ;
5+ import { catchError , filter , first , map , pluck , switchMap , tap } from 'rxjs/operators' ;
66import { fetchHttp } from '../utils/fetchHttp' ;
77import { isScullyGenerated , isScullyRunning } from '../utils/isScully' ;
88
@@ -21,11 +21,11 @@ interface State {
2121} )
2222export class TransferStateService {
2323 private script : HTMLScriptElement ;
24- private isNavigatingBS = new BehaviorSubject < boolean > ( false ) ;
24+ // private stateBS = new BehaviorSubject<State>({});
25+ /** subject to fire off incomming states */
26+ private initialUrl : string ;
2527 private stateBS = new BehaviorSubject < State > ( { } ) ;
26- private state$ = this . isNavigatingBS . pipe (
27- switchMap ( isNav => ( isNav ? EMPTY : this . stateBS . asObservable ( ) ) )
28- ) ;
28+ private state$ = this . stateBS . pipe ( filter ( state => state !== undefined ) ) ;
2929
3030 constructor ( @Inject ( DOCUMENT ) private document : Document , private router : Router ) {
3131 this . setupEnvForTransferState ( ) ;
@@ -40,6 +40,9 @@ export class TransferStateService {
4040 this . document . head . appendChild ( this . script ) ;
4141 } else if ( isScullyGenerated ( ) ) {
4242 // On the client AFTER scully rendered it
43+ this . initialUrl = window . location . pathname || '__no_NO_no__' ;
44+ this . initialUrl = this . initialUrl . endsWith ( '/' ) ? this . initialUrl . slice ( 0 , - 1 ) : this . initialUrl ;
45+ /** set the initial state */
4346 this . stateBS . next ( ( window && window [ SCULLY_SCRIPT_ID ] ) || { } ) ;
4447 }
4548 }
@@ -50,6 +53,19 @@ export class TransferStateService {
5053 * @param name The name of the state to
5154 */
5255 getState < T > ( name : string ) : Observable < T > {
56+ /**
57+ * We need the initial state only when the app is booting.
58+ * In this case, the router doesn't fire an event.
59+ * As the boot process is SYNC, putting in anything async will cause flicker in the view.
60+ * we can't use the subject in this case, because it will fire the
61+ * data sync before the component is ready.
62+ */
63+ // if (this.initial) {
64+ // this.initial = false;
65+ // // this.stateBS.next(this.state);
66+ // return of(this.state[name]);
67+ // }
68+ /** once booted, the router will make sure this event fires after navigation */
5369 return this . state$ . pipe ( pluck ( name ) ) ;
5470 }
5571
@@ -75,7 +91,15 @@ export class TransferStateService {
7591 . pipe (
7692 filter ( e => e instanceof NavigationStart ) ,
7793 switchMap ( ( e : NavigationStart ) => {
78- this . isNavigatingBS . next ( true ) ;
94+ if ( this . initialUrl === e . url ) {
95+ this . initialUrl = '__done__with__Initial__navigation__' ;
96+ return NEVER ;
97+ }
98+ return of ( e ) ;
99+ } ) ,
100+ /** reset the state, so new components will never get stale data */
101+ tap ( ( ) => this . stateBS . next ( undefined ) ) ,
102+ switchMap ( ( e : NavigationStart ) => {
79103 return forkJoin ( [
80104 /** prevent emitting before navigation to _this_ URL is done. */
81105 this . router . events . pipe (
@@ -95,16 +119,18 @@ export class TransferStateService {
95119 const newStateStr = html . split ( SCULLY_STATE_START ) [ 1 ] . split ( SCULLY_STATE_END ) [ 0 ] ;
96120 return JSON . parse ( newStateStr ) ;
97121 } catch {
98- return null ;
122+ /** in case of emergency (no state parsing possible,set state to undefined) */
123+ return { } ;
99124 }
100125 } ) ,
101- /** prevent progressing in case anything went sour above */
102- filter ( val => val !== null ) ,
103- /** activate the new state */
126+ catchError ( e => {
127+ // TODO: come up with better error text.
128+ /** the developer needs to know, but its not fatal, so just return an empty state */
129+ console . warn ( 'Error for getState during navigation:' , e ) ;
130+ return of ( { } ) ;
131+ } ) ,
104132 tap ( newState => {
105- /** signal to send out update */
106- this . isNavigatingBS . next ( false ) ;
107- /** replace the state, so we don't leak memory on old state */
133+ /** and activate the state in the components. on any error it will be empty */
108134 this . stateBS . next ( newState ) ;
109135 } )
110136 )
0 commit comments