@@ -15,11 +15,10 @@ import {
1515 Injectable ,
1616 Input ,
1717 NgModule ,
18- NgZone ,
1918 OnDestroy ,
2019 Output ,
2120} from '@angular/core' ;
22- import { Observable , Subject } from 'rxjs' ;
21+ import { Observable , Subject , Subscription } from 'rxjs' ;
2322import { debounceTime } from 'rxjs/operators' ;
2423
2524/**
@@ -36,49 +35,63 @@ export class MutationObserverFactory {
3635
3736/** A factory that creates ContentObservers. */
3837@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. */
5238export 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+ } ) ;
5958 }
6059
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 ++ ;
6874 }
69- return this ;
75+ return this . _observedElements . get ( element ) ! . stream ;
7076 }
7177
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+ }
7583 }
7684 }
7785
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+ }
8295 }
8396}
8497
@@ -103,12 +116,10 @@ export class CdkObserveContent implements AfterContentInit, OnDestroy {
103116 get disabled ( ) { return this . _disabled ; }
104117 set disabled ( value : any ) {
105118 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 ( ) ;
112123 }
113124 }
114125 private _disabled = false ;
@@ -118,30 +129,34 @@ export class CdkObserveContent implements AfterContentInit, OnDestroy {
118129 get debounce ( ) : number { return this . _debounce ; }
119130 set debounce ( value : number ) {
120131 this . _debounce = coerceNumberProperty ( value ) ;
132+ this . _subscribe ( ) ;
121133 }
122134 private _debounce : number ;
123135
124- private _observer : ContentObserver ;
136+ private _currentSubscription : Subscription | null = null ;
125137
126- constructor ( private _contentObserverFactory : ContentObserverFactory ,
127- private _elementRef : ElementRef , private _ngZone : NgZone ) { }
138+ constructor ( private _contentObserver : ContentObserver , private _elementRef : ElementRef ) { }
128139
129140 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 ( ) ;
137143 }
138144 }
139145
140146 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 ( ) ;
145160 }
146161 }
147162}
0 commit comments