X Tutup
Skip to content

Commit 0df8bc4

Browse files
vsavkinjelbourn
authored andcommitted
fix(dynamic_component_loader): leave the view tree in a consistent state when hydration fails
Closes #5718
1 parent 0d9a1de commit 0df8bc4

File tree

4 files changed

+49
-5
lines changed

4 files changed

+49
-5
lines changed

modules/angular2/src/core/application_ref.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import {NgZone} from 'angular2/src/core/zone/ng_zone';
2-
import {Type, isBlank, isPresent, assertionsEnabled, print, IS_DART} from 'angular2/src/facade/lang';
2+
import {
3+
Type,
4+
isBlank,
5+
isPresent,
6+
assertionsEnabled,
7+
print,
8+
IS_DART
9+
} from 'angular2/src/facade/lang';
310
import {provide, Provider, Injector, OpaqueToken} from 'angular2/src/core/di';
411
import {
512
APP_COMPONENT_REF_PROMISE,

modules/angular2/src/core/change_detection/abstract_change_detector.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
169169
// any work done in `hydrateDirectives`.
170170
dehydrateDirectives(destroyPipes: boolean): void {}
171171

172-
hydrated(): boolean { return this.context !== null; }
172+
hydrated(): boolean { return isPresent(this.context); }
173173

174174
afterContentLifecycleCallbacks(): void {
175175
this.dispatcher.notifyAfterContentChecked();

modules/angular2/src/core/linker/view_manager.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -319,9 +319,15 @@ export class AppViewManager_ extends AppViewManager {
319319
}
320320
this._utils.attachViewInContainer(parentView, boundElementIndex, contextView,
321321
contextBoundElementIndex, index, view);
322-
this._utils.hydrateViewInContainer(parentView, boundElementIndex, contextView,
323-
contextBoundElementIndex, index,
324-
imperativelyCreatedInjector);
322+
323+
try {
324+
this._utils.hydrateViewInContainer(parentView, boundElementIndex, contextView,
325+
contextBoundElementIndex, index,
326+
imperativelyCreatedInjector);
327+
} catch (e) {
328+
this._utils.detachViewInContainer(parentView, boundElementIndex, index);
329+
throw e;
330+
}
325331
return view.ref;
326332
}
327333

modules/angular2/test/core/linker/dynamic_component_loader_spec.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import {ElementRef} from 'angular2/src/core/linker/element_ref';
2626
import {DOCUMENT} from 'angular2/src/platform/dom/dom_tokens';
2727
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
2828
import {ComponentFixture_} from "angular2/src/testing/test_component_builder";
29+
import {BaseException} from 'angular2/src/facade/exceptions';
30+
import {PromiseWrapper} from 'angular2/src/facade/promise';
2931

3032
export function main() {
3133
describe('DynamicComponentLoader', function() {
@@ -124,6 +126,28 @@ export function main() {
124126
});
125127
}));
126128

129+
it('should leave the view tree in a consistent state if hydration fails',
130+
inject([DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
131+
(loader, tcb: TestComponentBuilder, async) => {
132+
tcb.overrideView(MyComp, new ViewMetadata({
133+
template: '<div><location #loc></location></div>',
134+
directives: [Location]
135+
}))
136+
.createAsync(MyComp)
137+
.then((tc: ComponentFixture) => {
138+
tc.debugElement
139+
140+
PromiseWrapper.catchError(
141+
loader.loadIntoLocation(DynamicallyLoadedThrows,
142+
tc.debugElement.elementRef, 'loc'),
143+
error => {
144+
expect(error.message).toContain("ThrownInConstructor");
145+
expect(() => tc.detectChanges()).not.toThrow();
146+
async.done();
147+
});
148+
});
149+
}));
150+
127151
it('should throw if the variable does not exist',
128152
inject([DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
129153
(loader, tcb: TestComponentBuilder, async) => {
@@ -223,6 +247,7 @@ export function main() {
223247
});
224248
});
225249
}));
250+
226251
});
227252

228253
describe('loadAsRoot', () => {
@@ -291,6 +316,12 @@ class DynamicallyCreatedCmp implements OnDestroy {
291316
class DynamicallyLoaded {
292317
}
293318

319+
@Component({selector: 'dummy'})
320+
@View({template: "DynamicallyLoaded;"})
321+
class DynamicallyLoadedThrows {
322+
constructor() { throw new BaseException("ThrownInConstructor"); }
323+
}
324+
294325
@Component({selector: 'dummy'})
295326
@View({template: "DynamicallyLoaded2;"})
296327
class DynamicallyLoaded2 {

0 commit comments

Comments
 (0)
X Tutup