|
6 | 6 | * found in the LICENSE file at https://angular.io/license
|
7 | 7 | */
|
8 | 8 |
|
9 |
| -import {ApplicationRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, EventEmitter, Injector, OnChanges, SimpleChange, SimpleChanges, Type} from '@angular/core'; |
| 9 | +import {ApplicationRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, EventEmitter, Injector, NgZone, OnChanges, SimpleChange, SimpleChanges, Type} from '@angular/core'; |
10 | 10 | import {merge, Observable} from 'rxjs';
|
11 | 11 | import {map} from 'rxjs/operators';
|
12 | 12 |
|
@@ -71,78 +71,108 @@ export class ComponentNgElementStrategy implements NgElementStrategy {
|
71 | 71 | */
|
72 | 72 | private readonly unchangedInputs = new Set<string>();
|
73 | 73 |
|
74 |
| - constructor(private componentFactory: ComponentFactory<any>, private injector: Injector) {} |
| 74 | + /** check for zone.js loaded */ |
| 75 | + private readonly zoneless: boolean; |
| 76 | + |
| 77 | + /** service for setting zone context */ |
| 78 | + private readonly ngZone: NgZone; |
| 79 | + |
| 80 | + /** the zone the element was created in */ |
| 81 | + private readonly elementZone: Zone|undefined; |
| 82 | + |
| 83 | + constructor(private componentFactory: ComponentFactory<any>, private injector: Injector) { |
| 84 | + this.zoneless = typeof Zone == 'undefined'; |
| 85 | + this.ngZone = this.injector.get<NgZone>(NgZone); |
| 86 | + if (!this.zoneless) { |
| 87 | + this.elementZone = this.ngZone.run(() => { |
| 88 | + return Zone.current; |
| 89 | + }); |
| 90 | + } |
| 91 | + } |
75 | 92 |
|
76 | 93 | /**
|
77 | 94 | * Initializes a new component if one has not yet been created and cancels any scheduled
|
78 | 95 | * destruction.
|
79 | 96 | */
|
80 | 97 | connect(element: HTMLElement) {
|
81 |
| - // If the element is marked to be destroyed, cancel the task since the component was reconnected |
82 |
| - if (this.scheduledDestroyFn !== null) { |
83 |
| - this.scheduledDestroyFn(); |
84 |
| - this.scheduledDestroyFn = null; |
85 |
| - return; |
86 |
| - } |
| 98 | + this.runInZone(() => { |
| 99 | + // If the element is marked to be destroyed, cancel the task since the component was |
| 100 | + // reconnected |
| 101 | + if (this.scheduledDestroyFn !== null) { |
| 102 | + this.scheduledDestroyFn(); |
| 103 | + this.scheduledDestroyFn = null; |
| 104 | + return; |
| 105 | + } |
87 | 106 |
|
88 |
| - if (this.componentRef === null) { |
89 |
| - this.initializeComponent(element); |
90 |
| - } |
| 107 | + if (this.componentRef === null) { |
| 108 | + this.initializeComponent(element); |
| 109 | + } |
| 110 | + }); |
91 | 111 | }
|
92 | 112 |
|
93 | 113 | /**
|
94 | 114 | * Schedules the component to be destroyed after some small delay in case the element is just
|
95 | 115 | * being moved across the DOM.
|
96 | 116 | */
|
97 | 117 | disconnect() {
|
98 |
| - // Return if there is no componentRef or the component is already scheduled for destruction |
99 |
| - if (this.componentRef === null || this.scheduledDestroyFn !== null) { |
100 |
| - return; |
101 |
| - } |
102 |
| - |
103 |
| - // Schedule the component to be destroyed after a small timeout in case it is being |
104 |
| - // moved elsewhere in the DOM |
105 |
| - this.scheduledDestroyFn = scheduler.schedule(() => { |
106 |
| - if (this.componentRef !== null) { |
107 |
| - this.componentRef.destroy(); |
108 |
| - this.componentRef = null; |
| 118 | + this.runInZone(() => { |
| 119 | + // Return if there is no componentRef or the component is already scheduled for destruction |
| 120 | + if (this.componentRef === null || this.scheduledDestroyFn !== null) { |
| 121 | + return; |
109 | 122 | }
|
110 |
| - }, DESTROY_DELAY); |
| 123 | + |
| 124 | + // Schedule the component to be destroyed after a small timeout in case it is being |
| 125 | + // moved elsewhere in the DOM |
| 126 | + this.scheduledDestroyFn = scheduler.schedule(() => { |
| 127 | + if (this.componentRef !== null) { |
| 128 | + this.componentRef.destroy(); |
| 129 | + this.componentRef = null; |
| 130 | + } |
| 131 | + }, DESTROY_DELAY); |
| 132 | + }); |
111 | 133 | }
|
112 | 134 |
|
113 | 135 | /**
|
114 | 136 | * Returns the component property value. If the component has not yet been created, the value is
|
115 | 137 | * retrieved from the cached initialization values.
|
116 | 138 | */
|
117 | 139 | getInputValue(property: string): any {
|
118 |
| - if (this.componentRef === null) { |
119 |
| - return this.initialInputValues.get(property); |
120 |
| - } |
| 140 | + return this.runInZone(() => { |
| 141 | + if (this.componentRef === null) { |
| 142 | + return this.initialInputValues.get(property); |
| 143 | + } |
121 | 144 |
|
122 |
| - return this.componentRef.instance[property]; |
| 145 | + return this.componentRef.instance[property]; |
| 146 | + }); |
123 | 147 | }
|
124 | 148 |
|
125 | 149 | /**
|
126 | 150 | * Sets the input value for the property. If the component has not yet been created, the value is
|
127 | 151 | * cached and set when the component is created.
|
128 | 152 | */
|
129 | 153 | setInputValue(property: string, value: any): void {
|
130 |
| - if (this.componentRef === null) { |
131 |
| - this.initialInputValues.set(property, value); |
132 |
| - return; |
133 |
| - } |
| 154 | + this.runInZone(() => { |
| 155 | + if (this.componentRef === null) { |
| 156 | + this.initialInputValues.set(property, value); |
| 157 | + return; |
| 158 | + } |
134 | 159 |
|
135 |
| - // Ignore the value if it is strictly equal to the current value, except if it is `undefined` |
136 |
| - // and this is the first change to the value (because an explicit `undefined` _is_ strictly |
137 |
| - // equal to not having a value set at all, but we still need to record this as a change). |
138 |
| - if (strictEquals(value, this.getInputValue(property)) && |
139 |
| - !((value === undefined) && this.unchangedInputs.has(property))) { |
140 |
| - return; |
141 |
| - } |
| 160 | + // Ignore the value if it is strictly equal to the current value, except if it is `undefined` |
| 161 | + // and this is the first change to the value (because an explicit `undefined` _is_ strictly |
| 162 | + // equal to not having a value set at all, but we still need to record this as a change). |
| 163 | + if (strictEquals(value, this.getInputValue(property)) && |
| 164 | + !((value === undefined) && this.unchangedInputs.has(property))) { |
| 165 | + return; |
| 166 | + } |
| 167 | + |
| 168 | + this.recordInputChange(property, value); |
| 169 | + this.componentRef.instance[property] = value; |
| 170 | + this.scheduleDetectChanges(); |
| 171 | + }); |
| 172 | + } |
142 | 173 |
|
143 |
| - this.recordInputChange(property, value); |
144 |
| - this.componentRef.instance[property] = value; |
145 |
| - this.scheduleDetectChanges(); |
| 174 | + private runInZone(fn: () => any) { |
| 175 | + return (this.zoneless || Zone.current === this.elementZone) ? fn() : this.ngZone.run(fn); |
146 | 176 | }
|
147 | 177 |
|
148 | 178 | /**
|
|
0 commit comments