observers.mjs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. import { coerceElement, coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';
  2. import * as i0 from '@angular/core';
  3. import { Injectable, EventEmitter, Directive, Output, Input, NgModule } from '@angular/core';
  4. import { Observable, Subject } from 'rxjs';
  5. import { debounceTime } from 'rxjs/operators';
  6. /**
  7. * Factory that creates a new MutationObserver and allows us to stub it out in unit tests.
  8. * @docs-private
  9. */
  10. class MutationObserverFactory {
  11. create(callback) {
  12. return typeof MutationObserver === 'undefined' ? null : new MutationObserver(callback);
  13. }
  14. static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: MutationObserverFactory, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
  15. static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: MutationObserverFactory, providedIn: 'root' }); }
  16. }
  17. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: MutationObserverFactory, decorators: [{
  18. type: Injectable,
  19. args: [{ providedIn: 'root' }]
  20. }] });
  21. /** An injectable service that allows watching elements for changes to their content. */
  22. class ContentObserver {
  23. constructor(_mutationObserverFactory) {
  24. this._mutationObserverFactory = _mutationObserverFactory;
  25. /** Keeps track of the existing MutationObservers so they can be reused. */
  26. this._observedElements = new Map();
  27. }
  28. ngOnDestroy() {
  29. this._observedElements.forEach((_, element) => this._cleanupObserver(element));
  30. }
  31. observe(elementOrRef) {
  32. const element = coerceElement(elementOrRef);
  33. return new Observable((observer) => {
  34. const stream = this._observeElement(element);
  35. const subscription = stream.subscribe(observer);
  36. return () => {
  37. subscription.unsubscribe();
  38. this._unobserveElement(element);
  39. };
  40. });
  41. }
  42. /**
  43. * Observes the given element by using the existing MutationObserver if available, or creating a
  44. * new one if not.
  45. */
  46. _observeElement(element) {
  47. if (!this._observedElements.has(element)) {
  48. const stream = new Subject();
  49. const observer = this._mutationObserverFactory.create(mutations => stream.next(mutations));
  50. if (observer) {
  51. observer.observe(element, {
  52. characterData: true,
  53. childList: true,
  54. subtree: true,
  55. });
  56. }
  57. this._observedElements.set(element, { observer, stream, count: 1 });
  58. }
  59. else {
  60. this._observedElements.get(element).count++;
  61. }
  62. return this._observedElements.get(element).stream;
  63. }
  64. /**
  65. * Un-observes the given element and cleans up the underlying MutationObserver if nobody else is
  66. * observing this element.
  67. */
  68. _unobserveElement(element) {
  69. if (this._observedElements.has(element)) {
  70. this._observedElements.get(element).count--;
  71. if (!this._observedElements.get(element).count) {
  72. this._cleanupObserver(element);
  73. }
  74. }
  75. }
  76. /** Clean up the underlying MutationObserver for the specified element. */
  77. _cleanupObserver(element) {
  78. if (this._observedElements.has(element)) {
  79. const { observer, stream } = this._observedElements.get(element);
  80. if (observer) {
  81. observer.disconnect();
  82. }
  83. stream.complete();
  84. this._observedElements.delete(element);
  85. }
  86. }
  87. static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: ContentObserver, deps: [{ token: MutationObserverFactory }], target: i0.ɵɵFactoryTarget.Injectable }); }
  88. static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: ContentObserver, providedIn: 'root' }); }
  89. }
  90. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: ContentObserver, decorators: [{
  91. type: Injectable,
  92. args: [{ providedIn: 'root' }]
  93. }], ctorParameters: function () { return [{ type: MutationObserverFactory }]; } });
  94. /**
  95. * Directive that triggers a callback whenever the content of
  96. * its associated element has changed.
  97. */
  98. class CdkObserveContent {
  99. /**
  100. * Whether observing content is disabled. This option can be used
  101. * to disconnect the underlying MutationObserver until it is needed.
  102. */
  103. get disabled() {
  104. return this._disabled;
  105. }
  106. set disabled(value) {
  107. this._disabled = coerceBooleanProperty(value);
  108. this._disabled ? this._unsubscribe() : this._subscribe();
  109. }
  110. /** Debounce interval for emitting the changes. */
  111. get debounce() {
  112. return this._debounce;
  113. }
  114. set debounce(value) {
  115. this._debounce = coerceNumberProperty(value);
  116. this._subscribe();
  117. }
  118. constructor(_contentObserver, _elementRef, _ngZone) {
  119. this._contentObserver = _contentObserver;
  120. this._elementRef = _elementRef;
  121. this._ngZone = _ngZone;
  122. /** Event emitted for each change in the element's content. */
  123. this.event = new EventEmitter();
  124. this._disabled = false;
  125. this._currentSubscription = null;
  126. }
  127. ngAfterContentInit() {
  128. if (!this._currentSubscription && !this.disabled) {
  129. this._subscribe();
  130. }
  131. }
  132. ngOnDestroy() {
  133. this._unsubscribe();
  134. }
  135. _subscribe() {
  136. this._unsubscribe();
  137. const stream = this._contentObserver.observe(this._elementRef);
  138. // TODO(mmalerba): We shouldn't be emitting on this @Output() outside the zone.
  139. // Consider brining it back inside the zone next time we're making breaking changes.
  140. // Bringing it back inside can cause things like infinite change detection loops and changed
  141. // after checked errors if people's code isn't handling it properly.
  142. this._ngZone.runOutsideAngular(() => {
  143. this._currentSubscription = (this.debounce ? stream.pipe(debounceTime(this.debounce)) : stream).subscribe(this.event);
  144. });
  145. }
  146. _unsubscribe() {
  147. this._currentSubscription?.unsubscribe();
  148. }
  149. static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkObserveContent, deps: [{ token: ContentObserver }, { token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive }); }
  150. static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: CdkObserveContent, selector: "[cdkObserveContent]", inputs: { disabled: ["cdkObserveContentDisabled", "disabled"], debounce: "debounce" }, outputs: { event: "cdkObserveContent" }, exportAs: ["cdkObserveContent"], ngImport: i0 }); }
  151. }
  152. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: CdkObserveContent, decorators: [{
  153. type: Directive,
  154. args: [{
  155. selector: '[cdkObserveContent]',
  156. exportAs: 'cdkObserveContent',
  157. }]
  158. }], ctorParameters: function () { return [{ type: ContentObserver }, { type: i0.ElementRef }, { type: i0.NgZone }]; }, propDecorators: { event: [{
  159. type: Output,
  160. args: ['cdkObserveContent']
  161. }], disabled: [{
  162. type: Input,
  163. args: ['cdkObserveContentDisabled']
  164. }], debounce: [{
  165. type: Input
  166. }] } });
  167. class ObserversModule {
  168. static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: ObserversModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
  169. static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.0.0", ngImport: i0, type: ObserversModule, declarations: [CdkObserveContent], exports: [CdkObserveContent] }); }
  170. static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: ObserversModule, providers: [MutationObserverFactory] }); }
  171. }
  172. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: ObserversModule, decorators: [{
  173. type: NgModule,
  174. args: [{
  175. exports: [CdkObserveContent],
  176. declarations: [CdkObserveContent],
  177. providers: [MutationObserverFactory],
  178. }]
  179. }] });
  180. /**
  181. * Generated bundle index. Do not edit.
  182. */
  183. export { CdkObserveContent, ContentObserver, MutationObserverFactory, ObserversModule };
  184. //# sourceMappingURL=observers.mjs.map