Skip to content
This repository was archived by the owner on Sep 16, 2022. It is now read-only.

refactor(Core): Remove ApplicationRefImpl. #1471

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions _tests/test/core/application_ref_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import 'package:test/test.dart';
import 'application_ref_test.template.dart' as ng;

void main() {
ApplicationRefImpl appRef;
ApplicationRef appRef;

group('dispose should ', () {
setUp(() {
appRef = new ApplicationRefImpl(
appRef = internalCreateApplicationRef(
new NgZone(enableLongStackTrace: true),
new Injector.map({
ExceptionHandler: const BrowserExceptionHandler(),
Expand Down
6 changes: 5 additions & 1 deletion angular/lib/src/bootstrap/modules.dart
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,11 @@ String createRandomAppId() {
@experimental
const bootstrapLegacyModule = const <Object>[
bootstrapMinimalModule,
const Provider(ApplicationRef, useClass: ApplicationRefImpl),
const Provider(
ApplicationRef,
useFactory: internalCreateApplicationRef,
deps: const [NgZone, Injector],
),
const Provider(AppViewUtils),
const Provider(SlowComponentLoader),
];
2 changes: 1 addition & 1 deletion angular/lib/src/bootstrap/run.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Injector appInjector(InjectorFactory userProvidedInjector) {
// We also add other top-level services with similar constraints:
// * `AppViewUtils`
return ngZone.run(() {
applicationRef = new ApplicationRefImpl(
applicationRef = internalCreateApplicationRef(
ngZone,
userInjector,
);
Expand Down
159 changes: 81 additions & 78 deletions angular/lib/src/core/application_ref.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,137 +3,140 @@ import 'dart:html';

import 'package:angular/src/core/change_detection/host.dart';
import 'package:angular/src/runtime.dart';
import 'package:meta/dart2js.dart' as dart2js;

import '../facade/exception_handler.dart' show ExceptionHandler;
import 'change_detection/host.dart';
import 'di.dart';
import 'linker/component_factory.dart' show ComponentRef, ComponentFactory;
import 'testability/testability.dart' show TestabilityRegistry, Testability;
import 'zone/ng_zone.dart' show NgZone, NgZoneError;
import 'zone/ng_zone.dart' show NgZone;

/// Create an Angular zone.
NgZone createNgZone() => new NgZone(enableLongStackTrace: isDevMode);
/// **INTERNAL ONLY**: Do not use.
@dart2js.tryInline
ApplicationRef internalCreateApplicationRef(
NgZone ngZone,
Injector injector,
) {
return new ApplicationRef._(
ngZone,
unsafeCast(injector.get(ExceptionHandler)),
injector,
);
}

/// A reference to an Angular application running on a page.
///
/// For more about Angular applications, see the documentation for [bootstrap].
abstract class ApplicationRef implements ChangeDetectionHost {
/// Register a listener to be called when the application is disposed.
void registerDisposeListener(void Function() listener);

/// Bootstrap a new component at the root level of the application.
///
/// When bootstrapping a new root component into an application,
/// Angular mounts the specified application component onto DOM elements
/// identified by the component's selector and kicks off automatic change
/// detection to finish initializing the component.
ComponentRef<T> bootstrap<T>(ComponentFactory<T> componentFactory);

/// Dispose of this application and all of its components.
void dispose();
}
class ApplicationRef extends ChangeDetectionHost {
final List<void Function()> _disposeListeners = [];
final List<ComponentRef<void>> _rootComponents = [];

@Injectable()
class ApplicationRefImpl extends ApplicationRef with ChangeDetectionHost {
final NgZone _zone;
final ExceptionHandler _exceptionHandler;
final Injector _injector;
final List<void Function()> _disposeListeners = [];
final List<ComponentRef> _rootComponents = [];
final List<StreamSubscription> _streamSubscriptions = [];
final NgZone _ngZone;

ExceptionHandler _exceptionHandler;
StreamSubscription<void> _onErrorSub;
StreamSubscription<void> _onMicroSub;

ApplicationRefImpl(this._zone, this._injector) {
_zone.run(() {
_exceptionHandler = unsafeCast(_injector.get(ExceptionHandler));
});
_streamSubscriptions.add(_zone.onError.listen((NgZoneError error) {
ApplicationRef._(
this._ngZone,
this._exceptionHandler,
this._injector,
) {
_onErrorSub = _ngZone.onError.listen((e) {
handleUncaughtException(
error.error,
new StackTrace.fromString(error.stackTrace.join('\n')),
e.error,
new StackTrace.fromString(e.stackTrace.join('\n')),
);
}));
_streamSubscriptions.add(_zone.onMicrotaskEmpty.listen((_) {
_zone.runGuarded(() {
tick();
});
}));
});
_onMicroSub = _ngZone.onMicrotaskEmpty.listen((_) {
_ngZone.runGuarded(tick);
});
}

/// Register a listener to be called when the application is disposed.
void registerDisposeListener(void Function() listener) {
_disposeListeners.add(listener);
}

/// Bootstrap a new component at the root level of the application.
///
/// When bootstrapping a new root component into an application,
/// Angular mounts the specified application component onto DOM elements
/// identified by the component's selector and kicks off automatic change
/// detection to finish initializing the component.
ComponentRef<T> bootstrap<T>(ComponentFactory<T> componentFactory) {
return unsafeCast(run(() {
var compRef = componentFactory.create(_injector, const []);
Element existingElement =
document.querySelector(componentFactory.selector);
final component = componentFactory.create(_injector);
final existing = querySelector(componentFactory.selector);
Element replacement;
if (existingElement != null) {
Element newElement = compRef.location;
if (existing != null) {
final newElement = component.location;
// For app shards using bootstrapStatic, transfer element id
// from original node to allow hosting applications to locate loaded
// application root.
if (newElement.id == null || newElement.id.isEmpty) {
newElement.id = existingElement.id;
newElement.id = existing.id;
}
existingElement.replaceWith(newElement);
replacement = newElement;
existing.replaceWith(replacement);
} else {
assert(compRef.location != null,
'Could not locate node with selector ${componentFactory.selector}');
document.body.append(compRef.location);
assert(component.location != null);
document.body.append(component.location);
}
compRef.onDestroy(() {
_unloadComponent(compRef);
replacement?.remove();
});
var testability = compRef.injector.get(Testability, null);
final testability = unsafeCast<Testability>(
component.injector.get(Testability, null),
);
if (testability != null) {
compRef.injector
.get(TestabilityRegistry)
.registerApplication(compRef.location, testability);
final registry = unsafeCast<TestabilityRegistry>(
_injector.get(TestabilityRegistry),
);
registry.registerApplication(component.location, testability);
}
_loadComponent(compRef);
return compRef;
_loadedRootComponent(component, replacement);
return component;
}));
}

void _loadComponent(ComponentRef<dynamic> componentRef) {
registerChangeDetector(componentRef.changeDetectorRef);
void _loadedRootComponent(ComponentRef<void> component, Element node) {
_rootComponents.add(component);
component.onDestroy(() {
_destroyedRootComponent(component);
node?.remove();
});
registerChangeDetector(component.changeDetectorRef);
tick();
_rootComponents.add(componentRef);
}

void _unloadComponent(ComponentRef<dynamic> componentRef) {
if (!_rootComponents.contains(componentRef)) {
void _destroyedRootComponent(ComponentRef<void> component) {
if (!_rootComponents.remove(component)) {
return;
}
unregisterChangeDetector(componentRef.changeDetectorRef);
_rootComponents.remove(componentRef);
unregisterChangeDetector(component.changeDetectorRef);
}

@override
/// Dispose of this application and all of its components.
void dispose() {
for (var ref in _rootComponents) {
ref.destroy();
}
for (var dispose in _disposeListeners) {
dispose();
_onErrorSub.cancel();
_onMicroSub.cancel();
for (final component in _rootComponents) {
component.destroy();
}
_disposeListeners.clear();
for (var subscription in _streamSubscriptions) {
subscription.cancel();
for (final listener in _disposeListeners) {
listener();
}
_streamSubscriptions.clear();
}

@override
void handleUncaughtException(Object error,
[StackTrace trace, String reason]) {
void handleUncaughtException(
Object error, [
StackTrace trace,
String reason,
]) {
_exceptionHandler.call(error, trace, reason);
}

@override
R runInZone<R>(R Function() callback) => _zone.run(callback);
R runInZone<R>(R Function() callback) => _ngZone.run(callback);
}
4 changes: 2 additions & 2 deletions angular_test/lib/src/bootstrap.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Future<ComponentRef<E>> bootstrapForTest<E>(
}
// This should be kept in sync with 'runApp' as much as possible.
final injector = appInjector(userInjector);
final ApplicationRefImpl appRef = injector.get(ApplicationRef);
final ApplicationRef appRef = injector.get(ApplicationRef);
NgZoneError caughtError;
final NgZone ngZone = injector.get(NgZone);
final onErrorSub = ngZone.onError.listen((e) {
Expand Down Expand Up @@ -92,7 +92,7 @@ Future<ComponentRef<E>> bootstrapForTest<E>(
}

Future<ComponentRef<E>> _runAndLoadComponent<E>(
ApplicationRefImpl appRef,
ApplicationRef appRef,
ComponentFactory<E> componentFactory,
Element hostElement,
Injector injector, {
Expand Down