@@ -2,7 +2,7 @@ import { Inject, Injectable, isDevMode, OnDestroy, Optional } from '@angular/cor
22import { Params } from '@angular/router' ;
33import { EMPTY , from , Observable , Subject } from 'rxjs' ;
44import { catchError , concatMap , debounceTime , map , takeUntil , tap } from 'rxjs/operators' ;
5- import { isMissing } from '../util' ;
5+ import { isMissing , NOP } from '../util' ;
66import { Unpack } from '../types' ;
77import { QueryParamGroup } from '../model/query-param-group' ;
88import { QueryParam } from '../model/query-param' ;
@@ -24,6 +24,12 @@ function hasArraySerialization(queryParam: QueryParam<any>, values: string | str
2424 return isMultiQueryParam ( queryParam ) ;
2525}
2626
27+ /** @internal */
28+ class QueryParamDirectiveState {
29+ constructor ( public directive : QueryParamNameDirective , public queue$ : Subject < any > ) {
30+ }
31+ }
32+
2733/**
2834 * Service implementing the synchronization logic
2935 *
@@ -38,8 +44,8 @@ export class QueryParamGroupService implements OnDestroy {
3844 /** The {@link QueryParamGroup} to bind. */
3945 private queryParamGroup : QueryParamGroup ;
4046
41- /** List of {@link QueryParamNameDirective} directives registered to this service. */
42- private directives : QueryParamNameDirective [ ] = [ ] ;
47+ /** List of {@link QueryParamNameDirective} directives registered to this service (by name) . */
48+ private directives = new Map < string , QueryParamDirectiveState > ( ) ;
4349
4450 /**
4551 * Queue of navigation parameters
@@ -63,6 +69,10 @@ export class QueryParamGroupService implements OnDestroy {
6369 public ngOnDestroy ( ) {
6470 this . destroy$ . next ( ) ;
6571 this . destroy$ . complete ( ) ;
72+
73+ if ( this . queryParamGroup ) {
74+ this . queryParamGroup . _clearChangeFunctions ( ) ;
75+ }
6676 }
6777
6878 /**
@@ -82,14 +92,17 @@ export class QueryParamGroupService implements OnDestroy {
8292 /**
8393 * Registers a {@link QueryParamNameDirective} directive.
8494 */
85- public addQueryParam ( directive : QueryParamNameDirective ) : void {
95+ public registerQueryParamDirective ( directive : QueryParamNameDirective ) : void {
8696 const queryParam : QueryParam < any > = this . queryParamGroup . get ( directive . name ) ;
8797 if ( ! queryParam ) {
8898 throw new Error ( `Could not find query param with name ${ directive . name } . Did you forget to add it to your QueryParamGroup?` ) ;
8999 }
90100 if ( ! directive . valueAccessor ) {
91101 throw new Error ( `No value accessor found for the form control. Please make sure to implement ControlValueAccessor on this component.` ) ;
92102 }
103+ if ( this . directives . has ( directive . name ) ) {
104+ throw new Error ( `A directive with name ${ directive . name } has already been registered.` ) ;
105+ }
93106
94107 // Chances are that we read the initial route before a directive has been registered here.
95108 // The value in the model will be correct, but we need to sync it to the view once initially.
@@ -105,19 +118,26 @@ export class QueryParamGroupService implements OnDestroy {
105118
106119 directive . valueAccessor . registerOnChange ( ( newValue : any ) => debouncedQueue$ . next ( newValue ) ) ;
107120
108- this . directives . push ( directive ) ;
121+ this . directives . set ( directive . name , new QueryParamDirectiveState ( directive , debouncedQueue$ ) ) ;
109122 }
110123
111124 /**
112125 * Deregisters a {@link QueryParamNameDirective} directive.
113126 */
114- public removeQueryParam ( directive : QueryParamNameDirective ) : void {
115- const index = this . directives . indexOf ( directive ) ;
116- if ( index === - 1 ) {
117- return ;
127+ public deregisterQueryParamDirective ( directive : QueryParamNameDirective ) : void {
128+ const state = this . directives . get ( directive . name ) ;
129+ if ( state && state . queue$ ) {
130+ state . queue$ . complete ( ) ;
118131 }
119132
120- this . directives . splice ( index , 1 ) ;
133+ this . directives . delete ( directive . name ) ;
134+ directive . valueAccessor . registerOnChange ( NOP ) ;
135+ directive . valueAccessor . registerOnTouched ( NOP ) ;
136+
137+ const queryParam : QueryParam < any > = this . queryParamGroup . get ( directive . name ) ;
138+ if ( queryParam ) {
139+ queryParam . _clearChangeFunctions ( ) ;
140+ }
121141 }
122142
123143 private startSynchronization ( ) {
@@ -155,17 +175,19 @@ export class QueryParamGroupService implements OnDestroy {
155175
156176 /** Listens for changes in the router and synchronizes to the model. */
157177 private setupRouterListener ( ) {
158- this . routerAdapter . queryParamMap . subscribe ( queryParamMap => {
178+ this . routerAdapter . queryParamMap . pipe (
179+ takeUntil ( this . destroy$ )
180+ ) . subscribe ( queryParamMap => {
159181 Object . keys ( this . queryParamGroup . queryParams ) . forEach ( queryParamName => {
160182 const queryParam : QueryParam < any > = this . queryParamGroup . get ( queryParamName ) ;
161183 const newValue = queryParam . multi
162184 ? this . deserialize ( queryParam , queryParamMap . getAll ( queryParam . param ) )
163185 : this . deserialize ( queryParam , queryParamMap . get ( queryParam . param ) ) ;
164186
165187 // Get the directive, if it has been initialized yet.
166- const directive = this . directives . find ( dir => dir . name === queryParamName ) ;
167- if ( ! isMissing ( directive ) ) {
168- directive . valueAccessor . writeValue ( newValue ) ;
188+ const state = this . directives . get ( queryParamName ) ;
189+ if ( state && state . directive ) {
190+ state . directive . valueAccessor . writeValue ( newValue ) ;
169191 }
170192
171193 queryParam . value = newValue ;
0 commit comments