@@ -15,11 +15,10 @@ import {
15
15
Injectable ,
16
16
Input ,
17
17
NgModule ,
18
- NgZone ,
19
18
OnDestroy ,
20
19
Output ,
21
20
} from '@angular/core' ;
22
- import { Observable , Subject } from 'rxjs' ;
21
+ import { Observable , Subject , Subscription } from 'rxjs' ;
23
22
import { debounceTime } from 'rxjs/operators' ;
24
23
25
24
/**
@@ -36,49 +35,63 @@ export class MutationObserverFactory {
36
35
37
36
/** A factory that creates ContentObservers. */
38
37
@Injectable ( { providedIn : 'root' } )
39
- export class ContentObserverFactory {
40
- constructor ( private _mutationObserverFactory : MutationObserverFactory , private _ngZone : NgZone ) { }
41
-
42
- create ( element : Element , debounce ?: number ) {
43
- const changes = new Subject < MutationRecord [ ] > ( ) ;
44
- const observer = this . _ngZone . runOutsideAngular (
45
- ( ) => this . _mutationObserverFactory . create ( ( mutations ) => changes . next ( mutations ) ) ) ;
46
- return new ContentObserver ( element , observer , changes , debounce ) ;
47
- }
48
- }
49
-
50
-
51
- /** A class that observes an element for content changes. */
52
38
export class ContentObserver {
53
- changes : Observable < MutationRecord [ ] > ;
54
-
55
- constructor ( private _element : Element , private _mutationObserver : MutationObserver | null ,
56
- private _rawChanges : Subject < MutationRecord [ ] > , debounce : number = 0 ) {
57
- this . changes = debounce ?
58
- this . _rawChanges . pipe ( debounceTime ( debounce ) ) : this . _rawChanges . asObservable ( ) ;
39
+ private _observedElements = new Map < Element , {
40
+ observer : MutationObserver | null ,
41
+ stream : Subject < MutationRecord [ ] > ,
42
+ count : number
43
+ } > ( ) ;
44
+
45
+ constructor ( private _mutationObserverFactory : MutationObserverFactory ) { }
46
+
47
+ observe ( element : Element , debounce ?: number ) : Observable < MutationRecord [ ] > {
48
+ return Observable . create ( observer => {
49
+ const stream = this . _observeElement ( element ) ;
50
+ const subscription =
51
+ ( debounce ? stream . pipe ( debounceTime ( debounce ) ) : stream ) . subscribe ( observer ) ;
52
+
53
+ return ( ) => {
54
+ subscription . unsubscribe ( ) ;
55
+ this . _unobserveElement ( element ) ;
56
+ }
57
+ } ) ;
59
58
}
60
59
61
- start ( ) : ContentObserver {
62
- if ( this . _mutationObserver ) {
63
- this . _mutationObserver . observe ( this . _element , {
64
- characterData : true ,
65
- childList : true ,
66
- subtree : true
67
- } ) ;
60
+ private _observeElement ( element : Element ) : Subject < MutationRecord [ ] > {
61
+ if ( ! this . _observedElements . has ( element ) ) {
62
+ const stream = new Subject < MutationRecord [ ] > ( ) ;
63
+ const observer = this . _mutationObserverFactory . create ( mutations => stream . next ( mutations ) ) ;
64
+ if ( observer ) {
65
+ observer . observe ( element , {
66
+ characterData : true ,
67
+ childList : true ,
68
+ subtree : true
69
+ } ) ;
70
+ }
71
+ this . _observedElements . set ( element , { observer, stream, count : 1 } ) ;
72
+ } else {
73
+ this . _observedElements . get ( element ) ! . count ++ ;
68
74
}
69
- return this ;
75
+ return this . _observedElements . get ( element ) ! . stream ;
70
76
}
71
77
72
- pause ( ) {
73
- if ( this . _mutationObserver ) {
74
- this . _mutationObserver . disconnect ( ) ;
78
+ private _unobserveElement ( element : Element ) {
79
+ if ( this . _observedElements . has ( element ) ) {
80
+ if ( ! -- this . _observedElements . get ( element ) ! . count ) {
81
+ this . _cleanupObserver ( element ) ;
82
+ }
75
83
}
76
84
}
77
85
78
- stop ( ) {
79
- this . pause ( ) ;
80
- this . _rawChanges . complete ( ) ;
81
- this . _mutationObserver = null ;
86
+ private _cleanupObserver ( element : Element ) {
87
+ if ( this . _observedElements . has ( element ) ) {
88
+ const { observer, stream} = this . _observedElements . get ( element ) ! ;
89
+ if ( observer ) {
90
+ observer . disconnect ( ) ;
91
+ }
92
+ stream . complete ( ) ;
93
+ this . _observedElements . delete ( element ) ;
94
+ }
82
95
}
83
96
}
84
97
@@ -103,12 +116,10 @@ export class CdkObserveContent implements AfterContentInit, OnDestroy {
103
116
get disabled ( ) { return this . _disabled ; }
104
117
set disabled ( value : any ) {
105
118
this . _disabled = coerceBooleanProperty ( value ) ;
106
- if ( this . _observer ) {
107
- if ( this . _disabled ) {
108
- this . _observer . pause ( ) ;
109
- } else {
110
- this . _observer . start ( ) ;
111
- }
119
+ if ( this . _disabled ) {
120
+ this . _unsubscribe ( ) ;
121
+ } else {
122
+ this . _subscribe ( ) ;
112
123
}
113
124
}
114
125
private _disabled = false ;
@@ -118,30 +129,34 @@ export class CdkObserveContent implements AfterContentInit, OnDestroy {
118
129
get debounce ( ) : number { return this . _debounce ; }
119
130
set debounce ( value : number ) {
120
131
this . _debounce = coerceNumberProperty ( value ) ;
132
+ this . _subscribe ( ) ;
121
133
}
122
134
private _debounce : number ;
123
135
124
- private _observer : ContentObserver ;
136
+ private _currentSubscription : Subscription | null = null ;
125
137
126
- constructor ( private _contentObserverFactory : ContentObserverFactory ,
127
- private _elementRef : ElementRef , private _ngZone : NgZone ) { }
138
+ constructor ( private _contentObserver : ContentObserver , private _elementRef : ElementRef ) { }
128
139
129
140
ngAfterContentInit ( ) {
130
- this . _observer =
131
- this . _contentObserverFactory . create ( this . _elementRef . nativeElement , this . debounce ) ;
132
- this . _ngZone . run (
133
- ( ) => this . _observer . changes . subscribe ( mutations => this . event . next ( mutations ) ) ) ;
134
-
135
- if ( ! this . disabled ) {
136
- this . _observer . start ( ) ;
141
+ if ( ! this . _currentSubscription && ! this . disabled ) {
142
+ this . _subscribe ( ) ;
137
143
}
138
144
}
139
145
140
146
ngOnDestroy ( ) {
141
- // Its possible _observer is undefined if the component is destroyed before init
142
- // (could happen in a test).
143
- if ( this . _observer ) {
144
- this . _observer . stop ( ) ;
147
+ this . _unsubscribe ( ) ;
148
+ }
149
+
150
+ private _subscribe ( ) {
151
+ this . _unsubscribe ( ) ;
152
+ this . _currentSubscription =
153
+ this . _contentObserver . observe ( this . _elementRef . nativeElement , this . debounce )
154
+ . subscribe ( mutations => this . event . next ( mutations ) ) ;
155
+ }
156
+
157
+ private _unsubscribe ( ) {
158
+ if ( this . _currentSubscription ) {
159
+ this . _currentSubscription . unsubscribe ( ) ;
145
160
}
146
161
}
147
162
}
0 commit comments