Skip to content

Commit

Permalink
fix(dynamic_component_loader): leave the view tree in a consistent st…
Browse files Browse the repository at this point in the history
…ate when hydration fails

Closes #5718
  • Loading branch information
vsavkin authored and jelbourn committed Dec 9, 2015
1 parent 0d9a1de commit 0df8bc4
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 5 deletions.
9 changes: 8 additions & 1 deletion modules/angular2/src/core/application_ref.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import {NgZone} from 'angular2/src/core/zone/ng_zone';
import {Type, isBlank, isPresent, assertionsEnabled, print, IS_DART} from 'angular2/src/facade/lang';
import {
Type,
isBlank,
isPresent,
assertionsEnabled,
print,
IS_DART
} from 'angular2/src/facade/lang';
import {provide, Provider, Injector, OpaqueToken} from 'angular2/src/core/di';
import {
APP_COMPONENT_REF_PROMISE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
// any work done in `hydrateDirectives`.
dehydrateDirectives(destroyPipes: boolean): void {}

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

afterContentLifecycleCallbacks(): void {
this.dispatcher.notifyAfterContentChecked();
Expand Down
12 changes: 9 additions & 3 deletions modules/angular2/src/core/linker/view_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,9 +319,15 @@ export class AppViewManager_ extends AppViewManager {
}
this._utils.attachViewInContainer(parentView, boundElementIndex, contextView,
contextBoundElementIndex, index, view);
this._utils.hydrateViewInContainer(parentView, boundElementIndex, contextView,
contextBoundElementIndex, index,
imperativelyCreatedInjector);

try {
this._utils.hydrateViewInContainer(parentView, boundElementIndex, contextView,
contextBoundElementIndex, index,
imperativelyCreatedInjector);
} catch (e) {
this._utils.detachViewInContainer(parentView, boundElementIndex, index);
throw e;
}
return view.ref;
}

Expand Down
31 changes: 31 additions & 0 deletions modules/angular2/test/core/linker/dynamic_component_loader_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import {ElementRef} from 'angular2/src/core/linker/element_ref';
import {DOCUMENT} from 'angular2/src/platform/dom/dom_tokens';
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
import {ComponentFixture_} from "angular2/src/testing/test_component_builder";
import {BaseException} from 'angular2/src/facade/exceptions';
import {PromiseWrapper} from 'angular2/src/facade/promise';

export function main() {
describe('DynamicComponentLoader', function() {
Expand Down Expand Up @@ -124,6 +126,28 @@ export function main() {
});
}));

it('should leave the view tree in a consistent state if hydration fails',
inject([DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
(loader, tcb: TestComponentBuilder, async) => {
tcb.overrideView(MyComp, new ViewMetadata({
template: '<div><location #loc></location></div>',
directives: [Location]
}))
.createAsync(MyComp)
.then((tc: ComponentFixture) => {
tc.debugElement

PromiseWrapper.catchError(
loader.loadIntoLocation(DynamicallyLoadedThrows,
tc.debugElement.elementRef, 'loc'),
error => {
expect(error.message).toContain("ThrownInConstructor");
expect(() => tc.detectChanges()).not.toThrow();
async.done();
});
});
}));

it('should throw if the variable does not exist',
inject([DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
(loader, tcb: TestComponentBuilder, async) => {
Expand Down Expand Up @@ -223,6 +247,7 @@ export function main() {
});
});
}));

});

describe('loadAsRoot', () => {
Expand Down Expand Up @@ -291,6 +316,12 @@ class DynamicallyCreatedCmp implements OnDestroy {
class DynamicallyLoaded {
}

@Component({selector: 'dummy'})
@View({template: "DynamicallyLoaded;"})
class DynamicallyLoadedThrows {
constructor() { throw new BaseException("ThrownInConstructor"); }
}

@Component({selector: 'dummy'})
@View({template: "DynamicallyLoaded2;"})
class DynamicallyLoaded2 {
Expand Down

0 comments on commit 0df8bc4

Please sign in to comment.