repackaging: all the file moves
This commit is contained in:
23
modules/@angular/core/src/angular_entrypoint.dart
Normal file
23
modules/@angular/core/src/angular_entrypoint.dart
Normal file
@ -0,0 +1,23 @@
|
||||
library angular2.src.core.angular_entrypoint;
|
||||
|
||||
/**
|
||||
* Marks a function or method as an Angular 2 entrypoint. Only necessary in Dart code.
|
||||
*
|
||||
* The optional `name` parameter will be reflected in logs when the entry point is processed.
|
||||
*
|
||||
* See [the wiki][] for detailed documentation.
|
||||
* [the wiki]: https://github.com/angular/angular/wiki/Angular-2-Dart-Transformer#entry_points
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```
|
||||
* @AngularEntrypoint("name-for-debug")
|
||||
* void main() {
|
||||
* bootstrap(MyComponent);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class AngularEntrypoint {
|
||||
final String name;
|
||||
const AngularEntrypoint([this.name]);
|
||||
}
|
3
modules/@angular/core/src/angular_entrypoint.ts
Normal file
3
modules/@angular/core/src/angular_entrypoint.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import 'angular2/src/facade/lang'; // kept so that Tools know this is an ES6 file
|
||||
|
||||
// TS does not have AngularEntrypoint
|
29
modules/@angular/core/src/application_common_providers.ts
Normal file
29
modules/@angular/core/src/application_common_providers.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import {Type} from 'angular2/src/facade/lang';
|
||||
import {APP_ID_RANDOM_PROVIDER} from './application_tokens';
|
||||
import {APPLICATION_CORE_PROVIDERS} from './application_ref';
|
||||
import {
|
||||
IterableDiffers,
|
||||
defaultIterableDiffers,
|
||||
KeyValueDiffers,
|
||||
defaultKeyValueDiffers
|
||||
} from './change_detection/change_detection';
|
||||
import {ViewUtils} from "./linker/view_utils";
|
||||
import {ComponentResolver, ReflectorComponentResolver} from './linker/component_resolver';
|
||||
import {DynamicComponentLoader, DynamicComponentLoader_} from './linker/dynamic_component_loader';
|
||||
|
||||
let __unused: Type; // avoid unused import when Type union types are erased
|
||||
|
||||
/**
|
||||
* A default set of providers which should be included in any Angular
|
||||
* application, regardless of the platform it runs onto.
|
||||
*/
|
||||
export const APPLICATION_COMMON_PROVIDERS: Array<Type | {[k: string]: any} | any[]> =
|
||||
/*@ts2dart_const*/[
|
||||
APPLICATION_CORE_PROVIDERS,
|
||||
/* @ts2dart_Provider */ {provide: ComponentResolver, useClass: ReflectorComponentResolver},
|
||||
APP_ID_RANDOM_PROVIDER,
|
||||
ViewUtils,
|
||||
/* @ts2dart_Provider */ {provide: IterableDiffers, useValue: defaultIterableDiffers},
|
||||
/* @ts2dart_Provider */ {provide: KeyValueDiffers, useValue: defaultKeyValueDiffers},
|
||||
/* @ts2dart_Provider */ {provide: DynamicComponentLoader, useClass: DynamicComponentLoader_}
|
||||
];
|
451
modules/@angular/core/src/application_ref.ts
Normal file
451
modules/@angular/core/src/application_ref.ts
Normal file
@ -0,0 +1,451 @@
|
||||
import {NgZone, NgZoneError} from 'angular2/src/core/zone/ng_zone';
|
||||
import {
|
||||
Type,
|
||||
isBlank,
|
||||
isPresent,
|
||||
assertionsEnabled,
|
||||
print,
|
||||
IS_DART,
|
||||
lockMode,
|
||||
isPromise
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {provide, Provider, Injector, Injectable} from 'angular2/src/core/di';
|
||||
import {APP_ID_RANDOM_PROVIDER, PLATFORM_INITIALIZER, APP_INITIALIZER} from './application_tokens';
|
||||
import {PromiseWrapper, PromiseCompleter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/testability';
|
||||
import {ComponentResolver} from 'angular2/src/core/linker/component_resolver';
|
||||
import {ComponentRef, ComponentFactory} from 'angular2/src/core/linker/component_factory';
|
||||
import {
|
||||
BaseException,
|
||||
WrappedException,
|
||||
ExceptionHandler,
|
||||
unimplemented
|
||||
} from 'angular2/src/facade/exceptions';
|
||||
import {Console} from 'angular2/src/core/console';
|
||||
import {wtfLeave, wtfCreateScope, WtfScopeFn} from './profile/profile';
|
||||
import {ChangeDetectorRef} from 'angular2/src/core/change_detection/change_detector_ref';
|
||||
|
||||
/**
|
||||
* Create an Angular zone.
|
||||
*/
|
||||
export function createNgZone(): NgZone {
|
||||
return new NgZone({enableLongStackTrace: assertionsEnabled()});
|
||||
}
|
||||
|
||||
var _platform: PlatformRef;
|
||||
var _inPlatformCreate: boolean = false;
|
||||
|
||||
/**
|
||||
* Creates a platform.
|
||||
* Platforms have to be eagerly created via this function.
|
||||
*/
|
||||
export function createPlatform(injector: Injector): PlatformRef {
|
||||
if (_inPlatformCreate) {
|
||||
throw new BaseException('Already creating a platform...');
|
||||
}
|
||||
if (isPresent(_platform) && !_platform.disposed) {
|
||||
throw new BaseException(
|
||||
"There can be only one platform. Destroy the previous one to create a new one.");
|
||||
}
|
||||
lockMode();
|
||||
_inPlatformCreate = true;
|
||||
try {
|
||||
_platform = injector.get(PlatformRef);
|
||||
} finally {
|
||||
_inPlatformCreate = false;
|
||||
}
|
||||
return _platform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that there currently is a platform
|
||||
* which contains the given token as a provider.
|
||||
*/
|
||||
export function assertPlatform(requiredToken: any): PlatformRef {
|
||||
var platform = getPlatform();
|
||||
if (isBlank(platform)) {
|
||||
throw new BaseException('Not platform exists!');
|
||||
}
|
||||
if (isPresent(platform) && isBlank(platform.injector.get(requiredToken, null))) {
|
||||
throw new BaseException(
|
||||
'A platform with a different configuration has been created. Please destroy it first.');
|
||||
}
|
||||
return platform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose the existing platform.
|
||||
*/
|
||||
export function disposePlatform(): void {
|
||||
if (isPresent(_platform) && !_platform.disposed) {
|
||||
_platform.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current platform.
|
||||
*/
|
||||
export function getPlatform(): PlatformRef {
|
||||
return isPresent(_platform) && !_platform.disposed ? _platform : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for ApplicationRef.bootstrap.
|
||||
* Requires a platform the be created first.
|
||||
*/
|
||||
export function coreBootstrap<C>(injector: Injector,
|
||||
componentFactory: ComponentFactory<C>): ComponentRef<C> {
|
||||
var appRef: ApplicationRef = injector.get(ApplicationRef);
|
||||
return appRef.bootstrap(componentFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the componentFactory for the given component,
|
||||
* waits for asynchronous initializers and bootstraps the component.
|
||||
* Requires a platform the be created first.
|
||||
*/
|
||||
export function coreLoadAndBootstrap(injector: Injector,
|
||||
componentType: Type): Promise<ComponentRef<any>> {
|
||||
var appRef: ApplicationRef = injector.get(ApplicationRef);
|
||||
return appRef.run(() => {
|
||||
var componentResolver: ComponentResolver = injector.get(ComponentResolver);
|
||||
return PromiseWrapper
|
||||
.all([componentResolver.resolveComponent(componentType), appRef.waitForAsyncInitializers()])
|
||||
.then((arr) => appRef.bootstrap(arr[0]));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The Angular platform is the entry point for Angular on a web page. Each page
|
||||
* has exactly one platform, and services (such as reflection) which are common
|
||||
* to every Angular application running on the page are bound in its scope.
|
||||
*
|
||||
* A page's platform is initialized implicitly when {@link bootstrap}() is called, or
|
||||
* explicitly by calling {@link createPlatform}().
|
||||
*/
|
||||
export abstract class PlatformRef {
|
||||
/**
|
||||
* Register a listener to be called when the platform is disposed.
|
||||
*/
|
||||
abstract registerDisposeListener(dispose: () => void): void;
|
||||
|
||||
/**
|
||||
* Retrieve the platform {@link Injector}, which is the parent injector for
|
||||
* every Angular application on the page and provides singleton providers.
|
||||
*/
|
||||
get injector(): Injector { throw unimplemented(); };
|
||||
|
||||
/**
|
||||
* Destroy the Angular platform and all Angular applications on the page.
|
||||
*/
|
||||
abstract dispose(): void;
|
||||
|
||||
get disposed(): boolean { throw unimplemented(); }
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class PlatformRef_ extends PlatformRef {
|
||||
/** @internal */
|
||||
_applications: ApplicationRef[] = [];
|
||||
/** @internal */
|
||||
_disposeListeners: Function[] = [];
|
||||
|
||||
private _disposed: boolean = false;
|
||||
|
||||
constructor(private _injector: Injector) {
|
||||
super();
|
||||
if (!_inPlatformCreate) {
|
||||
throw new BaseException('Platforms have to be created via `createPlatform`!');
|
||||
}
|
||||
let inits: Function[] = <Function[]>_injector.get(PLATFORM_INITIALIZER, null);
|
||||
if (isPresent(inits)) inits.forEach(init => init());
|
||||
}
|
||||
|
||||
registerDisposeListener(dispose: () => void): void { this._disposeListeners.push(dispose); }
|
||||
|
||||
get injector(): Injector { return this._injector; }
|
||||
|
||||
get disposed() { return this._disposed; }
|
||||
|
||||
addApplication(appRef: ApplicationRef) { this._applications.push(appRef); }
|
||||
|
||||
dispose(): void {
|
||||
ListWrapper.clone(this._applications).forEach((app) => app.dispose());
|
||||
this._disposeListeners.forEach((dispose) => dispose());
|
||||
this._disposed = true;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_applicationDisposed(app: ApplicationRef): void { ListWrapper.remove(this._applications, app); }
|
||||
}
|
||||
|
||||
/**
|
||||
* A reference to an Angular application running on a page.
|
||||
*
|
||||
* For more about Angular applications, see the documentation for {@link bootstrap}.
|
||||
*/
|
||||
export abstract class ApplicationRef {
|
||||
/**
|
||||
* Register a listener to be called each time `bootstrap()` is called to bootstrap
|
||||
* a new root component.
|
||||
*/
|
||||
abstract registerBootstrapListener(listener: (ref: ComponentRef<any>) => void): void;
|
||||
|
||||
/**
|
||||
* Register a listener to be called when the application is disposed.
|
||||
*/
|
||||
abstract registerDisposeListener(dispose: () => void): void;
|
||||
|
||||
/**
|
||||
* Returns a promise that resolves when all asynchronous application initializers
|
||||
* are done.
|
||||
*/
|
||||
abstract waitForAsyncInitializers(): Promise<any>;
|
||||
|
||||
/**
|
||||
* Runs the given callback in the zone and returns the result of the callback.
|
||||
* Exceptions will be forwarded to the ExceptionHandler and rethrown.
|
||||
*/
|
||||
abstract run(callback: Function): any;
|
||||
|
||||
/**
|
||||
* Bootstrap a new component at the root level of the application.
|
||||
*
|
||||
* ### Bootstrap process
|
||||
*
|
||||
* When bootstrapping a new root component into an application, Angular mounts the
|
||||
* specified application component onto DOM elements identified by the [componentType]'s
|
||||
* selector and kicks off automatic change detection to finish initializing the component.
|
||||
*
|
||||
* ### Example
|
||||
* {@example core/ts/platform/platform.ts region='longform'}
|
||||
*/
|
||||
abstract bootstrap<C>(componentFactory: ComponentFactory<C>): ComponentRef<C>;
|
||||
|
||||
/**
|
||||
* Retrieve the application {@link Injector}.
|
||||
*/
|
||||
get injector(): Injector { return <Injector>unimplemented(); };
|
||||
|
||||
/**
|
||||
* Retrieve the application {@link NgZone}.
|
||||
*/
|
||||
get zone(): NgZone { return <NgZone>unimplemented(); };
|
||||
|
||||
/**
|
||||
* Dispose of this application and all of its components.
|
||||
*/
|
||||
abstract dispose(): void;
|
||||
|
||||
/**
|
||||
* Invoke this method to explicitly process change detection and its side-effects.
|
||||
*
|
||||
* In development mode, `tick()` also performs a second change detection cycle to ensure that no
|
||||
* further changes are detected. If additional changes are picked up during this second cycle,
|
||||
* bindings in the app have side-effects that cannot be resolved in a single change detection
|
||||
* pass.
|
||||
* In this case, Angular throws an error, since an Angular application can only have one change
|
||||
* detection pass during which all change detection must complete.
|
||||
*/
|
||||
abstract tick(): void;
|
||||
|
||||
/**
|
||||
* Get a list of component types registered to this application.
|
||||
*/
|
||||
get componentTypes(): Type[] { return <Type[]>unimplemented(); };
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ApplicationRef_ extends ApplicationRef {
|
||||
/** @internal */
|
||||
static _tickScope: WtfScopeFn = wtfCreateScope('ApplicationRef#tick()');
|
||||
|
||||
/** @internal */
|
||||
private _bootstrapListeners: Function[] = [];
|
||||
/** @internal */
|
||||
private _disposeListeners: Function[] = [];
|
||||
/** @internal */
|
||||
private _rootComponents: ComponentRef<any>[] = [];
|
||||
/** @internal */
|
||||
private _rootComponentTypes: Type[] = [];
|
||||
/** @internal */
|
||||
private _changeDetectorRefs: ChangeDetectorRef[] = [];
|
||||
/** @internal */
|
||||
private _runningTick: boolean = false;
|
||||
/** @internal */
|
||||
private _enforceNoNewChanges: boolean = false;
|
||||
|
||||
private _exceptionHandler: ExceptionHandler;
|
||||
|
||||
private _asyncInitDonePromise: Promise<any>;
|
||||
private _asyncInitDone: boolean;
|
||||
|
||||
constructor(private _platform: PlatformRef_, private _zone: NgZone, private _injector: Injector) {
|
||||
super();
|
||||
var zone: NgZone = _injector.get(NgZone);
|
||||
this._enforceNoNewChanges = assertionsEnabled();
|
||||
zone.run(() => { this._exceptionHandler = _injector.get(ExceptionHandler); });
|
||||
this._asyncInitDonePromise = this.run(() => {
|
||||
let inits: Function[] = _injector.get(APP_INITIALIZER, null);
|
||||
var asyncInitResults = [];
|
||||
var asyncInitDonePromise;
|
||||
if (isPresent(inits)) {
|
||||
for (var i = 0; i < inits.length; i++) {
|
||||
var initResult = inits[i]();
|
||||
if (isPromise(initResult)) {
|
||||
asyncInitResults.push(initResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (asyncInitResults.length > 0) {
|
||||
asyncInitDonePromise =
|
||||
PromiseWrapper.all(asyncInitResults).then((_) => this._asyncInitDone = true);
|
||||
this._asyncInitDone = false;
|
||||
} else {
|
||||
this._asyncInitDone = true;
|
||||
asyncInitDonePromise = PromiseWrapper.resolve(true);
|
||||
}
|
||||
return asyncInitDonePromise;
|
||||
});
|
||||
ObservableWrapper.subscribe(zone.onError, (error: NgZoneError) => {
|
||||
this._exceptionHandler.call(error.error, error.stackTrace);
|
||||
});
|
||||
ObservableWrapper.subscribe(this._zone.onMicrotaskEmpty,
|
||||
(_) => { this._zone.run(() => { this.tick(); }); });
|
||||
}
|
||||
|
||||
registerBootstrapListener(listener: (ref: ComponentRef<any>) => void): void {
|
||||
this._bootstrapListeners.push(listener);
|
||||
}
|
||||
|
||||
registerDisposeListener(dispose: () => void): void { this._disposeListeners.push(dispose); }
|
||||
|
||||
registerChangeDetector(changeDetector: ChangeDetectorRef): void {
|
||||
this._changeDetectorRefs.push(changeDetector);
|
||||
}
|
||||
|
||||
unregisterChangeDetector(changeDetector: ChangeDetectorRef): void {
|
||||
ListWrapper.remove(this._changeDetectorRefs, changeDetector);
|
||||
}
|
||||
|
||||
waitForAsyncInitializers(): Promise<any> { return this._asyncInitDonePromise; }
|
||||
|
||||
run(callback: Function): any {
|
||||
var zone = this.injector.get(NgZone);
|
||||
var result;
|
||||
// Note: Don't use zone.runGuarded as we want to know about
|
||||
// the thrown exception!
|
||||
// Note: the completer needs to be created outside
|
||||
// of `zone.run` as Dart swallows rejected promises
|
||||
// via the onError callback of the promise.
|
||||
var completer = PromiseWrapper.completer();
|
||||
zone.run(() => {
|
||||
try {
|
||||
result = callback();
|
||||
if (isPromise(result)) {
|
||||
PromiseWrapper.then(result, (ref) => { completer.resolve(ref); }, (err, stackTrace) => {
|
||||
completer.reject(err, stackTrace);
|
||||
this._exceptionHandler.call(err, stackTrace);
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
this._exceptionHandler.call(e, e.stack);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
return isPromise(result) ? completer.promise : result;
|
||||
}
|
||||
|
||||
bootstrap<C>(componentFactory: ComponentFactory<C>): ComponentRef<C> {
|
||||
if (!this._asyncInitDone) {
|
||||
throw new BaseException(
|
||||
'Cannot bootstrap as there are still asynchronous initializers running. Wait for them using waitForAsyncInitializers().');
|
||||
}
|
||||
return this.run(() => {
|
||||
this._rootComponentTypes.push(componentFactory.componentType);
|
||||
var compRef = componentFactory.create(this._injector, [], componentFactory.selector);
|
||||
compRef.onDestroy(() => { this._unloadComponent(compRef); });
|
||||
var testability = compRef.injector.get(Testability, null);
|
||||
if (isPresent(testability)) {
|
||||
compRef.injector.get(TestabilityRegistry)
|
||||
.registerApplication(compRef.location.nativeElement, testability);
|
||||
}
|
||||
|
||||
this._loadComponent(compRef);
|
||||
let c: Console = this._injector.get(Console);
|
||||
if (assertionsEnabled()) {
|
||||
c.log(
|
||||
"Angular 2 is running in the development mode. Call enableProdMode() to enable the production mode.");
|
||||
}
|
||||
return compRef;
|
||||
});
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_loadComponent(componentRef: ComponentRef<any>): void {
|
||||
this._changeDetectorRefs.push(componentRef.changeDetectorRef);
|
||||
this.tick();
|
||||
this._rootComponents.push(componentRef);
|
||||
this._bootstrapListeners.forEach((listener) => listener(componentRef));
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_unloadComponent(componentRef: ComponentRef<any>): void {
|
||||
if (!ListWrapper.contains(this._rootComponents, componentRef)) {
|
||||
return;
|
||||
}
|
||||
this.unregisterChangeDetector(componentRef.changeDetectorRef);
|
||||
ListWrapper.remove(this._rootComponents, componentRef);
|
||||
}
|
||||
|
||||
get injector(): Injector { return this._injector; }
|
||||
|
||||
get zone(): NgZone { return this._zone; }
|
||||
|
||||
tick(): void {
|
||||
if (this._runningTick) {
|
||||
throw new BaseException("ApplicationRef.tick is called recursively");
|
||||
}
|
||||
|
||||
var s = ApplicationRef_._tickScope();
|
||||
try {
|
||||
this._runningTick = true;
|
||||
this._changeDetectorRefs.forEach((detector) => detector.detectChanges());
|
||||
if (this._enforceNoNewChanges) {
|
||||
this._changeDetectorRefs.forEach((detector) => detector.checkNoChanges());
|
||||
}
|
||||
} finally {
|
||||
this._runningTick = false;
|
||||
wtfLeave(s);
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
// TODO(alxhub): Dispose of the NgZone.
|
||||
ListWrapper.clone(this._rootComponents).forEach((ref) => ref.destroy());
|
||||
this._disposeListeners.forEach((dispose) => dispose());
|
||||
this._platform._applicationDisposed(this);
|
||||
}
|
||||
|
||||
get componentTypes(): Type[] { return this._rootComponentTypes; }
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const PLATFORM_CORE_PROVIDERS =
|
||||
/*@ts2dart_const*/[
|
||||
PlatformRef_,
|
||||
/*@ts2dart_const*/ (
|
||||
/* @ts2dart_Provider */ {provide: PlatformRef, useExisting: PlatformRef_})
|
||||
];
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const APPLICATION_CORE_PROVIDERS = /*@ts2dart_const*/[
|
||||
/* @ts2dart_Provider */ {provide: NgZone, useFactory: createNgZone, deps: []},
|
||||
ApplicationRef_,
|
||||
/* @ts2dart_Provider */ {provide: ApplicationRef, useExisting: ApplicationRef_}
|
||||
];
|
49
modules/@angular/core/src/application_tokens.ts
Normal file
49
modules/@angular/core/src/application_tokens.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import {OpaqueToken, Provider} from 'angular2/src/core/di';
|
||||
import {Math, StringWrapper} from 'angular2/src/facade/lang';
|
||||
|
||||
/**
|
||||
* A DI Token representing a unique string id assigned to the application by Angular and used
|
||||
* primarily for prefixing application attributes and CSS styles when
|
||||
* {@link ViewEncapsulation#Emulated} is being used.
|
||||
*
|
||||
* If you need to avoid randomly generated value to be used as an application id, you can provide
|
||||
* a custom value via a DI provider <!-- TODO: provider --> configuring the root {@link Injector}
|
||||
* using this token.
|
||||
*/
|
||||
export const APP_ID: any = /*@ts2dart_const*/ new OpaqueToken('AppId');
|
||||
|
||||
function _appIdRandomProviderFactory() {
|
||||
return `${_randomChar()}${_randomChar()}${_randomChar()}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Providers that will generate a random APP_ID_TOKEN.
|
||||
*/
|
||||
export const APP_ID_RANDOM_PROVIDER =
|
||||
/*@ts2dart_const*/ /* @ts2dart_Provider */ {
|
||||
provide: APP_ID,
|
||||
useFactory: _appIdRandomProviderFactory,
|
||||
deps: []
|
||||
};
|
||||
|
||||
function _randomChar(): string {
|
||||
return StringWrapper.fromCharCode(97 + Math.floor(Math.random() * 25));
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that will be executed when a platform is initialized.
|
||||
*/
|
||||
export const PLATFORM_INITIALIZER: any =
|
||||
/*@ts2dart_const*/ new OpaqueToken("Platform Initializer");
|
||||
|
||||
/**
|
||||
* A function that will be executed when an application is initialized.
|
||||
*/
|
||||
export const APP_INITIALIZER: any =
|
||||
/*@ts2dart_const*/ new OpaqueToken("Application Initializer");
|
||||
|
||||
/**
|
||||
* A token which indicates the root directory of the application
|
||||
*/
|
||||
export const PACKAGE_ROOT_URL: any =
|
||||
/*@ts2dart_const*/ new OpaqueToken("Application Packages Root URL");
|
24
modules/@angular/core/src/change_detection.ts
Normal file
24
modules/@angular/core/src/change_detection.ts
Normal file
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
* Change detection enables data binding in Angular.
|
||||
*/
|
||||
|
||||
export {
|
||||
ChangeDetectionStrategy,
|
||||
|
||||
ChangeDetectorRef,
|
||||
|
||||
WrappedValue,
|
||||
SimpleChange,
|
||||
PipeTransform,
|
||||
IterableDiffers,
|
||||
IterableDiffer,
|
||||
IterableDifferFactory,
|
||||
KeyValueDiffers,
|
||||
KeyValueDiffer,
|
||||
KeyValueDifferFactory,
|
||||
CollectionChangeRecord,
|
||||
KeyValueChangeRecord,
|
||||
TrackByFn
|
||||
} from './change_detection/change_detection';
|
@ -0,0 +1,58 @@
|
||||
import {IterableDiffers, IterableDifferFactory} from './differs/iterable_differs';
|
||||
import {DefaultIterableDifferFactory} from './differs/default_iterable_differ';
|
||||
import {KeyValueDiffers, KeyValueDifferFactory} from './differs/keyvalue_differs';
|
||||
import {
|
||||
DefaultKeyValueDifferFactory,
|
||||
KeyValueChangeRecord
|
||||
} from './differs/default_keyvalue_differ';
|
||||
|
||||
export {
|
||||
DefaultKeyValueDifferFactory,
|
||||
KeyValueChangeRecord
|
||||
} from './differs/default_keyvalue_differ';
|
||||
export {
|
||||
DefaultIterableDifferFactory,
|
||||
CollectionChangeRecord
|
||||
} from './differs/default_iterable_differ';
|
||||
|
||||
export {
|
||||
ChangeDetectionStrategy,
|
||||
CHANGE_DETECTION_STRATEGY_VALUES,
|
||||
ChangeDetectorState,
|
||||
CHANGE_DETECTOR_STATE_VALUES,
|
||||
isDefaultChangeDetectionStrategy
|
||||
} from './constants';
|
||||
export {ChangeDetectorRef} from './change_detector_ref';
|
||||
export {
|
||||
IterableDiffers,
|
||||
IterableDiffer,
|
||||
IterableDifferFactory,
|
||||
TrackByFn
|
||||
} from './differs/iterable_differs';
|
||||
export {KeyValueDiffers, KeyValueDiffer, KeyValueDifferFactory} from './differs/keyvalue_differs';
|
||||
export {PipeTransform} from './pipe_transform';
|
||||
|
||||
export {
|
||||
WrappedValue,
|
||||
ValueUnwrapper,
|
||||
SimpleChange,
|
||||
devModeEqual,
|
||||
looseIdentical,
|
||||
uninitialized
|
||||
} from './change_detection_util';
|
||||
|
||||
/**
|
||||
* Structural diffing for `Object`s and `Map`s.
|
||||
*/
|
||||
export const keyValDiff: KeyValueDifferFactory[] =
|
||||
/*@ts2dart_const*/[new DefaultKeyValueDifferFactory()];
|
||||
|
||||
/**
|
||||
* Structural diffing for `Iterable` types such as `Array`s.
|
||||
*/
|
||||
export const iterableDiff: IterableDifferFactory[] =
|
||||
/*@ts2dart_const*/[new DefaultIterableDifferFactory()];
|
||||
|
||||
export const defaultIterableDiffers = /*@ts2dart_const*/ new IterableDiffers(iterableDiff);
|
||||
|
||||
export const defaultKeyValueDiffers = /*@ts2dart_const*/ new KeyValueDiffers(keyValDiff);
|
@ -0,0 +1,75 @@
|
||||
import {isBlank, looseIdentical, isPrimitive} from 'angular2/src/facade/lang';
|
||||
import {
|
||||
StringMapWrapper,
|
||||
isListLikeIterable,
|
||||
areIterablesEqual
|
||||
} from 'angular2/src/facade/collection';
|
||||
|
||||
export {looseIdentical} from 'angular2/src/facade/lang';
|
||||
export var uninitialized: Object = /*@ts2dart_const*/ new Object();
|
||||
|
||||
export function devModeEqual(a: any, b: any): boolean {
|
||||
if (isListLikeIterable(a) && isListLikeIterable(b)) {
|
||||
return areIterablesEqual(a, b, devModeEqual);
|
||||
|
||||
} else if (!isListLikeIterable(a) && !isPrimitive(a) && !isListLikeIterable(b) &&
|
||||
!isPrimitive(b)) {
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return looseIdentical(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that the result of a {@link PipeMetadata} transformation has changed even though the
|
||||
* reference
|
||||
* has not changed.
|
||||
*
|
||||
* The wrapped value will be unwrapped by change detection, and the unwrapped value will be stored.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* if (this._latestValue === this._latestReturnedValue) {
|
||||
* return this._latestReturnedValue;
|
||||
* } else {
|
||||
* this._latestReturnedValue = this._latestValue;
|
||||
* return WrappedValue.wrap(this._latestValue); // this will force update
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export class WrappedValue {
|
||||
constructor(public wrapped: any) {}
|
||||
|
||||
static wrap(value: any): WrappedValue { return new WrappedValue(value); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for unwrapping WrappedValue s
|
||||
*/
|
||||
export class ValueUnwrapper {
|
||||
public hasWrappedValue = false;
|
||||
|
||||
unwrap(value: any): any {
|
||||
if (value instanceof WrappedValue) {
|
||||
this.hasWrappedValue = true;
|
||||
return value.wrapped;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
reset() { this.hasWrappedValue = false; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a basic change from a previous to a new value.
|
||||
*/
|
||||
export class SimpleChange {
|
||||
constructor(public previousValue: any, public currentValue: any) {}
|
||||
|
||||
/**
|
||||
* Check whether the new value is the first value assigned.
|
||||
*/
|
||||
isFirstChange(): boolean { return this.previousValue === uninitialized; }
|
||||
}
|
@ -0,0 +1,192 @@
|
||||
export abstract class ChangeDetectorRef {
|
||||
/**
|
||||
* Marks all {@link ChangeDetectionStrategy#OnPush} ancestors as to be checked.
|
||||
*
|
||||
* <!-- TODO: Add a link to a chapter on OnPush components -->
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/GC512b?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: 'cmp',
|
||||
* changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
* template: `Number of ticks: {{numberOfTicks}}`
|
||||
* })
|
||||
* class Cmp {
|
||||
* numberOfTicks = 0;
|
||||
*
|
||||
* constructor(ref: ChangeDetectorRef) {
|
||||
* setInterval(() => {
|
||||
* this.numberOfTicks ++
|
||||
* // the following is required, otherwise the view will not be updated
|
||||
* this.ref.markForCheck();
|
||||
* }, 1000);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
* template: `
|
||||
* <cmp><cmp>
|
||||
* `,
|
||||
* directives: [Cmp]
|
||||
* })
|
||||
* class App {
|
||||
* }
|
||||
*
|
||||
* bootstrap(App);
|
||||
* ```
|
||||
*/
|
||||
abstract markForCheck(): void;
|
||||
|
||||
/**
|
||||
* Detaches the change detector from the change detector tree.
|
||||
*
|
||||
* The detached change detector will not be checked until it is reattached.
|
||||
*
|
||||
* This can also be used in combination with {@link ChangeDetectorRef#detectChanges} to implement
|
||||
* local change
|
||||
* detection checks.
|
||||
*
|
||||
* <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
|
||||
* <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* The following example defines a component with a large list of readonly data.
|
||||
* Imagine the data changes constantly, many times per second. For performance reasons,
|
||||
* we want to check and update the list every five seconds. We can do that by detaching
|
||||
* the component's change detector and doing a local check every five seconds.
|
||||
*
|
||||
* ```typescript
|
||||
* class DataProvider {
|
||||
* // in a real application the returned data will be different every time
|
||||
* get data() {
|
||||
* return [1,2,3,4,5];
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'giant-list',
|
||||
* template: `
|
||||
* <li *ngFor="let d of dataProvider.data">Data {{d}}</lig>
|
||||
* `,
|
||||
* directives: [NgFor]
|
||||
* })
|
||||
* class GiantList {
|
||||
* constructor(private ref: ChangeDetectorRef, private dataProvider:DataProvider) {
|
||||
* ref.detach();
|
||||
* setInterval(() => {
|
||||
* this.ref.detectChanges();
|
||||
* }, 5000);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* providers: [DataProvider],
|
||||
* template: `
|
||||
* <giant-list><giant-list>
|
||||
* `,
|
||||
* directives: [GiantList]
|
||||
* })
|
||||
* class App {
|
||||
* }
|
||||
*
|
||||
* bootstrap(App);
|
||||
* ```
|
||||
*/
|
||||
abstract detach(): void;
|
||||
|
||||
/**
|
||||
* Checks the change detector and its children.
|
||||
*
|
||||
* This can also be used in combination with {@link ChangeDetectorRef#detach} to implement local
|
||||
* change detection
|
||||
* checks.
|
||||
*
|
||||
* <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
|
||||
* <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* The following example defines a component with a large list of readonly data.
|
||||
* Imagine, the data changes constantly, many times per second. For performance reasons,
|
||||
* we want to check and update the list every five seconds.
|
||||
*
|
||||
* We can do that by detaching the component's change detector and doing a local change detection
|
||||
* check
|
||||
* every five seconds.
|
||||
*
|
||||
* See {@link ChangeDetectorRef#detach} for more information.
|
||||
*/
|
||||
abstract detectChanges(): void;
|
||||
|
||||
/**
|
||||
* Checks the change detector and its children, and throws if any changes are detected.
|
||||
*
|
||||
* This is used in development mode to verify that running change detection doesn't introduce
|
||||
* other changes.
|
||||
*/
|
||||
abstract checkNoChanges(): void;
|
||||
|
||||
/**
|
||||
* Reattach the change detector to the change detector tree.
|
||||
*
|
||||
* This also marks OnPush ancestors as to be checked. This reattached change detector will be
|
||||
* checked during the next change detection run.
|
||||
*
|
||||
* <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/aUhZha?p=preview))
|
||||
*
|
||||
* The following example creates a component displaying `live` data. The component will detach
|
||||
* its change detector from the main change detector tree when the component's live property
|
||||
* is set to false.
|
||||
*
|
||||
* ```typescript
|
||||
* class DataProvider {
|
||||
* data = 1;
|
||||
*
|
||||
* constructor() {
|
||||
* setInterval(() => {
|
||||
* this.data = this.data * 2;
|
||||
* }, 500);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'live-data',
|
||||
* inputs: ['live'],
|
||||
* template: `Data: {{dataProvider.data}}`
|
||||
* })
|
||||
* class LiveData {
|
||||
* constructor(private ref: ChangeDetectorRef, private dataProvider:DataProvider) {}
|
||||
*
|
||||
* set live(value) {
|
||||
* if (value)
|
||||
* this.ref.reattach();
|
||||
* else
|
||||
* this.ref.detach();
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* providers: [DataProvider],
|
||||
* template: `
|
||||
* Live Update: <input type="checkbox" [(ngModel)]="live">
|
||||
* <live-data [live]="live"><live-data>
|
||||
* `,
|
||||
* directives: [LiveData, FORM_DIRECTIVES]
|
||||
* })
|
||||
* class App {
|
||||
* live = true;
|
||||
* }
|
||||
*
|
||||
* bootstrap(App);
|
||||
* ```
|
||||
*/
|
||||
abstract reattach(): void;
|
||||
}
|
93
modules/@angular/core/src/change_detection/constants.ts
Normal file
93
modules/@angular/core/src/change_detection/constants.ts
Normal file
@ -0,0 +1,93 @@
|
||||
import {StringWrapper, normalizeBool, isBlank} from 'angular2/src/facade/lang';
|
||||
|
||||
/**
|
||||
* Describes the current state of the change detector.
|
||||
*/
|
||||
export enum ChangeDetectorState {
|
||||
/**
|
||||
* `NeverChecked` means that the change detector has not been checked yet, and
|
||||
* initialization methods should be called during detection.
|
||||
*/
|
||||
NeverChecked,
|
||||
|
||||
/**
|
||||
* `CheckedBefore` means that the change detector has successfully completed at least
|
||||
* one detection previously.
|
||||
*/
|
||||
CheckedBefore,
|
||||
|
||||
/**
|
||||
* `Errored` means that the change detector encountered an error checking a binding
|
||||
* or calling a directive lifecycle method and is now in an inconsistent state. Change
|
||||
* detectors in this state will no longer detect changes.
|
||||
*/
|
||||
Errored
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Describes within the change detector which strategy will be used the next time change
|
||||
* detection is triggered.
|
||||
*/
|
||||
export enum ChangeDetectionStrategy {
|
||||
/**
|
||||
* `CheckedOnce` means that after calling detectChanges the mode of the change detector
|
||||
* will become `Checked`.
|
||||
*/
|
||||
CheckOnce,
|
||||
|
||||
/**
|
||||
* `Checked` means that the change detector should be skipped until its mode changes to
|
||||
* `CheckOnce`.
|
||||
*/
|
||||
Checked,
|
||||
|
||||
/**
|
||||
* `CheckAlways` means that after calling detectChanges the mode of the change detector
|
||||
* will remain `CheckAlways`.
|
||||
*/
|
||||
CheckAlways,
|
||||
|
||||
/**
|
||||
* `Detached` means that the change detector sub tree is not a part of the main tree and
|
||||
* should be skipped.
|
||||
*/
|
||||
Detached,
|
||||
|
||||
/**
|
||||
* `OnPush` means that the change detector's mode will be set to `CheckOnce` during hydration.
|
||||
*/
|
||||
OnPush,
|
||||
|
||||
/**
|
||||
* `Default` means that the change detector's mode will be set to `CheckAlways` during hydration.
|
||||
*/
|
||||
Default,
|
||||
}
|
||||
|
||||
/**
|
||||
* List of possible {@link ChangeDetectionStrategy} values.
|
||||
*/
|
||||
export var CHANGE_DETECTION_STRATEGY_VALUES = [
|
||||
ChangeDetectionStrategy.CheckOnce,
|
||||
ChangeDetectionStrategy.Checked,
|
||||
ChangeDetectionStrategy.CheckAlways,
|
||||
ChangeDetectionStrategy.Detached,
|
||||
ChangeDetectionStrategy.OnPush,
|
||||
ChangeDetectionStrategy.Default
|
||||
];
|
||||
|
||||
/**
|
||||
* List of possible {@link ChangeDetectorState} values.
|
||||
*/
|
||||
export var CHANGE_DETECTOR_STATE_VALUES = [
|
||||
ChangeDetectorState.NeverChecked,
|
||||
ChangeDetectorState.CheckedBefore,
|
||||
ChangeDetectorState.Errored
|
||||
];
|
||||
|
||||
export function isDefaultChangeDetectionStrategy(
|
||||
changeDetectionStrategy: ChangeDetectionStrategy): boolean {
|
||||
return isBlank(changeDetectionStrategy) ||
|
||||
changeDetectionStrategy === ChangeDetectionStrategy.Default;
|
||||
}
|
@ -0,0 +1,694 @@
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {isListLikeIterable, iterateListLike, ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {
|
||||
isBlank,
|
||||
isPresent,
|
||||
stringify,
|
||||
getMapKey,
|
||||
looseIdentical,
|
||||
isArray
|
||||
} from 'angular2/src/facade/lang';
|
||||
|
||||
import {ChangeDetectorRef} from '../change_detector_ref';
|
||||
import {IterableDiffer, IterableDifferFactory, TrackByFn} from '../differs/iterable_differs';
|
||||
|
||||
/* @ts2dart_const */
|
||||
export class DefaultIterableDifferFactory implements IterableDifferFactory {
|
||||
constructor() {}
|
||||
supports(obj: Object): boolean { return isListLikeIterable(obj); }
|
||||
create(cdRef: ChangeDetectorRef, trackByFn?: TrackByFn): DefaultIterableDiffer {
|
||||
return new DefaultIterableDiffer(trackByFn);
|
||||
}
|
||||
}
|
||||
|
||||
var trackByIdentity = (index: number, item: any) => item;
|
||||
|
||||
export class DefaultIterableDiffer implements IterableDiffer {
|
||||
private _length: number = null;
|
||||
private _collection = null;
|
||||
// Keeps track of the used records at any point in time (during & across `_check()` calls)
|
||||
private _linkedRecords: _DuplicateMap = null;
|
||||
// Keeps track of the removed records at any point in time during `_check()` calls.
|
||||
private _unlinkedRecords: _DuplicateMap = null;
|
||||
private _previousItHead: CollectionChangeRecord = null;
|
||||
private _itHead: CollectionChangeRecord = null;
|
||||
private _itTail: CollectionChangeRecord = null;
|
||||
private _additionsHead: CollectionChangeRecord = null;
|
||||
private _additionsTail: CollectionChangeRecord = null;
|
||||
private _movesHead: CollectionChangeRecord = null;
|
||||
private _movesTail: CollectionChangeRecord = null;
|
||||
private _removalsHead: CollectionChangeRecord = null;
|
||||
private _removalsTail: CollectionChangeRecord = null;
|
||||
// Keeps track of records where custom track by is the same, but item identity has changed
|
||||
private _identityChangesHead: CollectionChangeRecord = null;
|
||||
private _identityChangesTail: CollectionChangeRecord = null;
|
||||
|
||||
constructor(private _trackByFn?: TrackByFn) {
|
||||
this._trackByFn = isPresent(this._trackByFn) ? this._trackByFn : trackByIdentity;
|
||||
}
|
||||
|
||||
get collection() { return this._collection; }
|
||||
|
||||
get length(): number { return this._length; }
|
||||
|
||||
forEachItem(fn: Function) {
|
||||
var record: CollectionChangeRecord;
|
||||
for (record = this._itHead; record !== null; record = record._next) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachPreviousItem(fn: Function) {
|
||||
var record: CollectionChangeRecord;
|
||||
for (record = this._previousItHead; record !== null; record = record._nextPrevious) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachAddedItem(fn: Function) {
|
||||
var record: CollectionChangeRecord;
|
||||
for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachMovedItem(fn: Function) {
|
||||
var record: CollectionChangeRecord;
|
||||
for (record = this._movesHead; record !== null; record = record._nextMoved) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachRemovedItem(fn: Function) {
|
||||
var record: CollectionChangeRecord;
|
||||
for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachIdentityChange(fn: Function) {
|
||||
var record: CollectionChangeRecord;
|
||||
for (record = this._identityChangesHead; record !== null; record = record._nextIdentityChange) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
diff(collection: any): DefaultIterableDiffer {
|
||||
if (isBlank(collection)) collection = [];
|
||||
if (!isListLikeIterable(collection)) {
|
||||
throw new BaseException(`Error trying to diff '${collection}'`);
|
||||
}
|
||||
|
||||
if (this.check(collection)) {
|
||||
return this;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy() {}
|
||||
|
||||
// todo(vicb): optim for UnmodifiableListView (frozen arrays)
|
||||
check(collection: any): boolean {
|
||||
this._reset();
|
||||
|
||||
var record: CollectionChangeRecord = this._itHead;
|
||||
var mayBeDirty: boolean = false;
|
||||
var index: number;
|
||||
var item;
|
||||
var itemTrackBy;
|
||||
if (isArray(collection)) {
|
||||
var list = collection;
|
||||
this._length = collection.length;
|
||||
|
||||
for (index = 0; index < this._length; index++) {
|
||||
item = list[index];
|
||||
itemTrackBy = this._trackByFn(index, item);
|
||||
if (record === null || !looseIdentical(record.trackById, itemTrackBy)) {
|
||||
record = this._mismatch(record, item, itemTrackBy, index);
|
||||
mayBeDirty = true;
|
||||
} else {
|
||||
if (mayBeDirty) {
|
||||
// TODO(misko): can we limit this to duplicates only?
|
||||
record = this._verifyReinsertion(record, item, itemTrackBy, index);
|
||||
}
|
||||
if (!looseIdentical(record.item, item)) this._addIdentityChange(record, item);
|
||||
}
|
||||
|
||||
record = record._next;
|
||||
}
|
||||
} else {
|
||||
index = 0;
|
||||
iterateListLike(collection, (item) => {
|
||||
itemTrackBy = this._trackByFn(index, item);
|
||||
if (record === null || !looseIdentical(record.trackById, itemTrackBy)) {
|
||||
record = this._mismatch(record, item, itemTrackBy, index);
|
||||
mayBeDirty = true;
|
||||
} else {
|
||||
if (mayBeDirty) {
|
||||
// TODO(misko): can we limit this to duplicates only?
|
||||
record = this._verifyReinsertion(record, item, itemTrackBy, index);
|
||||
}
|
||||
if (!looseIdentical(record.item, item)) this._addIdentityChange(record, item);
|
||||
}
|
||||
record = record._next;
|
||||
index++;
|
||||
});
|
||||
this._length = index;
|
||||
}
|
||||
|
||||
this._truncate(record);
|
||||
this._collection = collection;
|
||||
return this.isDirty;
|
||||
}
|
||||
|
||||
/* CollectionChanges is considered dirty if it has any additions, moves, removals, or identity
|
||||
* changes.
|
||||
*/
|
||||
get isDirty(): boolean {
|
||||
return this._additionsHead !== null || this._movesHead !== null ||
|
||||
this._removalsHead !== null || this._identityChangesHead !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the state of the change objects to show no changes. This means set previousKey to
|
||||
* currentKey, and clear all of the queues (additions, moves, removals).
|
||||
* Set the previousIndexes of moved and added items to their currentIndexes
|
||||
* Reset the list of additions, moves and removals
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
_reset() {
|
||||
if (this.isDirty) {
|
||||
var record: CollectionChangeRecord;
|
||||
var nextRecord: CollectionChangeRecord;
|
||||
|
||||
for (record = this._previousItHead = this._itHead; record !== null; record = record._next) {
|
||||
record._nextPrevious = record._next;
|
||||
}
|
||||
|
||||
for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
||||
record.previousIndex = record.currentIndex;
|
||||
}
|
||||
this._additionsHead = this._additionsTail = null;
|
||||
|
||||
for (record = this._movesHead; record !== null; record = nextRecord) {
|
||||
record.previousIndex = record.currentIndex;
|
||||
nextRecord = record._nextMoved;
|
||||
}
|
||||
this._movesHead = this._movesTail = null;
|
||||
this._removalsHead = this._removalsTail = null;
|
||||
this._identityChangesHead = this._identityChangesTail = null;
|
||||
|
||||
// todo(vicb) when assert gets supported
|
||||
// assert(!this.isDirty);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the core function which handles differences between collections.
|
||||
*
|
||||
* - `record` is the record which we saw at this position last time. If null then it is a new
|
||||
* item.
|
||||
* - `item` is the current item in the collection
|
||||
* - `index` is the position of the item in the collection
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
_mismatch(record: CollectionChangeRecord, item: any, itemTrackBy: any,
|
||||
index: number): CollectionChangeRecord {
|
||||
// The previous record after which we will append the current one.
|
||||
var previousRecord: CollectionChangeRecord;
|
||||
|
||||
if (record === null) {
|
||||
previousRecord = this._itTail;
|
||||
} else {
|
||||
previousRecord = record._prev;
|
||||
// Remove the record from the collection since we know it does not match the item.
|
||||
this._remove(record);
|
||||
}
|
||||
|
||||
// Attempt to see if we have seen the item before.
|
||||
record = this._linkedRecords === null ? null : this._linkedRecords.get(itemTrackBy, index);
|
||||
if (record !== null) {
|
||||
// We have seen this before, we need to move it forward in the collection.
|
||||
// But first we need to check if identity changed, so we can update in view if necessary
|
||||
if (!looseIdentical(record.item, item)) this._addIdentityChange(record, item);
|
||||
|
||||
this._moveAfter(record, previousRecord, index);
|
||||
} else {
|
||||
// Never seen it, check evicted list.
|
||||
record = this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy);
|
||||
if (record !== null) {
|
||||
// It is an item which we have evicted earlier: reinsert it back into the list.
|
||||
// But first we need to check if identity changed, so we can update in view if necessary
|
||||
if (!looseIdentical(record.item, item)) this._addIdentityChange(record, item);
|
||||
|
||||
this._reinsertAfter(record, previousRecord, index);
|
||||
} else {
|
||||
// It is a new item: add it.
|
||||
record =
|
||||
this._addAfter(new CollectionChangeRecord(item, itemTrackBy), previousRecord, index);
|
||||
}
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
/**
|
||||
* This check is only needed if an array contains duplicates. (Short circuit of nothing dirty)
|
||||
*
|
||||
* Use case: `[a, a]` => `[b, a, a]`
|
||||
*
|
||||
* If we did not have this check then the insertion of `b` would:
|
||||
* 1) evict first `a`
|
||||
* 2) insert `b` at `0` index.
|
||||
* 3) leave `a` at index `1` as is. <-- this is wrong!
|
||||
* 3) reinsert `a` at index 2. <-- this is wrong!
|
||||
*
|
||||
* The correct behavior is:
|
||||
* 1) evict first `a`
|
||||
* 2) insert `b` at `0` index.
|
||||
* 3) reinsert `a` at index 1.
|
||||
* 3) move `a` at from `1` to `2`.
|
||||
*
|
||||
*
|
||||
* Double check that we have not evicted a duplicate item. We need to check if the item type may
|
||||
* have already been removed:
|
||||
* The insertion of b will evict the first 'a'. If we don't reinsert it now it will be reinserted
|
||||
* at the end. Which will show up as the two 'a's switching position. This is incorrect, since a
|
||||
* better way to think of it is as insert of 'b' rather then switch 'a' with 'b' and then add 'a'
|
||||
* at the end.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
_verifyReinsertion(record: CollectionChangeRecord, item: any, itemTrackBy: any,
|
||||
index: number): CollectionChangeRecord {
|
||||
var reinsertRecord: CollectionChangeRecord =
|
||||
this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy);
|
||||
if (reinsertRecord !== null) {
|
||||
record = this._reinsertAfter(reinsertRecord, record._prev, index);
|
||||
} else if (record.currentIndex != index) {
|
||||
record.currentIndex = index;
|
||||
this._addToMoves(record, index);
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rid of any excess {@link CollectionChangeRecord}s from the previous collection
|
||||
*
|
||||
* - `record` The first excess {@link CollectionChangeRecord}.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
_truncate(record: CollectionChangeRecord) {
|
||||
// Anything after that needs to be removed;
|
||||
while (record !== null) {
|
||||
var nextRecord: CollectionChangeRecord = record._next;
|
||||
this._addToRemovals(this._unlink(record));
|
||||
record = nextRecord;
|
||||
}
|
||||
if (this._unlinkedRecords !== null) {
|
||||
this._unlinkedRecords.clear();
|
||||
}
|
||||
|
||||
if (this._additionsTail !== null) {
|
||||
this._additionsTail._nextAdded = null;
|
||||
}
|
||||
if (this._movesTail !== null) {
|
||||
this._movesTail._nextMoved = null;
|
||||
}
|
||||
if (this._itTail !== null) {
|
||||
this._itTail._next = null;
|
||||
}
|
||||
if (this._removalsTail !== null) {
|
||||
this._removalsTail._nextRemoved = null;
|
||||
}
|
||||
if (this._identityChangesTail !== null) {
|
||||
this._identityChangesTail._nextIdentityChange = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_reinsertAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord,
|
||||
index: number): CollectionChangeRecord {
|
||||
if (this._unlinkedRecords !== null) {
|
||||
this._unlinkedRecords.remove(record);
|
||||
}
|
||||
var prev = record._prevRemoved;
|
||||
var next = record._nextRemoved;
|
||||
|
||||
if (prev === null) {
|
||||
this._removalsHead = next;
|
||||
} else {
|
||||
prev._nextRemoved = next;
|
||||
}
|
||||
if (next === null) {
|
||||
this._removalsTail = prev;
|
||||
} else {
|
||||
next._prevRemoved = prev;
|
||||
}
|
||||
|
||||
this._insertAfter(record, prevRecord, index);
|
||||
this._addToMoves(record, index);
|
||||
return record;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_moveAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord,
|
||||
index: number): CollectionChangeRecord {
|
||||
this._unlink(record);
|
||||
this._insertAfter(record, prevRecord, index);
|
||||
this._addToMoves(record, index);
|
||||
return record;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_addAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord,
|
||||
index: number): CollectionChangeRecord {
|
||||
this._insertAfter(record, prevRecord, index);
|
||||
|
||||
if (this._additionsTail === null) {
|
||||
// todo(vicb)
|
||||
// assert(this._additionsHead === null);
|
||||
this._additionsTail = this._additionsHead = record;
|
||||
} else {
|
||||
// todo(vicb)
|
||||
// assert(_additionsTail._nextAdded === null);
|
||||
// assert(record._nextAdded === null);
|
||||
this._additionsTail = this._additionsTail._nextAdded = record;
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_insertAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord,
|
||||
index: number): CollectionChangeRecord {
|
||||
// todo(vicb)
|
||||
// assert(record != prevRecord);
|
||||
// assert(record._next === null);
|
||||
// assert(record._prev === null);
|
||||
|
||||
var next: CollectionChangeRecord = prevRecord === null ? this._itHead : prevRecord._next;
|
||||
// todo(vicb)
|
||||
// assert(next != record);
|
||||
// assert(prevRecord != record);
|
||||
record._next = next;
|
||||
record._prev = prevRecord;
|
||||
if (next === null) {
|
||||
this._itTail = record;
|
||||
} else {
|
||||
next._prev = record;
|
||||
}
|
||||
if (prevRecord === null) {
|
||||
this._itHead = record;
|
||||
} else {
|
||||
prevRecord._next = record;
|
||||
}
|
||||
|
||||
if (this._linkedRecords === null) {
|
||||
this._linkedRecords = new _DuplicateMap();
|
||||
}
|
||||
this._linkedRecords.put(record);
|
||||
|
||||
record.currentIndex = index;
|
||||
return record;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_remove(record: CollectionChangeRecord): CollectionChangeRecord {
|
||||
return this._addToRemovals(this._unlink(record));
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_unlink(record: CollectionChangeRecord): CollectionChangeRecord {
|
||||
if (this._linkedRecords !== null) {
|
||||
this._linkedRecords.remove(record);
|
||||
}
|
||||
|
||||
var prev = record._prev;
|
||||
var next = record._next;
|
||||
|
||||
// todo(vicb)
|
||||
// assert((record._prev = null) === null);
|
||||
// assert((record._next = null) === null);
|
||||
|
||||
if (prev === null) {
|
||||
this._itHead = next;
|
||||
} else {
|
||||
prev._next = next;
|
||||
}
|
||||
if (next === null) {
|
||||
this._itTail = prev;
|
||||
} else {
|
||||
next._prev = prev;
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_addToMoves(record: CollectionChangeRecord, toIndex: number): CollectionChangeRecord {
|
||||
// todo(vicb)
|
||||
// assert(record._nextMoved === null);
|
||||
|
||||
if (record.previousIndex === toIndex) {
|
||||
return record;
|
||||
}
|
||||
|
||||
if (this._movesTail === null) {
|
||||
// todo(vicb)
|
||||
// assert(_movesHead === null);
|
||||
this._movesTail = this._movesHead = record;
|
||||
} else {
|
||||
// todo(vicb)
|
||||
// assert(_movesTail._nextMoved === null);
|
||||
this._movesTail = this._movesTail._nextMoved = record;
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_addToRemovals(record: CollectionChangeRecord): CollectionChangeRecord {
|
||||
if (this._unlinkedRecords === null) {
|
||||
this._unlinkedRecords = new _DuplicateMap();
|
||||
}
|
||||
this._unlinkedRecords.put(record);
|
||||
record.currentIndex = null;
|
||||
record._nextRemoved = null;
|
||||
|
||||
if (this._removalsTail === null) {
|
||||
// todo(vicb)
|
||||
// assert(_removalsHead === null);
|
||||
this._removalsTail = this._removalsHead = record;
|
||||
record._prevRemoved = null;
|
||||
} else {
|
||||
// todo(vicb)
|
||||
// assert(_removalsTail._nextRemoved === null);
|
||||
// assert(record._nextRemoved === null);
|
||||
record._prevRemoved = this._removalsTail;
|
||||
this._removalsTail = this._removalsTail._nextRemoved = record;
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_addIdentityChange(record: CollectionChangeRecord, item: any) {
|
||||
record.item = item;
|
||||
if (this._identityChangesTail === null) {
|
||||
this._identityChangesTail = this._identityChangesHead = record;
|
||||
} else {
|
||||
this._identityChangesTail = this._identityChangesTail._nextIdentityChange = record;
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
|
||||
toString(): string {
|
||||
var list = [];
|
||||
this.forEachItem((record) => list.push(record));
|
||||
|
||||
var previous = [];
|
||||
this.forEachPreviousItem((record) => previous.push(record));
|
||||
|
||||
var additions = [];
|
||||
this.forEachAddedItem((record) => additions.push(record));
|
||||
|
||||
var moves = [];
|
||||
this.forEachMovedItem((record) => moves.push(record));
|
||||
|
||||
var removals = [];
|
||||
this.forEachRemovedItem((record) => removals.push(record));
|
||||
|
||||
var identityChanges = [];
|
||||
this.forEachIdentityChange((record) => identityChanges.push(record));
|
||||
|
||||
return "collection: " + list.join(', ') + "\n" + "previous: " + previous.join(', ') + "\n" +
|
||||
"additions: " + additions.join(', ') + "\n" + "moves: " + moves.join(', ') + "\n" +
|
||||
"removals: " + removals.join(', ') + "\n" + "identityChanges: " +
|
||||
identityChanges.join(', ') + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
export class CollectionChangeRecord {
|
||||
currentIndex: number = null;
|
||||
previousIndex: number = null;
|
||||
|
||||
/** @internal */
|
||||
_nextPrevious: CollectionChangeRecord = null;
|
||||
/** @internal */
|
||||
_prev: CollectionChangeRecord = null;
|
||||
/** @internal */
|
||||
_next: CollectionChangeRecord = null;
|
||||
/** @internal */
|
||||
_prevDup: CollectionChangeRecord = null;
|
||||
/** @internal */
|
||||
_nextDup: CollectionChangeRecord = null;
|
||||
/** @internal */
|
||||
_prevRemoved: CollectionChangeRecord = null;
|
||||
/** @internal */
|
||||
_nextRemoved: CollectionChangeRecord = null;
|
||||
/** @internal */
|
||||
_nextAdded: CollectionChangeRecord = null;
|
||||
/** @internal */
|
||||
_nextMoved: CollectionChangeRecord = null;
|
||||
/** @internal */
|
||||
_nextIdentityChange: CollectionChangeRecord = null;
|
||||
|
||||
|
||||
constructor(public item: any, public trackById: any) {}
|
||||
|
||||
toString(): string {
|
||||
return this.previousIndex === this.currentIndex ?
|
||||
stringify(this.item) :
|
||||
stringify(this.item) + '[' + stringify(this.previousIndex) + '->' +
|
||||
stringify(this.currentIndex) + ']';
|
||||
}
|
||||
}
|
||||
|
||||
// A linked list of CollectionChangeRecords with the same CollectionChangeRecord.item
|
||||
class _DuplicateItemRecordList {
|
||||
/** @internal */
|
||||
_head: CollectionChangeRecord = null;
|
||||
/** @internal */
|
||||
_tail: CollectionChangeRecord = null;
|
||||
|
||||
/**
|
||||
* Append the record to the list of duplicates.
|
||||
*
|
||||
* Note: by design all records in the list of duplicates hold the same value in record.item.
|
||||
*/
|
||||
add(record: CollectionChangeRecord): void {
|
||||
if (this._head === null) {
|
||||
this._head = this._tail = record;
|
||||
record._nextDup = null;
|
||||
record._prevDup = null;
|
||||
} else {
|
||||
// todo(vicb)
|
||||
// assert(record.item == _head.item ||
|
||||
// record.item is num && record.item.isNaN && _head.item is num && _head.item.isNaN);
|
||||
this._tail._nextDup = record;
|
||||
record._prevDup = this._tail;
|
||||
record._nextDup = null;
|
||||
this._tail = record;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a CollectionChangeRecord having CollectionChangeRecord.trackById == trackById and
|
||||
// CollectionChangeRecord.currentIndex >= afterIndex
|
||||
get(trackById: any, afterIndex: number): CollectionChangeRecord {
|
||||
var record: CollectionChangeRecord;
|
||||
for (record = this._head; record !== null; record = record._nextDup) {
|
||||
if ((afterIndex === null || afterIndex < record.currentIndex) &&
|
||||
looseIdentical(record.trackById, trackById)) {
|
||||
return record;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove one {@link CollectionChangeRecord} from the list of duplicates.
|
||||
*
|
||||
* Returns whether the list of duplicates is empty.
|
||||
*/
|
||||
remove(record: CollectionChangeRecord): boolean {
|
||||
// todo(vicb)
|
||||
// assert(() {
|
||||
// // verify that the record being removed is in the list.
|
||||
// for (CollectionChangeRecord cursor = _head; cursor != null; cursor = cursor._nextDup) {
|
||||
// if (identical(cursor, record)) return true;
|
||||
// }
|
||||
// return false;
|
||||
//});
|
||||
|
||||
var prev: CollectionChangeRecord = record._prevDup;
|
||||
var next: CollectionChangeRecord = record._nextDup;
|
||||
if (prev === null) {
|
||||
this._head = next;
|
||||
} else {
|
||||
prev._nextDup = next;
|
||||
}
|
||||
if (next === null) {
|
||||
this._tail = prev;
|
||||
} else {
|
||||
next._prevDup = prev;
|
||||
}
|
||||
return this._head === null;
|
||||
}
|
||||
}
|
||||
|
||||
class _DuplicateMap {
|
||||
map = new Map<any, _DuplicateItemRecordList>();
|
||||
|
||||
put(record: CollectionChangeRecord) {
|
||||
// todo(vicb) handle corner cases
|
||||
var key = getMapKey(record.trackById);
|
||||
|
||||
var duplicates = this.map.get(key);
|
||||
if (!isPresent(duplicates)) {
|
||||
duplicates = new _DuplicateItemRecordList();
|
||||
this.map.set(key, duplicates);
|
||||
}
|
||||
duplicates.add(record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the `value` using key. Because the CollectionChangeRecord value may be one which we
|
||||
* have already iterated over, we use the afterIndex to pretend it is not there.
|
||||
*
|
||||
* Use case: `[a, b, c, a, a]` if we are at index `3` which is the second `a` then asking if we
|
||||
* have any more `a`s needs to return the last `a` not the first or second.
|
||||
*/
|
||||
get(trackById: any, afterIndex: number = null): CollectionChangeRecord {
|
||||
var key = getMapKey(trackById);
|
||||
|
||||
var recordList = this.map.get(key);
|
||||
return isBlank(recordList) ? null : recordList.get(trackById, afterIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a {@link CollectionChangeRecord} from the list of duplicates.
|
||||
*
|
||||
* The list of duplicates also is removed from the map if it gets empty.
|
||||
*/
|
||||
remove(record: CollectionChangeRecord): CollectionChangeRecord {
|
||||
var key = getMapKey(record.trackById);
|
||||
// todo(vicb)
|
||||
// assert(this.map.containsKey(key));
|
||||
var recordList: _DuplicateItemRecordList = this.map.get(key);
|
||||
// Remove the list of duplicates when it gets empty
|
||||
if (recordList.remove(record)) {
|
||||
this.map.delete(key);
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
get isEmpty(): boolean { return this.map.size === 0; }
|
||||
|
||||
clear() { this.map.clear(); }
|
||||
|
||||
toString(): string { return '_DuplicateMap(' + stringify(this.map) + ')'; }
|
||||
}
|
@ -0,0 +1,363 @@
|
||||
import {MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {stringify, looseIdentical, isJsObject, isBlank} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {ChangeDetectorRef} from '../change_detector_ref';
|
||||
import {KeyValueDiffer, KeyValueDifferFactory} from '../differs/keyvalue_differs';
|
||||
|
||||
/* @ts2dart_const */
|
||||
export class DefaultKeyValueDifferFactory implements KeyValueDifferFactory {
|
||||
constructor() {}
|
||||
supports(obj: any): boolean { return obj instanceof Map || isJsObject(obj); }
|
||||
|
||||
create(cdRef: ChangeDetectorRef): KeyValueDiffer { return new DefaultKeyValueDiffer(); }
|
||||
}
|
||||
|
||||
export class DefaultKeyValueDiffer implements KeyValueDiffer {
|
||||
private _records: Map<any, any> = new Map();
|
||||
private _mapHead: KeyValueChangeRecord = null;
|
||||
private _previousMapHead: KeyValueChangeRecord = null;
|
||||
private _changesHead: KeyValueChangeRecord = null;
|
||||
private _changesTail: KeyValueChangeRecord = null;
|
||||
private _additionsHead: KeyValueChangeRecord = null;
|
||||
private _additionsTail: KeyValueChangeRecord = null;
|
||||
private _removalsHead: KeyValueChangeRecord = null;
|
||||
private _removalsTail: KeyValueChangeRecord = null;
|
||||
|
||||
get isDirty(): boolean {
|
||||
return this._additionsHead !== null || this._changesHead !== null ||
|
||||
this._removalsHead !== null;
|
||||
}
|
||||
|
||||
forEachItem(fn: Function) {
|
||||
var record: KeyValueChangeRecord;
|
||||
for (record = this._mapHead; record !== null; record = record._next) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachPreviousItem(fn: Function) {
|
||||
var record: KeyValueChangeRecord;
|
||||
for (record = this._previousMapHead; record !== null; record = record._nextPrevious) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachChangedItem(fn: Function) {
|
||||
var record: KeyValueChangeRecord;
|
||||
for (record = this._changesHead; record !== null; record = record._nextChanged) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachAddedItem(fn: Function) {
|
||||
var record: KeyValueChangeRecord;
|
||||
for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
forEachRemovedItem(fn: Function) {
|
||||
var record: KeyValueChangeRecord;
|
||||
for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
|
||||
fn(record);
|
||||
}
|
||||
}
|
||||
|
||||
diff(map: Map<any, any>): any {
|
||||
if (isBlank(map)) map = MapWrapper.createFromPairs([]);
|
||||
if (!(map instanceof Map || isJsObject(map))) {
|
||||
throw new BaseException(`Error trying to diff '${map}'`);
|
||||
}
|
||||
|
||||
if (this.check(map)) {
|
||||
return this;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy() {}
|
||||
|
||||
check(map: Map<any, any>): boolean {
|
||||
this._reset();
|
||||
var records = this._records;
|
||||
var oldSeqRecord: KeyValueChangeRecord = this._mapHead;
|
||||
var lastOldSeqRecord: KeyValueChangeRecord = null;
|
||||
var lastNewSeqRecord: KeyValueChangeRecord = null;
|
||||
var seqChanged: boolean = false;
|
||||
|
||||
this._forEach(map, (value, key) => {
|
||||
var newSeqRecord;
|
||||
if (oldSeqRecord !== null && key === oldSeqRecord.key) {
|
||||
newSeqRecord = oldSeqRecord;
|
||||
if (!looseIdentical(value, oldSeqRecord.currentValue)) {
|
||||
oldSeqRecord.previousValue = oldSeqRecord.currentValue;
|
||||
oldSeqRecord.currentValue = value;
|
||||
this._addToChanges(oldSeqRecord);
|
||||
}
|
||||
} else {
|
||||
seqChanged = true;
|
||||
if (oldSeqRecord !== null) {
|
||||
oldSeqRecord._next = null;
|
||||
this._removeFromSeq(lastOldSeqRecord, oldSeqRecord);
|
||||
this._addToRemovals(oldSeqRecord);
|
||||
}
|
||||
if (records.has(key)) {
|
||||
newSeqRecord = records.get(key);
|
||||
} else {
|
||||
newSeqRecord = new KeyValueChangeRecord(key);
|
||||
records.set(key, newSeqRecord);
|
||||
newSeqRecord.currentValue = value;
|
||||
this._addToAdditions(newSeqRecord);
|
||||
}
|
||||
}
|
||||
|
||||
if (seqChanged) {
|
||||
if (this._isInRemovals(newSeqRecord)) {
|
||||
this._removeFromRemovals(newSeqRecord);
|
||||
}
|
||||
if (lastNewSeqRecord == null) {
|
||||
this._mapHead = newSeqRecord;
|
||||
} else {
|
||||
lastNewSeqRecord._next = newSeqRecord;
|
||||
}
|
||||
}
|
||||
lastOldSeqRecord = oldSeqRecord;
|
||||
lastNewSeqRecord = newSeqRecord;
|
||||
oldSeqRecord = oldSeqRecord === null ? null : oldSeqRecord._next;
|
||||
});
|
||||
this._truncate(lastOldSeqRecord, oldSeqRecord);
|
||||
return this.isDirty;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_reset() {
|
||||
if (this.isDirty) {
|
||||
var record: KeyValueChangeRecord;
|
||||
// Record the state of the mapping
|
||||
for (record = this._previousMapHead = this._mapHead; record !== null; record = record._next) {
|
||||
record._nextPrevious = record._next;
|
||||
}
|
||||
|
||||
for (record = this._changesHead; record !== null; record = record._nextChanged) {
|
||||
record.previousValue = record.currentValue;
|
||||
}
|
||||
|
||||
for (record = this._additionsHead; record != null; record = record._nextAdded) {
|
||||
record.previousValue = record.currentValue;
|
||||
}
|
||||
|
||||
// todo(vicb) once assert is supported
|
||||
// assert(() {
|
||||
// var r = _changesHead;
|
||||
// while (r != null) {
|
||||
// var nextRecord = r._nextChanged;
|
||||
// r._nextChanged = null;
|
||||
// r = nextRecord;
|
||||
// }
|
||||
//
|
||||
// r = _additionsHead;
|
||||
// while (r != null) {
|
||||
// var nextRecord = r._nextAdded;
|
||||
// r._nextAdded = null;
|
||||
// r = nextRecord;
|
||||
// }
|
||||
//
|
||||
// r = _removalsHead;
|
||||
// while (r != null) {
|
||||
// var nextRecord = r._nextRemoved;
|
||||
// r._nextRemoved = null;
|
||||
// r = nextRecord;
|
||||
// }
|
||||
//
|
||||
// return true;
|
||||
//});
|
||||
this._changesHead = this._changesTail = null;
|
||||
this._additionsHead = this._additionsTail = null;
|
||||
this._removalsHead = this._removalsTail = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_truncate(lastRecord: KeyValueChangeRecord, record: KeyValueChangeRecord) {
|
||||
while (record !== null) {
|
||||
if (lastRecord === null) {
|
||||
this._mapHead = null;
|
||||
} else {
|
||||
lastRecord._next = null;
|
||||
}
|
||||
var nextRecord = record._next;
|
||||
// todo(vicb) assert
|
||||
// assert((() {
|
||||
// record._next = null;
|
||||
// return true;
|
||||
//}));
|
||||
this._addToRemovals(record);
|
||||
lastRecord = record;
|
||||
record = nextRecord;
|
||||
}
|
||||
|
||||
for (var rec: KeyValueChangeRecord = this._removalsHead; rec !== null; rec = rec._nextRemoved) {
|
||||
rec.previousValue = rec.currentValue;
|
||||
rec.currentValue = null;
|
||||
this._records.delete(rec.key);
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_isInRemovals(record: KeyValueChangeRecord) {
|
||||
return record === this._removalsHead || record._nextRemoved !== null ||
|
||||
record._prevRemoved !== null;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_addToRemovals(record: KeyValueChangeRecord) {
|
||||
// todo(vicb) assert
|
||||
// assert(record._next == null);
|
||||
// assert(record._nextAdded == null);
|
||||
// assert(record._nextChanged == null);
|
||||
// assert(record._nextRemoved == null);
|
||||
// assert(record._prevRemoved == null);
|
||||
if (this._removalsHead === null) {
|
||||
this._removalsHead = this._removalsTail = record;
|
||||
} else {
|
||||
this._removalsTail._nextRemoved = record;
|
||||
record._prevRemoved = this._removalsTail;
|
||||
this._removalsTail = record;
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_removeFromSeq(prev: KeyValueChangeRecord, record: KeyValueChangeRecord) {
|
||||
var next = record._next;
|
||||
if (prev === null) {
|
||||
this._mapHead = next;
|
||||
} else {
|
||||
prev._next = next;
|
||||
}
|
||||
// todo(vicb) assert
|
||||
// assert((() {
|
||||
// record._next = null;
|
||||
// return true;
|
||||
//})());
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_removeFromRemovals(record: KeyValueChangeRecord) {
|
||||
// todo(vicb) assert
|
||||
// assert(record._next == null);
|
||||
// assert(record._nextAdded == null);
|
||||
// assert(record._nextChanged == null);
|
||||
|
||||
var prev = record._prevRemoved;
|
||||
var next = record._nextRemoved;
|
||||
if (prev === null) {
|
||||
this._removalsHead = next;
|
||||
} else {
|
||||
prev._nextRemoved = next;
|
||||
}
|
||||
if (next === null) {
|
||||
this._removalsTail = prev;
|
||||
} else {
|
||||
next._prevRemoved = prev;
|
||||
}
|
||||
record._prevRemoved = record._nextRemoved = null;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_addToAdditions(record: KeyValueChangeRecord) {
|
||||
// todo(vicb): assert
|
||||
// assert(record._next == null);
|
||||
// assert(record._nextAdded == null);
|
||||
// assert(record._nextChanged == null);
|
||||
// assert(record._nextRemoved == null);
|
||||
// assert(record._prevRemoved == null);
|
||||
if (this._additionsHead === null) {
|
||||
this._additionsHead = this._additionsTail = record;
|
||||
} else {
|
||||
this._additionsTail._nextAdded = record;
|
||||
this._additionsTail = record;
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_addToChanges(record: KeyValueChangeRecord) {
|
||||
// todo(vicb) assert
|
||||
// assert(record._nextAdded == null);
|
||||
// assert(record._nextChanged == null);
|
||||
// assert(record._nextRemoved == null);
|
||||
// assert(record._prevRemoved == null);
|
||||
if (this._changesHead === null) {
|
||||
this._changesHead = this._changesTail = record;
|
||||
} else {
|
||||
this._changesTail._nextChanged = record;
|
||||
this._changesTail = record;
|
||||
}
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
var items = [];
|
||||
var previous = [];
|
||||
var changes = [];
|
||||
var additions = [];
|
||||
var removals = [];
|
||||
var record: KeyValueChangeRecord;
|
||||
|
||||
for (record = this._mapHead; record !== null; record = record._next) {
|
||||
items.push(stringify(record));
|
||||
}
|
||||
for (record = this._previousMapHead; record !== null; record = record._nextPrevious) {
|
||||
previous.push(stringify(record));
|
||||
}
|
||||
for (record = this._changesHead; record !== null; record = record._nextChanged) {
|
||||
changes.push(stringify(record));
|
||||
}
|
||||
for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
||||
additions.push(stringify(record));
|
||||
}
|
||||
for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
|
||||
removals.push(stringify(record));
|
||||
}
|
||||
|
||||
return "map: " + items.join(', ') + "\n" + "previous: " + previous.join(', ') + "\n" +
|
||||
"additions: " + additions.join(', ') + "\n" + "changes: " + changes.join(', ') + "\n" +
|
||||
"removals: " + removals.join(', ') + "\n";
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_forEach(obj, fn: Function) {
|
||||
if (obj instanceof Map) {
|
||||
(<Map<any, any>>obj).forEach(<any>fn);
|
||||
} else {
|
||||
StringMapWrapper.forEach(obj, fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class KeyValueChangeRecord {
|
||||
previousValue: any = null;
|
||||
currentValue: any = null;
|
||||
|
||||
/** @internal */
|
||||
_nextPrevious: KeyValueChangeRecord = null;
|
||||
/** @internal */
|
||||
_next: KeyValueChangeRecord = null;
|
||||
/** @internal */
|
||||
_nextAdded: KeyValueChangeRecord = null;
|
||||
/** @internal */
|
||||
_nextRemoved: KeyValueChangeRecord = null;
|
||||
/** @internal */
|
||||
_prevRemoved: KeyValueChangeRecord = null;
|
||||
/** @internal */
|
||||
_nextChanged: KeyValueChangeRecord = null;
|
||||
|
||||
constructor(public key: any) {}
|
||||
|
||||
toString(): string {
|
||||
return looseIdentical(this.previousValue, this.currentValue) ?
|
||||
stringify(this.key) :
|
||||
(stringify(this.key) + '[' + stringify(this.previousValue) + '->' +
|
||||
stringify(this.currentValue) + ']');
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
import {isBlank, isPresent, getTypeNameForDebugging} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {ChangeDetectorRef} from '../change_detector_ref';
|
||||
import {Provider, SkipSelfMetadata, OptionalMetadata, Injectable} from 'angular2/src/core/di';
|
||||
|
||||
/**
|
||||
* A strategy for tracking changes over time to an iterable. Used for {@link NgFor} to
|
||||
* respond to changes in an iterable by effecting equivalent changes in the DOM.
|
||||
*/
|
||||
export interface IterableDiffer {
|
||||
diff(object: any): any;
|
||||
onDestroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* An optional function passed into {@link NgFor} that defines how to track
|
||||
* items in an iterable (e.g. by index or id)
|
||||
*/
|
||||
export interface TrackByFn { (index: number, item: any): any; }
|
||||
|
||||
|
||||
/**
|
||||
* Provides a factory for {@link IterableDiffer}.
|
||||
*/
|
||||
export interface IterableDifferFactory {
|
||||
supports(objects: any): boolean;
|
||||
create(cdRef: ChangeDetectorRef, trackByFn?: TrackByFn): IterableDiffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* A repository of different iterable diffing strategies used by NgFor, NgClass, and others.
|
||||
* @ts2dart_const
|
||||
*/
|
||||
export class IterableDiffers {
|
||||
/*@ts2dart_const*/
|
||||
constructor(public factories: IterableDifferFactory[]) {}
|
||||
|
||||
static create(factories: IterableDifferFactory[], parent?: IterableDiffers): IterableDiffers {
|
||||
if (isPresent(parent)) {
|
||||
var copied = ListWrapper.clone(parent.factories);
|
||||
factories = factories.concat(copied);
|
||||
return new IterableDiffers(factories);
|
||||
} else {
|
||||
return new IterableDiffers(factories);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an array of {@link IterableDifferFactory} and returns a provider used to extend the
|
||||
* inherited {@link IterableDiffers} instance with the provided factories and return a new
|
||||
* {@link IterableDiffers} instance.
|
||||
*
|
||||
* The following example shows how to extend an existing list of factories,
|
||||
* which will only be applied to the injector for this component and its children.
|
||||
* This step is all that's required to make a new {@link IterableDiffer} available.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* @Component({
|
||||
* viewProviders: [
|
||||
* IterableDiffers.extend([new ImmutableListDiffer()])
|
||||
* ]
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
static extend(factories: IterableDifferFactory[]): Provider {
|
||||
return new Provider(IterableDiffers, {
|
||||
useFactory: (parent: IterableDiffers) => {
|
||||
if (isBlank(parent)) {
|
||||
// Typically would occur when calling IterableDiffers.extend inside of dependencies passed
|
||||
// to
|
||||
// bootstrap(), which would override default pipes instead of extending them.
|
||||
throw new BaseException('Cannot extend IterableDiffers without a parent injector');
|
||||
}
|
||||
return IterableDiffers.create(factories, parent);
|
||||
},
|
||||
// Dependency technically isn't optional, but we can provide a better error message this way.
|
||||
deps: [[IterableDiffers, new SkipSelfMetadata(), new OptionalMetadata()]]
|
||||
});
|
||||
}
|
||||
|
||||
find(iterable: any): IterableDifferFactory {
|
||||
var factory = this.factories.find(f => f.supports(iterable));
|
||||
if (isPresent(factory)) {
|
||||
return factory;
|
||||
} else {
|
||||
throw new BaseException(
|
||||
`Cannot find a differ supporting object '${iterable}' of type '${getTypeNameForDebugging(iterable)}'`);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
import {isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {ChangeDetectorRef} from '../change_detector_ref';
|
||||
import {Provider, SkipSelfMetadata, OptionalMetadata, Injectable} from 'angular2/src/core/di';
|
||||
|
||||
/**
|
||||
* A differ that tracks changes made to an object over time.
|
||||
*/
|
||||
export interface KeyValueDiffer {
|
||||
diff(object: any);
|
||||
onDestroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a factory for {@link KeyValueDiffer}.
|
||||
*/
|
||||
export interface KeyValueDifferFactory {
|
||||
supports(objects: any): boolean;
|
||||
create(cdRef: ChangeDetectorRef): KeyValueDiffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* A repository of different Map diffing strategies used by NgClass, NgStyle, and others.
|
||||
* @ts2dart_const
|
||||
*/
|
||||
export class KeyValueDiffers {
|
||||
/*@ts2dart_const*/
|
||||
constructor(public factories: KeyValueDifferFactory[]) {}
|
||||
|
||||
static create(factories: KeyValueDifferFactory[], parent?: KeyValueDiffers): KeyValueDiffers {
|
||||
if (isPresent(parent)) {
|
||||
var copied = ListWrapper.clone(parent.factories);
|
||||
factories = factories.concat(copied);
|
||||
return new KeyValueDiffers(factories);
|
||||
} else {
|
||||
return new KeyValueDiffers(factories);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an array of {@link KeyValueDifferFactory} and returns a provider used to extend the
|
||||
* inherited {@link KeyValueDiffers} instance with the provided factories and return a new
|
||||
* {@link KeyValueDiffers} instance.
|
||||
*
|
||||
* The following example shows how to extend an existing list of factories,
|
||||
* which will only be applied to the injector for this component and its children.
|
||||
* This step is all that's required to make a new {@link KeyValueDiffer} available.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* @Component({
|
||||
* viewProviders: [
|
||||
* KeyValueDiffers.extend([new ImmutableMapDiffer()])
|
||||
* ]
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
static extend(factories: KeyValueDifferFactory[]): Provider {
|
||||
return new Provider(KeyValueDiffers, {
|
||||
useFactory: (parent: KeyValueDiffers) => {
|
||||
if (isBlank(parent)) {
|
||||
// Typically would occur when calling KeyValueDiffers.extend inside of dependencies passed
|
||||
// to
|
||||
// bootstrap(), which would override default pipes instead of extending them.
|
||||
throw new BaseException('Cannot extend KeyValueDiffers without a parent injector');
|
||||
}
|
||||
return KeyValueDiffers.create(factories, parent);
|
||||
},
|
||||
// Dependency technically isn't optional, but we can provide a better error message this way.
|
||||
deps: [[KeyValueDiffers, new SkipSelfMetadata(), new OptionalMetadata()]]
|
||||
});
|
||||
}
|
||||
|
||||
find(kv: Object): KeyValueDifferFactory {
|
||||
var factory = this.factories.find(f => f.supports(kv));
|
||||
if (isPresent(factory)) {
|
||||
return factory;
|
||||
} else {
|
||||
throw new BaseException(`Cannot find a differ supporting object '${kv}'`);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* To create a Pipe, you must implement this interface.
|
||||
*
|
||||
* Angular invokes the `transform` method with the value of a binding
|
||||
* as the first argument, and any parameters as the second argument in list form.
|
||||
*
|
||||
* ## Syntax
|
||||
*
|
||||
* `value | pipeName[:arg0[:arg1...]]`
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/f5oyIked9M2cKzvZNKHV?p=preview))
|
||||
*
|
||||
* The `RepeatPipe` below repeats the value as many times as indicated by the first argument:
|
||||
*
|
||||
* ```
|
||||
* import {Pipe, PipeTransform} from 'angular2/core';
|
||||
*
|
||||
* @Pipe({name: 'repeat'})
|
||||
* export class RepeatPipe implements PipeTransform {
|
||||
* transform(value: any, times: number) {
|
||||
* return value.repeat(times);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Invoking `{{ 'ok' | repeat:3 }}` in a template produces `okokok`.
|
||||
*
|
||||
*/
|
||||
abstract class PipeTransform {
|
||||
// Note: Dart does not support varargs,
|
||||
// so we can't type the `transform` method...
|
||||
// dynamic transform(dynamic value, List<dynamic> ...args): any;
|
||||
}
|
29
modules/@angular/core/src/change_detection/pipe_transform.ts
Normal file
29
modules/@angular/core/src/change_detection/pipe_transform.ts
Normal file
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* To create a Pipe, you must implement this interface.
|
||||
*
|
||||
* Angular invokes the `transform` method with the value of a binding
|
||||
* as the first argument, and any parameters as the second argument in list form.
|
||||
*
|
||||
* ## Syntax
|
||||
*
|
||||
* `value | pipeName[:arg0[:arg1...]]`
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/f5oyIked9M2cKzvZNKHV?p=preview))
|
||||
*
|
||||
* The `RepeatPipe` below repeats the value as many times as indicated by the first argument:
|
||||
*
|
||||
* ```
|
||||
* import {Pipe, PipeTransform} from 'angular2/core';
|
||||
*
|
||||
* @Pipe({name: 'repeat'})
|
||||
* export class RepeatPipe implements PipeTransform {
|
||||
* transform(value: any, times: number) {
|
||||
* return value.repeat(times);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Invoking `{{ 'ok' | repeat:3 }}` in a template produces `okokok`.
|
||||
*
|
||||
*/
|
||||
export interface PipeTransform { transform(value: any, ...args: any[]): any; }
|
13
modules/@angular/core/src/console.ts
Normal file
13
modules/@angular/core/src/console.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import {print, warn} from 'angular2/src/facade/lang';
|
||||
|
||||
// Note: Need to rename warn as in Dart
|
||||
// class members and imports can't use the same name.
|
||||
let _warnImpl = warn;
|
||||
|
||||
@Injectable()
|
||||
export class Console {
|
||||
log(message: string): void { print(message); }
|
||||
// Note: for reporting errors use `DOM.logError()` as it is platform specific
|
||||
warn(message: string): void { _warnImpl(message); }
|
||||
}
|
179
modules/@angular/core/src/debug/debug_node.ts
Normal file
179
modules/@angular/core/src/debug/debug_node.ts
Normal file
@ -0,0 +1,179 @@
|
||||
import {isPresent, Type} from 'angular2/src/facade/lang';
|
||||
import {Predicate, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {Injector} from 'angular2/src/core/di';
|
||||
import {RenderDebugInfo} from 'angular2/src/core/render/api';
|
||||
|
||||
export class EventListener { constructor(public name: string, public callback: Function){}; }
|
||||
|
||||
export class DebugNode {
|
||||
nativeNode: any;
|
||||
listeners: EventListener[];
|
||||
parent: DebugElement;
|
||||
|
||||
constructor(nativeNode: any, parent: DebugNode, private _debugInfo: RenderDebugInfo) {
|
||||
this.nativeNode = nativeNode;
|
||||
if (isPresent(parent) && parent instanceof DebugElement) {
|
||||
parent.addChild(this);
|
||||
} else {
|
||||
this.parent = null;
|
||||
}
|
||||
this.listeners = [];
|
||||
}
|
||||
|
||||
get injector(): Injector { return isPresent(this._debugInfo) ? this._debugInfo.injector : null; }
|
||||
|
||||
get componentInstance(): any {
|
||||
return isPresent(this._debugInfo) ? this._debugInfo.component : null;
|
||||
}
|
||||
|
||||
get context(): any { return isPresent(this._debugInfo) ? this._debugInfo.context : null; }
|
||||
|
||||
get references(): {[key: string]: any} {
|
||||
return isPresent(this._debugInfo) ? this._debugInfo.references : null;
|
||||
}
|
||||
|
||||
get providerTokens(): any[] {
|
||||
return isPresent(this._debugInfo) ? this._debugInfo.providerTokens : null;
|
||||
}
|
||||
|
||||
get source(): string { return isPresent(this._debugInfo) ? this._debugInfo.source : null; }
|
||||
|
||||
/**
|
||||
* Use injector.get(token) instead.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
inject(token: any): any { return this.injector.get(token); }
|
||||
}
|
||||
|
||||
export class DebugElement extends DebugNode {
|
||||
name: string;
|
||||
properties: {[key: string]: string};
|
||||
attributes: {[key: string]: string};
|
||||
childNodes: DebugNode[];
|
||||
nativeElement: any;
|
||||
|
||||
constructor(nativeNode: any, parent: any, _debugInfo: RenderDebugInfo) {
|
||||
super(nativeNode, parent, _debugInfo);
|
||||
this.properties = {};
|
||||
this.attributes = {};
|
||||
this.childNodes = [];
|
||||
this.nativeElement = nativeNode;
|
||||
}
|
||||
|
||||
addChild(child: DebugNode) {
|
||||
if (isPresent(child)) {
|
||||
this.childNodes.push(child);
|
||||
child.parent = this;
|
||||
}
|
||||
}
|
||||
|
||||
removeChild(child: DebugNode) {
|
||||
var childIndex = this.childNodes.indexOf(child);
|
||||
if (childIndex !== -1) {
|
||||
child.parent = null;
|
||||
this.childNodes.splice(childIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
insertChildrenAfter(child: DebugNode, newChildren: DebugNode[]) {
|
||||
var siblingIndex = this.childNodes.indexOf(child);
|
||||
if (siblingIndex !== -1) {
|
||||
var previousChildren = this.childNodes.slice(0, siblingIndex + 1);
|
||||
var nextChildren = this.childNodes.slice(siblingIndex + 1);
|
||||
this.childNodes =
|
||||
ListWrapper.concat(ListWrapper.concat(previousChildren, newChildren), nextChildren);
|
||||
for (var i = 0; i < newChildren.length; ++i) {
|
||||
var newChild = newChildren[i];
|
||||
if (isPresent(newChild.parent)) {
|
||||
newChild.parent.removeChild(newChild);
|
||||
}
|
||||
newChild.parent = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query(predicate: Predicate<DebugElement>): DebugElement {
|
||||
var results = this.queryAll(predicate);
|
||||
return results.length > 0 ? results[0] : null;
|
||||
}
|
||||
|
||||
queryAll(predicate: Predicate<DebugElement>): DebugElement[] {
|
||||
var matches: DebugElement[] = [];
|
||||
_queryElementChildren(this, predicate, matches);
|
||||
return matches;
|
||||
}
|
||||
|
||||
queryAllNodes(predicate: Predicate<DebugNode>): DebugNode[] {
|
||||
var matches: DebugNode[] = [];
|
||||
_queryNodeChildren(this, predicate, matches);
|
||||
return matches;
|
||||
}
|
||||
|
||||
get children(): DebugElement[] {
|
||||
var children: DebugElement[] = [];
|
||||
this.childNodes.forEach((node) => {
|
||||
if (node instanceof DebugElement) {
|
||||
children.push(node);
|
||||
}
|
||||
});
|
||||
return children;
|
||||
}
|
||||
|
||||
triggerEventHandler(eventName: string, eventObj: any) {
|
||||
this.listeners.forEach((listener) => {
|
||||
if (listener.name == eventName) {
|
||||
listener.callback(eventObj);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function asNativeElements(debugEls: DebugElement[]): any {
|
||||
return debugEls.map((el) => el.nativeElement);
|
||||
}
|
||||
|
||||
function _queryElementChildren(element: DebugElement, predicate: Predicate<DebugElement>,
|
||||
matches: DebugElement[]) {
|
||||
element.childNodes.forEach(node => {
|
||||
if (node instanceof DebugElement) {
|
||||
if (predicate(node)) {
|
||||
matches.push(node);
|
||||
}
|
||||
_queryElementChildren(node, predicate, matches);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function _queryNodeChildren(parentNode: DebugNode, predicate: Predicate<DebugNode>,
|
||||
matches: DebugNode[]) {
|
||||
if (parentNode instanceof DebugElement) {
|
||||
parentNode.childNodes.forEach(node => {
|
||||
if (predicate(node)) {
|
||||
matches.push(node);
|
||||
}
|
||||
if (node instanceof DebugElement) {
|
||||
_queryNodeChildren(node, predicate, matches);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Need to keep the nodes in a global Map so that multiple angular apps are supported.
|
||||
var _nativeNodeToDebugNode = new Map<any, DebugNode>();
|
||||
|
||||
export function getDebugNode(nativeNode: any): DebugNode {
|
||||
return _nativeNodeToDebugNode.get(nativeNode);
|
||||
}
|
||||
|
||||
export function getAllDebugNodes(): DebugNode[] {
|
||||
return MapWrapper.values(_nativeNodeToDebugNode);
|
||||
}
|
||||
|
||||
export function indexDebugNode(node: DebugNode) {
|
||||
_nativeNodeToDebugNode.set(node.nativeNode, node);
|
||||
}
|
||||
|
||||
export function removeDebugNodeFromIndex(node: DebugNode) {
|
||||
_nativeNodeToDebugNode.delete(node.nativeNode);
|
||||
}
|
141
modules/@angular/core/src/debug/debug_renderer.ts
Normal file
141
modules/@angular/core/src/debug/debug_renderer.ts
Normal file
@ -0,0 +1,141 @@
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {
|
||||
Renderer,
|
||||
RootRenderer,
|
||||
RenderComponentType,
|
||||
RenderDebugInfo
|
||||
} from 'angular2/src/core/render/api';
|
||||
import {
|
||||
DebugNode,
|
||||
DebugElement,
|
||||
EventListener,
|
||||
getDebugNode,
|
||||
indexDebugNode,
|
||||
removeDebugNodeFromIndex
|
||||
} from 'angular2/src/core/debug/debug_node';
|
||||
|
||||
export class DebugDomRootRenderer implements RootRenderer {
|
||||
constructor(private _delegate: RootRenderer) {}
|
||||
|
||||
renderComponent(componentProto: RenderComponentType): Renderer {
|
||||
return new DebugDomRenderer(this._delegate.renderComponent(componentProto));
|
||||
}
|
||||
}
|
||||
|
||||
export class DebugDomRenderer implements Renderer {
|
||||
constructor(private _delegate: Renderer) {}
|
||||
|
||||
selectRootElement(selectorOrNode: string | any, debugInfo: RenderDebugInfo): any {
|
||||
var nativeEl = this._delegate.selectRootElement(selectorOrNode, debugInfo);
|
||||
var debugEl = new DebugElement(nativeEl, null, debugInfo);
|
||||
indexDebugNode(debugEl);
|
||||
return nativeEl;
|
||||
}
|
||||
|
||||
createElement(parentElement: any, name: string, debugInfo: RenderDebugInfo): any {
|
||||
var nativeEl = this._delegate.createElement(parentElement, name, debugInfo);
|
||||
var debugEl = new DebugElement(nativeEl, getDebugNode(parentElement), debugInfo);
|
||||
debugEl.name = name;
|
||||
indexDebugNode(debugEl);
|
||||
return nativeEl;
|
||||
}
|
||||
|
||||
createViewRoot(hostElement: any): any { return this._delegate.createViewRoot(hostElement); }
|
||||
|
||||
createTemplateAnchor(parentElement: any, debugInfo: RenderDebugInfo): any {
|
||||
var comment = this._delegate.createTemplateAnchor(parentElement, debugInfo);
|
||||
var debugEl = new DebugNode(comment, getDebugNode(parentElement), debugInfo);
|
||||
indexDebugNode(debugEl);
|
||||
return comment;
|
||||
}
|
||||
|
||||
createText(parentElement: any, value: string, debugInfo: RenderDebugInfo): any {
|
||||
var text = this._delegate.createText(parentElement, value, debugInfo);
|
||||
var debugEl = new DebugNode(text, getDebugNode(parentElement), debugInfo);
|
||||
indexDebugNode(debugEl);
|
||||
return text;
|
||||
}
|
||||
|
||||
projectNodes(parentElement: any, nodes: any[]) {
|
||||
var debugParent = getDebugNode(parentElement);
|
||||
if (isPresent(debugParent) && debugParent instanceof DebugElement) {
|
||||
let debugElement = debugParent;
|
||||
nodes.forEach((node) => { debugElement.addChild(getDebugNode(node)); });
|
||||
}
|
||||
this._delegate.projectNodes(parentElement, nodes);
|
||||
}
|
||||
|
||||
attachViewAfter(node: any, viewRootNodes: any[]) {
|
||||
var debugNode = getDebugNode(node);
|
||||
if (isPresent(debugNode)) {
|
||||
var debugParent = debugNode.parent;
|
||||
if (viewRootNodes.length > 0 && isPresent(debugParent)) {
|
||||
var debugViewRootNodes: DebugNode[] = [];
|
||||
viewRootNodes.forEach((rootNode) => debugViewRootNodes.push(getDebugNode(rootNode)));
|
||||
debugParent.insertChildrenAfter(debugNode, debugViewRootNodes);
|
||||
}
|
||||
}
|
||||
this._delegate.attachViewAfter(node, viewRootNodes);
|
||||
}
|
||||
|
||||
detachView(viewRootNodes: any[]) {
|
||||
viewRootNodes.forEach((node) => {
|
||||
var debugNode = getDebugNode(node);
|
||||
if (isPresent(debugNode) && isPresent(debugNode.parent)) {
|
||||
debugNode.parent.removeChild(debugNode);
|
||||
}
|
||||
});
|
||||
this._delegate.detachView(viewRootNodes);
|
||||
}
|
||||
|
||||
destroyView(hostElement: any, viewAllNodes: any[]) {
|
||||
viewAllNodes.forEach((node) => { removeDebugNodeFromIndex(getDebugNode(node)); });
|
||||
this._delegate.destroyView(hostElement, viewAllNodes);
|
||||
}
|
||||
|
||||
listen(renderElement: any, name: string, callback: Function): Function {
|
||||
var debugEl = getDebugNode(renderElement);
|
||||
if (isPresent(debugEl)) {
|
||||
debugEl.listeners.push(new EventListener(name, callback));
|
||||
}
|
||||
return this._delegate.listen(renderElement, name, callback);
|
||||
}
|
||||
|
||||
listenGlobal(target: string, name: string, callback: Function): Function {
|
||||
return this._delegate.listenGlobal(target, name, callback);
|
||||
}
|
||||
|
||||
setElementProperty(renderElement: any, propertyName: string, propertyValue: any) {
|
||||
var debugEl = getDebugNode(renderElement);
|
||||
if (isPresent(debugEl) && debugEl instanceof DebugElement) {
|
||||
debugEl.properties[propertyName] = propertyValue;
|
||||
}
|
||||
this._delegate.setElementProperty(renderElement, propertyName, propertyValue);
|
||||
}
|
||||
|
||||
setElementAttribute(renderElement: any, attributeName: string, attributeValue: string) {
|
||||
var debugEl = getDebugNode(renderElement);
|
||||
if (isPresent(debugEl) && debugEl instanceof DebugElement) {
|
||||
debugEl.attributes[attributeName] = attributeValue;
|
||||
}
|
||||
this._delegate.setElementAttribute(renderElement, attributeName, attributeValue);
|
||||
}
|
||||
|
||||
setBindingDebugInfo(renderElement: any, propertyName: string, propertyValue: string) {
|
||||
this._delegate.setBindingDebugInfo(renderElement, propertyName, propertyValue);
|
||||
}
|
||||
|
||||
setElementClass(renderElement: any, className: string, isAdd: boolean) {
|
||||
this._delegate.setElementClass(renderElement, className, isAdd);
|
||||
}
|
||||
|
||||
setElementStyle(renderElement: any, styleName: string, styleValue: string) {
|
||||
this._delegate.setElementStyle(renderElement, styleName, styleValue);
|
||||
}
|
||||
|
||||
invokeElementMethod(renderElement: any, methodName: string, args: any[]) {
|
||||
this._delegate.invokeElementMethod(renderElement, methodName, args);
|
||||
}
|
||||
|
||||
setText(renderNode: any, text: string) { this._delegate.setText(renderNode, text); }
|
||||
}
|
49
modules/@angular/core/src/di.ts
Normal file
49
modules/@angular/core/src/di.ts
Normal file
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
* The `di` module provides dependency injection container services.
|
||||
*/
|
||||
|
||||
export {
|
||||
InjectMetadata,
|
||||
OptionalMetadata,
|
||||
InjectableMetadata,
|
||||
SelfMetadata,
|
||||
HostMetadata,
|
||||
SkipSelfMetadata,
|
||||
DependencyMetadata
|
||||
} from './di/metadata';
|
||||
|
||||
// we have to reexport * because Dart and TS export two different sets of types
|
||||
export * from './di/decorators';
|
||||
|
||||
export {forwardRef, resolveForwardRef, ForwardRefFn} from './di/forward_ref';
|
||||
|
||||
export {Injector} from './di/injector';
|
||||
export {ReflectiveInjector} from './di/reflective_injector';
|
||||
export {
|
||||
Binding,
|
||||
ProviderBuilder,
|
||||
bind,
|
||||
|
||||
Provider,
|
||||
provide
|
||||
} from './di/provider';
|
||||
export {
|
||||
ResolvedReflectiveBinding,
|
||||
ResolvedReflectiveFactory,
|
||||
ReflectiveDependency,
|
||||
|
||||
ResolvedReflectiveProvider
|
||||
} from './di/reflective_provider';
|
||||
export {ReflectiveKey} from './di/reflective_key';
|
||||
export {
|
||||
NoProviderError,
|
||||
AbstractProviderError,
|
||||
CyclicDependencyError,
|
||||
InstantiationError,
|
||||
InvalidProviderError,
|
||||
NoAnnotationError,
|
||||
OutOfBoundsError
|
||||
} from './di/reflective_exceptions';
|
||||
export {OpaqueToken} from './di/opaque_token';
|
46
modules/@angular/core/src/di/decorators.dart
Normal file
46
modules/@angular/core/src/di/decorators.dart
Normal file
@ -0,0 +1,46 @@
|
||||
library angular2.di.decorators;
|
||||
|
||||
import 'metadata.dart';
|
||||
export 'metadata.dart';
|
||||
|
||||
/**
|
||||
* {@link InjectMetadata}.
|
||||
*/
|
||||
class Inject extends InjectMetadata {
|
||||
const Inject(dynamic token) : super(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link OptionalMetadata}.
|
||||
*/
|
||||
class Optional extends OptionalMetadata {
|
||||
const Optional() : super();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link InjectableMetadata}.
|
||||
*/
|
||||
class Injectable extends InjectableMetadata {
|
||||
const Injectable() : super();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link SelfMetadata}.
|
||||
*/
|
||||
class Self extends SelfMetadata {
|
||||
const Self() : super();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link HostMetadata}.
|
||||
*/
|
||||
class Host extends HostMetadata {
|
||||
const Host() : super();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link SkipSelfMetadata}.
|
||||
*/
|
||||
class SkipSelf extends SkipSelfMetadata {
|
||||
const SkipSelf() : super();
|
||||
}
|
88
modules/@angular/core/src/di/decorators.ts
Normal file
88
modules/@angular/core/src/di/decorators.ts
Normal file
@ -0,0 +1,88 @@
|
||||
import {
|
||||
InjectMetadata,
|
||||
OptionalMetadata,
|
||||
InjectableMetadata,
|
||||
SelfMetadata,
|
||||
HostMetadata,
|
||||
SkipSelfMetadata
|
||||
} from './metadata';
|
||||
import {makeDecorator, makeParamDecorator} from '../util/decorators';
|
||||
|
||||
/**
|
||||
* Factory for creating {@link InjectMetadata}.
|
||||
*/
|
||||
export interface InjectMetadataFactory {
|
||||
(token: any): any;
|
||||
new (token: any): InjectMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for creating {@link OptionalMetadata}.
|
||||
*/
|
||||
export interface OptionalMetadataFactory {
|
||||
(): any;
|
||||
new (): OptionalMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for creating {@link InjectableMetadata}.
|
||||
*/
|
||||
export interface InjectableMetadataFactory {
|
||||
(): any;
|
||||
new (): InjectableMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for creating {@link SelfMetadata}.
|
||||
*/
|
||||
export interface SelfMetadataFactory {
|
||||
(): any;
|
||||
new (): SelfMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for creating {@link HostMetadata}.
|
||||
*/
|
||||
export interface HostMetadataFactory {
|
||||
(): any;
|
||||
new (): HostMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for creating {@link SkipSelfMetadata}.
|
||||
*/
|
||||
export interface SkipSelfMetadataFactory {
|
||||
(): any;
|
||||
new (): SkipSelfMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for creating {@link InjectMetadata}.
|
||||
*/
|
||||
export var Inject: InjectMetadataFactory = makeParamDecorator(InjectMetadata);
|
||||
|
||||
/**
|
||||
* Factory for creating {@link OptionalMetadata}.
|
||||
*/
|
||||
export var Optional: OptionalMetadataFactory = makeParamDecorator(OptionalMetadata);
|
||||
|
||||
/**
|
||||
* Factory for creating {@link InjectableMetadata}.
|
||||
*/
|
||||
export var Injectable: InjectableMetadataFactory =
|
||||
<InjectableMetadataFactory>makeDecorator(InjectableMetadata);
|
||||
|
||||
/**
|
||||
* Factory for creating {@link SelfMetadata}.
|
||||
*/
|
||||
export var Self: SelfMetadataFactory = makeParamDecorator(SelfMetadata);
|
||||
|
||||
/**
|
||||
* Factory for creating {@link HostMetadata}.
|
||||
*/
|
||||
export var Host: HostMetadataFactory = makeParamDecorator(HostMetadata);
|
||||
|
||||
/**
|
||||
* Factory for creating {@link SkipSelfMetadata}.
|
||||
*/
|
||||
export var SkipSelf: SkipSelfMetadataFactory = makeParamDecorator(SkipSelfMetadata);
|
15
modules/@angular/core/src/di/forward_ref.dart
Normal file
15
modules/@angular/core/src/di/forward_ref.dart
Normal file
@ -0,0 +1,15 @@
|
||||
library angular2.di.forward_ref;
|
||||
|
||||
typedef dynamic ForwardRefFn();
|
||||
|
||||
/**
|
||||
* Dart does not have the forward ref problem, so this function is a noop.
|
||||
*/
|
||||
forwardRef(ForwardRefFn forwardRefFn) => forwardRefFn();
|
||||
|
||||
/**
|
||||
* Lazily retrieve the reference value.
|
||||
*
|
||||
* See: {@link forwardRef}
|
||||
*/
|
||||
resolveForwardRef(type) => type;
|
51
modules/@angular/core/src/di/forward_ref.ts
Normal file
51
modules/@angular/core/src/di/forward_ref.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import {Type, stringify, isFunction} from 'angular2/src/facade/lang';
|
||||
|
||||
/**
|
||||
* An interface that a function passed into {@link forwardRef} has to implement.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/forward_ref/forward_ref.ts region='forward_ref_fn'}
|
||||
*/
|
||||
export interface ForwardRefFn { (): any; }
|
||||
|
||||
/**
|
||||
* Allows to refer to references which are not yet defined.
|
||||
*
|
||||
* For instance, `forwardRef` is used when the `token` which we need to refer to for the purposes of
|
||||
* DI is declared,
|
||||
* but not yet defined. It is also used when the `token` which we use when creating a query is not
|
||||
* yet defined.
|
||||
*
|
||||
* ### Example
|
||||
* {@example core/di/ts/forward_ref/forward_ref.ts region='forward_ref'}
|
||||
*/
|
||||
export function forwardRef(forwardRefFn: ForwardRefFn): Type {
|
||||
(<any>forwardRefFn).__forward_ref__ = forwardRef;
|
||||
(<any>forwardRefFn).toString = function() { return stringify(this()); };
|
||||
return (<Type><any>forwardRefFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily retrieves the reference value from a forwardRef.
|
||||
*
|
||||
* Acts as the identity function when given a non-forward-ref value.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/GU72mJrk1fiodChcmiDR?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* var ref = forwardRef(() => "refValue");
|
||||
* expect(resolveForwardRef(ref)).toEqual("refValue");
|
||||
* expect(resolveForwardRef("regularValue")).toEqual("regularValue");
|
||||
* ```
|
||||
*
|
||||
* See: {@link forwardRef}
|
||||
*/
|
||||
export function resolveForwardRef(type: any): any {
|
||||
if (isFunction(type) && type.hasOwnProperty('__forward_ref__') &&
|
||||
type.__forward_ref__ === forwardRef) {
|
||||
return (<ForwardRefFn>type)();
|
||||
} else {
|
||||
return type;
|
||||
}
|
||||
}
|
34
modules/@angular/core/src/di/injector.ts
Normal file
34
modules/@angular/core/src/di/injector.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
|
||||
const _THROW_IF_NOT_FOUND = /*@ts2dart_const*/ new Object();
|
||||
export const THROW_IF_NOT_FOUND = /*@ts2dart_const*/ _THROW_IF_NOT_FOUND;
|
||||
|
||||
export abstract class Injector {
|
||||
static THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
|
||||
|
||||
/**
|
||||
* Retrieves an instance from the injector based on the provided token.
|
||||
* If not found:
|
||||
* - Throws {@link NoProviderError} if no `notFoundValue` that is not equal to
|
||||
* Injector.THROW_IF_NOT_FOUND is given
|
||||
* - Returns the `notFoundValue` otherwise
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/HeXSHg?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* var injector = ReflectiveInjector.resolveAndCreate([
|
||||
* provide("validToken", {useValue: "Value"})
|
||||
* ]);
|
||||
* expect(injector.get("validToken")).toEqual("Value");
|
||||
* expect(() => injector.get("invalidToken")).toThrowError();
|
||||
* ```
|
||||
*
|
||||
* `Injector` returns itself when given `Injector` as a token.
|
||||
*
|
||||
* ```typescript
|
||||
* var injector = ReflectiveInjector.resolveAndCreate([]);
|
||||
* expect(injector.get(Injector)).toBe(injector);
|
||||
* ```
|
||||
*/
|
||||
get(token: any, notFoundValue?: any): any { return unimplemented(); }
|
||||
}
|
239
modules/@angular/core/src/di/metadata.ts
Normal file
239
modules/@angular/core/src/di/metadata.ts
Normal file
@ -0,0 +1,239 @@
|
||||
import {stringify, isBlank, isPresent} from "angular2/src/facade/lang";
|
||||
|
||||
/**
|
||||
* A parameter metadata that specifies a dependency.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/6uHYJK?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* class Engine {}
|
||||
*
|
||||
* @Injectable()
|
||||
* class Car {
|
||||
* engine;
|
||||
* constructor(@Inject("MyEngine") engine:Engine) {
|
||||
* this.engine = engine;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* provide("MyEngine", {useClass: Engine}),
|
||||
* Car
|
||||
* ]);
|
||||
*
|
||||
* expect(injector.get(Car).engine instanceof Engine).toBe(true);
|
||||
* ```
|
||||
*
|
||||
* When `@Inject()` is not present, {@link Injector} will use the type annotation of the parameter.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```typescript
|
||||
* class Engine {}
|
||||
*
|
||||
* @Injectable()
|
||||
* class Car {
|
||||
* constructor(public engine: Engine) {} //same as constructor(@Inject(Engine) engine:Engine)
|
||||
* }
|
||||
*
|
||||
* var injector = Injector.resolveAndCreate([Engine, Car]);
|
||||
* expect(injector.get(Car).engine instanceof Engine).toBe(true);
|
||||
* ```
|
||||
* @ts2dart_const
|
||||
*/
|
||||
export class InjectMetadata {
|
||||
constructor(public token) {}
|
||||
toString(): string { return `@Inject(${stringify(this.token)})`; }
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter metadata that marks a dependency as optional. {@link Injector} provides `null` if
|
||||
* the dependency is not found.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/AsryOm?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* class Engine {}
|
||||
*
|
||||
* @Injectable()
|
||||
* class Car {
|
||||
* engine;
|
||||
* constructor(@Optional() engine:Engine) {
|
||||
* this.engine = engine;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* var injector = Injector.resolveAndCreate([Car]);
|
||||
* expect(injector.get(Car).engine).toBeNull();
|
||||
* ```
|
||||
* @ts2dart_const
|
||||
*/
|
||||
export class OptionalMetadata {
|
||||
toString(): string { return `@Optional()`; }
|
||||
}
|
||||
|
||||
/**
|
||||
* `DependencyMetadata` is used by the framework to extend DI.
|
||||
* This is internal to Angular and should not be used directly.
|
||||
* @ts2dart_const
|
||||
*/
|
||||
export class DependencyMetadata {
|
||||
get token() { return null; }
|
||||
}
|
||||
|
||||
/**
|
||||
* A marker metadata that marks a class as available to {@link Injector} for creation.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/Wk4DMQ?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Injectable()
|
||||
* class UsefulService {}
|
||||
*
|
||||
* @Injectable()
|
||||
* class NeedsService {
|
||||
* constructor(public service:UsefulService) {}
|
||||
* }
|
||||
*
|
||||
* var injector = Injector.resolveAndCreate([NeedsService, UsefulService]);
|
||||
* expect(injector.get(NeedsService).service instanceof UsefulService).toBe(true);
|
||||
* ```
|
||||
* {@link Injector} will throw {@link NoAnnotationError} when trying to instantiate a class that
|
||||
* does not have `@Injectable` marker, as shown in the example below.
|
||||
*
|
||||
* ```typescript
|
||||
* class UsefulService {}
|
||||
*
|
||||
* class NeedsService {
|
||||
* constructor(public service:UsefulService) {}
|
||||
* }
|
||||
*
|
||||
* var injector = Injector.resolveAndCreate([NeedsService, UsefulService]);
|
||||
* expect(() => injector.get(NeedsService)).toThrowError();
|
||||
* ```
|
||||
* @ts2dart_const
|
||||
*/
|
||||
export class InjectableMetadata {
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that an {@link Injector} should retrieve a dependency only from itself.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/NeagAg?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* class Dependency {
|
||||
* }
|
||||
*
|
||||
* @Injectable()
|
||||
* class NeedsDependency {
|
||||
* dependency;
|
||||
* constructor(@Self() dependency:Dependency) {
|
||||
* this.dependency = dependency;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* var inj = Injector.resolveAndCreate([Dependency, NeedsDependency]);
|
||||
* var nd = inj.get(NeedsDependency);
|
||||
*
|
||||
* expect(nd.dependency instanceof Dependency).toBe(true);
|
||||
*
|
||||
* var inj = Injector.resolveAndCreate([Dependency]);
|
||||
* var child = inj.resolveAndCreateChild([NeedsDependency]);
|
||||
* expect(() => child.get(NeedsDependency)).toThrowError();
|
||||
* ```
|
||||
* @ts2dart_const
|
||||
*/
|
||||
export class SelfMetadata {
|
||||
toString(): string { return `@Self()`; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that the dependency resolution should start from the parent injector.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/Wchdzb?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* class Dependency {
|
||||
* }
|
||||
*
|
||||
* @Injectable()
|
||||
* class NeedsDependency {
|
||||
* dependency;
|
||||
* constructor(@SkipSelf() dependency:Dependency) {
|
||||
* this.dependency = dependency;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* var parent = Injector.resolveAndCreate([Dependency]);
|
||||
* var child = parent.resolveAndCreateChild([NeedsDependency]);
|
||||
* expect(child.get(NeedsDependency).dependency instanceof Depedency).toBe(true);
|
||||
*
|
||||
* var inj = Injector.resolveAndCreate([Dependency, NeedsDependency]);
|
||||
* expect(() => inj.get(NeedsDependency)).toThrowError();
|
||||
* ```
|
||||
* @ts2dart_const
|
||||
*/
|
||||
export class SkipSelfMetadata {
|
||||
toString(): string { return `@SkipSelf()`; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that an injector should retrieve a dependency from any injector until reaching the
|
||||
* closest host.
|
||||
*
|
||||
* In Angular, a component element is automatically declared as a host for all the injectors in
|
||||
* its view.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/GX79pV?p=preview))
|
||||
*
|
||||
* In the following example `App` contains `ParentCmp`, which contains `ChildDirective`.
|
||||
* So `ParentCmp` is the host of `ChildDirective`.
|
||||
*
|
||||
* `ChildDirective` depends on two services: `HostService` and `OtherService`.
|
||||
* `HostService` is defined at `ParentCmp`, and `OtherService` is defined at `App`.
|
||||
*
|
||||
*```typescript
|
||||
* class OtherService {}
|
||||
* class HostService {}
|
||||
*
|
||||
* @Directive({
|
||||
* selector: 'child-directive'
|
||||
* })
|
||||
* class ChildDirective {
|
||||
* constructor(@Optional() @Host() os:OtherService, @Optional() @Host() hs:HostService){
|
||||
* console.log("os is null", os);
|
||||
* console.log("hs is NOT null", hs);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'parent-cmp',
|
||||
* providers: [HostService],
|
||||
* template: `
|
||||
* Dir: <child-directive></child-directive>
|
||||
* `,
|
||||
* directives: [ChildDirective]
|
||||
* })
|
||||
* class ParentCmp {
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* providers: [OtherService],
|
||||
* template: `
|
||||
* Parent: <parent-cmp></parent-cmp>
|
||||
* `,
|
||||
* directives: [ParentCmp]
|
||||
* })
|
||||
* class App {
|
||||
* }
|
||||
*
|
||||
* bootstrap(App);
|
||||
*```
|
||||
* @ts2dart_const
|
||||
*/
|
||||
export class HostMetadata {
|
||||
toString(): string { return `@Host()`; }
|
||||
}
|
28
modules/@angular/core/src/di/opaque_token.ts
Normal file
28
modules/@angular/core/src/di/opaque_token.ts
Normal file
@ -0,0 +1,28 @@
|
||||
|
||||
/**
|
||||
* Creates a token that can be used in a DI Provider.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/Ys9ezXpj2Mnoy3Uc8KBp?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* var t = new OpaqueToken("value");
|
||||
*
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* provide(t, {useValue: "bindingValue"})
|
||||
* ]);
|
||||
*
|
||||
* expect(injector.get(t)).toEqual("bindingValue");
|
||||
* ```
|
||||
*
|
||||
* Using an `OpaqueToken` is preferable to using strings as tokens because of possible collisions
|
||||
* caused by multiple providers using the same string as two different tokens.
|
||||
*
|
||||
* Using an `OpaqueToken` is preferable to using an `Object` as tokens because it provides better
|
||||
* error messages.
|
||||
* @ts2dart_const
|
||||
*/
|
||||
export class OpaqueToken {
|
||||
constructor(private _desc: string) {}
|
||||
|
||||
toString(): string { return `Token ${this._desc}`; }
|
||||
}
|
411
modules/@angular/core/src/di/provider.ts
Normal file
411
modules/@angular/core/src/di/provider.ts
Normal file
@ -0,0 +1,411 @@
|
||||
import {
|
||||
normalizeBool,
|
||||
Type,
|
||||
isType,
|
||||
isBlank,
|
||||
isFunction,
|
||||
stringify
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
|
||||
/**
|
||||
* Describes how the {@link Injector} should instantiate a given token.
|
||||
*
|
||||
* See {@link provide}.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/GNAyj6K6PfYg2NBzgwZ5?p%3Dpreview&p=preview))
|
||||
*
|
||||
* ```javascript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* new Provider("message", { useValue: 'Hello' })
|
||||
* ]);
|
||||
*
|
||||
* expect(injector.get("message")).toEqual('Hello');
|
||||
* ```
|
||||
* @ts2dart_const
|
||||
*/
|
||||
export class Provider {
|
||||
/**
|
||||
* Token used when retrieving this provider. Usually, it is a type {@link Type}.
|
||||
*/
|
||||
token;
|
||||
|
||||
/**
|
||||
* Binds a DI token to an implementation class.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/RSTG86qgmoxCyj9SWPwY?p=preview))
|
||||
*
|
||||
* Because `useExisting` and `useClass` are often confused, the example contains
|
||||
* both use cases for easy comparison.
|
||||
*
|
||||
* ```typescript
|
||||
* class Vehicle {}
|
||||
*
|
||||
* class Car extends Vehicle {}
|
||||
*
|
||||
* var injectorClass = Injector.resolveAndCreate([
|
||||
* Car,
|
||||
* {provide: Vehicle, useClass: Car }
|
||||
* ]);
|
||||
* var injectorAlias = Injector.resolveAndCreate([
|
||||
* Car,
|
||||
* {provide: Vehicle, useExisting: Car }
|
||||
* ]);
|
||||
*
|
||||
* expect(injectorClass.get(Vehicle)).not.toBe(injectorClass.get(Car));
|
||||
* expect(injectorClass.get(Vehicle) instanceof Car).toBe(true);
|
||||
*
|
||||
* expect(injectorAlias.get(Vehicle)).toBe(injectorAlias.get(Car));
|
||||
* expect(injectorAlias.get(Vehicle) instanceof Car).toBe(true);
|
||||
* ```
|
||||
*/
|
||||
useClass: Type;
|
||||
|
||||
/**
|
||||
* Binds a DI token to a value.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/UFVsMVQIDe7l4waWziES?p=preview))
|
||||
*
|
||||
* ```javascript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* new Provider("message", { useValue: 'Hello' })
|
||||
* ]);
|
||||
*
|
||||
* expect(injector.get("message")).toEqual('Hello');
|
||||
* ```
|
||||
*/
|
||||
useValue;
|
||||
|
||||
/**
|
||||
* Binds a DI token to an existing token.
|
||||
*
|
||||
* {@link Injector} returns the same instance as if the provided token was used.
|
||||
* This is in contrast to `useClass` where a separate instance of `useClass` is returned.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/QsatsOJJ6P8T2fMe9gr8?p=preview))
|
||||
*
|
||||
* Because `useExisting` and `useClass` are often confused the example contains
|
||||
* both use cases for easy comparison.
|
||||
*
|
||||
* ```typescript
|
||||
* class Vehicle {}
|
||||
*
|
||||
* class Car extends Vehicle {}
|
||||
*
|
||||
* var injectorAlias = Injector.resolveAndCreate([
|
||||
* Car,
|
||||
* {provide: Vehicle, useExisting: Car }
|
||||
* ]);
|
||||
* var injectorClass = Injector.resolveAndCreate([
|
||||
* Car,
|
||||
* {provide: Vehicle, useClass: Car }
|
||||
* ]);
|
||||
*
|
||||
* expect(injectorAlias.get(Vehicle)).toBe(injectorAlias.get(Car));
|
||||
* expect(injectorAlias.get(Vehicle) instanceof Car).toBe(true);
|
||||
*
|
||||
* expect(injectorClass.get(Vehicle)).not.toBe(injectorClass.get(Car));
|
||||
* expect(injectorClass.get(Vehicle) instanceof Car).toBe(true);
|
||||
* ```
|
||||
*/
|
||||
useExisting;
|
||||
|
||||
/**
|
||||
* Binds a DI token to a function which computes the value.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/Scoxy0pJNqKGAPZY1VVC?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* {provide: Number, useFactory: () => { return 1+2; }},
|
||||
* new Provider(String, { useFactory: (value) => { return "Value: " + value; },
|
||||
* deps: [Number] })
|
||||
* ]);
|
||||
*
|
||||
* expect(injector.get(Number)).toEqual(3);
|
||||
* expect(injector.get(String)).toEqual('Value: 3');
|
||||
* ```
|
||||
*
|
||||
* Used in conjunction with dependencies.
|
||||
*/
|
||||
useFactory: Function;
|
||||
|
||||
/**
|
||||
* Specifies a set of dependencies
|
||||
* (as `token`s) which should be injected into the factory function.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/Scoxy0pJNqKGAPZY1VVC?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* {provide: Number, useFactory: () => { return 1+2; }},
|
||||
* new Provider(String, { useFactory: (value) => { return "Value: " + value; },
|
||||
* deps: [Number] })
|
||||
* ]);
|
||||
*
|
||||
* expect(injector.get(Number)).toEqual(3);
|
||||
* expect(injector.get(String)).toEqual('Value: 3');
|
||||
* ```
|
||||
*
|
||||
* Used in conjunction with `useFactory`.
|
||||
*/
|
||||
dependencies: Object[];
|
||||
|
||||
/** @internal */
|
||||
_multi: boolean;
|
||||
|
||||
constructor(token, {useClass, useValue, useExisting, useFactory, deps, multi}: {
|
||||
useClass?: Type,
|
||||
useValue?: any,
|
||||
useExisting?: any,
|
||||
useFactory?: Function,
|
||||
deps?: Object[],
|
||||
multi?: boolean
|
||||
}) {
|
||||
this.token = token;
|
||||
this.useClass = useClass;
|
||||
this.useValue = useValue;
|
||||
this.useExisting = useExisting;
|
||||
this.useFactory = useFactory;
|
||||
this.dependencies = deps;
|
||||
this._multi = multi;
|
||||
}
|
||||
|
||||
// TODO: Provide a full working example after alpha38 is released.
|
||||
/**
|
||||
* Creates multiple providers matching the same token (a multi-provider).
|
||||
*
|
||||
* Multi-providers are used for creating pluggable service, where the system comes
|
||||
* with some default providers, and the user can register additional providers.
|
||||
* The combination of the default providers and the additional providers will be
|
||||
* used to drive the behavior of the system.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```typescript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* new Provider("Strings", { useValue: "String1", multi: true}),
|
||||
* new Provider("Strings", { useValue: "String2", multi: true})
|
||||
* ]);
|
||||
*
|
||||
* expect(injector.get("Strings")).toEqual(["String1", "String2"]);
|
||||
* ```
|
||||
*
|
||||
* Multi-providers and regular providers cannot be mixed. The following
|
||||
* will throw an exception:
|
||||
*
|
||||
* ```typescript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* new Provider("Strings", { useValue: "String1", multi: true }),
|
||||
* new Provider("Strings", { useValue: "String2"})
|
||||
* ]);
|
||||
* ```
|
||||
*/
|
||||
get multi(): boolean { return normalizeBool(this._multi); }
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link Provider} instead.
|
||||
*
|
||||
* @deprecated
|
||||
* @ts2dart_const
|
||||
*/
|
||||
export class Binding extends Provider {
|
||||
constructor(token, {toClass, toValue, toAlias, toFactory, deps, multi}: {
|
||||
toClass?: Type,
|
||||
toValue?: any,
|
||||
toAlias?: any,
|
||||
toFactory: Function, deps?: Object[], multi?: boolean
|
||||
}) {
|
||||
super(token, {
|
||||
useClass: toClass,
|
||||
useValue: toValue,
|
||||
useExisting: toAlias,
|
||||
useFactory: toFactory,
|
||||
deps: deps,
|
||||
multi: multi
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
get toClass() { return this.useClass; }
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
get toAlias() { return this.useExisting; }
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
get toFactory() { return this.useFactory; }
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
get toValue() { return this.useValue; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Provider}.
|
||||
*
|
||||
* To construct a {@link Provider}, bind a `token` to either a class, a value, a factory function,
|
||||
* or
|
||||
* to an existing `token`.
|
||||
* See {@link ProviderBuilder} for more details.
|
||||
*
|
||||
* The `token` is most commonly a class or {@link OpaqueToken-class.html}.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
export function bind(token): ProviderBuilder {
|
||||
return new ProviderBuilder(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for the {@link bind} function.
|
||||
*/
|
||||
export class ProviderBuilder {
|
||||
constructor(public token) {}
|
||||
|
||||
/**
|
||||
* Binds a DI token to a class.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/ZpBCSYqv6e2ud5KXLdxQ?p=preview))
|
||||
*
|
||||
* Because `toAlias` and `toClass` are often confused, the example contains
|
||||
* both use cases for easy comparison.
|
||||
*
|
||||
* ```typescript
|
||||
* class Vehicle {}
|
||||
*
|
||||
* class Car extends Vehicle {}
|
||||
*
|
||||
* var injectorClass = Injector.resolveAndCreate([
|
||||
* Car,
|
||||
* provide(Vehicle, {useClass: Car})
|
||||
* ]);
|
||||
* var injectorAlias = Injector.resolveAndCreate([
|
||||
* Car,
|
||||
* provide(Vehicle, {useExisting: Car})
|
||||
* ]);
|
||||
*
|
||||
* expect(injectorClass.get(Vehicle)).not.toBe(injectorClass.get(Car));
|
||||
* expect(injectorClass.get(Vehicle) instanceof Car).toBe(true);
|
||||
*
|
||||
* expect(injectorAlias.get(Vehicle)).toBe(injectorAlias.get(Car));
|
||||
* expect(injectorAlias.get(Vehicle) instanceof Car).toBe(true);
|
||||
* ```
|
||||
*/
|
||||
toClass(type: Type): Provider {
|
||||
if (!isType(type)) {
|
||||
throw new BaseException(
|
||||
`Trying to create a class provider but "${stringify(type)}" is not a class!`);
|
||||
}
|
||||
return new Provider(this.token, {useClass: type});
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a DI token to a value.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/G024PFHmDL0cJFgfZK8O?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* provide('message', {useValue: 'Hello'})
|
||||
* ]);
|
||||
*
|
||||
* expect(injector.get('message')).toEqual('Hello');
|
||||
* ```
|
||||
*/
|
||||
toValue(value: any): Provider { return new Provider(this.token, {useValue: value}); }
|
||||
|
||||
/**
|
||||
* Binds a DI token to an existing token.
|
||||
*
|
||||
* Angular will return the same instance as if the provided token was used. (This is
|
||||
* in contrast to `useClass` where a separate instance of `useClass` will be returned.)
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/uBaoF2pN5cfc5AfZapNw?p=preview))
|
||||
*
|
||||
* Because `toAlias` and `toClass` are often confused, the example contains
|
||||
* both use cases for easy comparison.
|
||||
*
|
||||
* ```typescript
|
||||
* class Vehicle {}
|
||||
*
|
||||
* class Car extends Vehicle {}
|
||||
*
|
||||
* var injectorAlias = Injector.resolveAndCreate([
|
||||
* Car,
|
||||
* provide(Vehicle, {useExisting: Car})
|
||||
* ]);
|
||||
* var injectorClass = Injector.resolveAndCreate([
|
||||
* Car,
|
||||
* provide(Vehicle, {useClass: Car})
|
||||
* ]);
|
||||
*
|
||||
* expect(injectorAlias.get(Vehicle)).toBe(injectorAlias.get(Car));
|
||||
* expect(injectorAlias.get(Vehicle) instanceof Car).toBe(true);
|
||||
*
|
||||
* expect(injectorClass.get(Vehicle)).not.toBe(injectorClass.get(Car));
|
||||
* expect(injectorClass.get(Vehicle) instanceof Car).toBe(true);
|
||||
* ```
|
||||
*/
|
||||
toAlias(aliasToken: /*Type*/ any): Provider {
|
||||
if (isBlank(aliasToken)) {
|
||||
throw new BaseException(`Can not alias ${stringify(this.token)} to a blank value!`);
|
||||
}
|
||||
return new Provider(this.token, {useExisting: aliasToken});
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a DI token to a function which computes the value.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/OejNIfTT3zb1iBxaIYOb?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* provide(Number, {useFactory: () => { return 1+2; }}),
|
||||
* provide(String, {useFactory: (v) => { return "Value: " + v; }, deps: [Number]})
|
||||
* ]);
|
||||
*
|
||||
* expect(injector.get(Number)).toEqual(3);
|
||||
* expect(injector.get(String)).toEqual('Value: 3');
|
||||
* ```
|
||||
*/
|
||||
toFactory(factory: Function, dependencies?: any[]): Provider {
|
||||
if (!isFunction(factory)) {
|
||||
throw new BaseException(
|
||||
`Trying to create a factory provider but "${stringify(factory)}" is not a function!`);
|
||||
}
|
||||
return new Provider(this.token, {useFactory: factory, deps: dependencies});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Provider}.
|
||||
*
|
||||
* See {@link Provider} for more details.
|
||||
*
|
||||
* <!-- TODO: improve the docs -->
|
||||
*/
|
||||
export function provide(token, {useClass, useValue, useExisting, useFactory, deps, multi}: {
|
||||
useClass?: Type,
|
||||
useValue?: any,
|
||||
useExisting?: any,
|
||||
useFactory?: Function,
|
||||
deps?: Object[],
|
||||
multi?: boolean
|
||||
}): Provider {
|
||||
return new Provider(token, {
|
||||
useClass: useClass,
|
||||
useValue: useValue,
|
||||
useExisting: useExisting,
|
||||
useFactory: useFactory,
|
||||
deps: deps,
|
||||
multi: multi
|
||||
});
|
||||
}
|
268
modules/@angular/core/src/di/reflective_exceptions.ts
Normal file
268
modules/@angular/core/src/di/reflective_exceptions.ts
Normal file
@ -0,0 +1,268 @@
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {stringify, isBlank} from 'angular2/src/facade/lang';
|
||||
import {BaseException, WrappedException, unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {ReflectiveKey} from './reflective_key';
|
||||
import {ReflectiveInjector} from './reflective_injector';
|
||||
|
||||
function findFirstClosedCycle(keys: any[]): any[] {
|
||||
var res = [];
|
||||
for (var i = 0; i < keys.length; ++i) {
|
||||
if (ListWrapper.contains(res, keys[i])) {
|
||||
res.push(keys[i]);
|
||||
return res;
|
||||
} else {
|
||||
res.push(keys[i]);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function constructResolvingPath(keys: any[]): string {
|
||||
if (keys.length > 1) {
|
||||
var reversed = findFirstClosedCycle(ListWrapper.reversed(keys));
|
||||
var tokenStrs = reversed.map(k => stringify(k.token));
|
||||
return " (" + tokenStrs.join(' -> ') + ")";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Base class for all errors arising from misconfigured providers.
|
||||
*/
|
||||
export class AbstractProviderError extends BaseException {
|
||||
/** @internal */
|
||||
message: string;
|
||||
|
||||
/** @internal */
|
||||
keys: ReflectiveKey[];
|
||||
|
||||
/** @internal */
|
||||
injectors: ReflectiveInjector[];
|
||||
|
||||
/** @internal */
|
||||
constructResolvingMessage: Function;
|
||||
|
||||
constructor(injector: ReflectiveInjector, key: ReflectiveKey,
|
||||
constructResolvingMessage: Function) {
|
||||
super("DI Exception");
|
||||
this.keys = [key];
|
||||
this.injectors = [injector];
|
||||
this.constructResolvingMessage = constructResolvingMessage;
|
||||
this.message = this.constructResolvingMessage(this.keys);
|
||||
}
|
||||
|
||||
addKey(injector: ReflectiveInjector, key: ReflectiveKey): void {
|
||||
this.injectors.push(injector);
|
||||
this.keys.push(key);
|
||||
this.message = this.constructResolvingMessage(this.keys);
|
||||
}
|
||||
|
||||
get context() { return this.injectors[this.injectors.length - 1].debugContext(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when trying to retrieve a dependency by `Key` from {@link Injector}, but the
|
||||
* {@link Injector} does not have a {@link Provider} for {@link Key}.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/vq8D3FRB9aGbnWJqtEPE?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* class A {
|
||||
* constructor(b:B) {}
|
||||
* }
|
||||
*
|
||||
* expect(() => Injector.resolveAndCreate([A])).toThrowError();
|
||||
* ```
|
||||
*/
|
||||
export class NoProviderError extends AbstractProviderError {
|
||||
constructor(injector: ReflectiveInjector, key: ReflectiveKey) {
|
||||
super(injector, key, function(keys: any[]) {
|
||||
var first = stringify(ListWrapper.first(keys).token);
|
||||
return `No provider for ${first}!${constructResolvingPath(keys)}`;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when dependencies form a cycle.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/wYQdNos0Tzql3ei1EV9j?p=info))
|
||||
*
|
||||
* ```typescript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* provide("one", {useFactory: (two) => "two", deps: [[new Inject("two")]]}),
|
||||
* provide("two", {useFactory: (one) => "one", deps: [[new Inject("one")]]})
|
||||
* ]);
|
||||
*
|
||||
* expect(() => injector.get("one")).toThrowError();
|
||||
* ```
|
||||
*
|
||||
* Retrieving `A` or `B` throws a `CyclicDependencyError` as the graph above cannot be constructed.
|
||||
*/
|
||||
export class CyclicDependencyError extends AbstractProviderError {
|
||||
constructor(injector: ReflectiveInjector, key: ReflectiveKey) {
|
||||
super(injector, key, function(keys: any[]) {
|
||||
return `Cannot instantiate cyclic dependency!${constructResolvingPath(keys)}`;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a constructing type returns with an Error.
|
||||
*
|
||||
* The `InstantiationError` class contains the original error plus the dependency graph which caused
|
||||
* this object to be instantiated.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/7aWYdcqTQsP0eNqEdUAf?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* class A {
|
||||
* constructor() {
|
||||
* throw new Error('message');
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* var injector = Injector.resolveAndCreate([A]);
|
||||
|
||||
* try {
|
||||
* injector.get(A);
|
||||
* } catch (e) {
|
||||
* expect(e instanceof InstantiationError).toBe(true);
|
||||
* expect(e.originalException.message).toEqual("message");
|
||||
* expect(e.originalStack).toBeDefined();
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export class InstantiationError extends WrappedException {
|
||||
/** @internal */
|
||||
keys: ReflectiveKey[];
|
||||
|
||||
/** @internal */
|
||||
injectors: ReflectiveInjector[];
|
||||
|
||||
constructor(injector: ReflectiveInjector, originalException, originalStack, key: ReflectiveKey) {
|
||||
super("DI Exception", originalException, originalStack, null);
|
||||
this.keys = [key];
|
||||
this.injectors = [injector];
|
||||
}
|
||||
|
||||
addKey(injector: ReflectiveInjector, key: ReflectiveKey): void {
|
||||
this.injectors.push(injector);
|
||||
this.keys.push(key);
|
||||
}
|
||||
|
||||
get wrapperMessage(): string {
|
||||
var first = stringify(ListWrapper.first(this.keys).token);
|
||||
return `Error during instantiation of ${first}!${constructResolvingPath(this.keys)}.`;
|
||||
}
|
||||
|
||||
get causeKey(): ReflectiveKey { return this.keys[0]; }
|
||||
|
||||
get context() { return this.injectors[this.injectors.length - 1].debugContext(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when an object other then {@link Provider} (or `Type`) is passed to {@link Injector}
|
||||
* creation.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/YatCFbPAMCL0JSSQ4mvH?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* expect(() => Injector.resolveAndCreate(["not a type"])).toThrowError();
|
||||
* ```
|
||||
*/
|
||||
export class InvalidProviderError extends BaseException {
|
||||
constructor(provider) {
|
||||
super("Invalid provider - only instances of Provider and Type are allowed, got: " +
|
||||
provider.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when the class has no annotation information.
|
||||
*
|
||||
* Lack of annotation information prevents the {@link Injector} from determining which dependencies
|
||||
* need to be injected into the constructor.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/rHnZtlNS7vJOPQ6pcVkm?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* class A {
|
||||
* constructor(b) {}
|
||||
* }
|
||||
*
|
||||
* expect(() => Injector.resolveAndCreate([A])).toThrowError();
|
||||
* ```
|
||||
*
|
||||
* This error is also thrown when the class not marked with {@link Injectable} has parameter types.
|
||||
*
|
||||
* ```typescript
|
||||
* class B {}
|
||||
*
|
||||
* class A {
|
||||
* constructor(b:B) {} // no information about the parameter types of A is available at runtime.
|
||||
* }
|
||||
*
|
||||
* expect(() => Injector.resolveAndCreate([A,B])).toThrowError();
|
||||
* ```
|
||||
*/
|
||||
export class NoAnnotationError extends BaseException {
|
||||
constructor(typeOrFunc, params: any[][]) {
|
||||
super(NoAnnotationError._genMessage(typeOrFunc, params));
|
||||
}
|
||||
|
||||
private static _genMessage(typeOrFunc, params: any[][]) {
|
||||
var signature = [];
|
||||
for (var i = 0, ii = params.length; i < ii; i++) {
|
||||
var parameter = params[i];
|
||||
if (isBlank(parameter) || parameter.length == 0) {
|
||||
signature.push('?');
|
||||
} else {
|
||||
signature.push(parameter.map(stringify).join(' '));
|
||||
}
|
||||
}
|
||||
return "Cannot resolve all parameters for '" + stringify(typeOrFunc) + "'(" +
|
||||
signature.join(', ') + "). " +
|
||||
"Make sure that all the parameters are decorated with Inject or have valid type annotations and that '" +
|
||||
stringify(typeOrFunc) + "' is decorated with Injectable.";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when getting an object by index.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/bRs0SX2OTQiJzqvjgl8P?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* class A {}
|
||||
*
|
||||
* var injector = Injector.resolveAndCreate([A]);
|
||||
*
|
||||
* expect(() => injector.getAt(100)).toThrowError();
|
||||
* ```
|
||||
*/
|
||||
export class OutOfBoundsError extends BaseException {
|
||||
constructor(index) { super(`Index ${index} is out-of-bounds.`); }
|
||||
}
|
||||
|
||||
// TODO: add a working example after alpha38 is released
|
||||
/**
|
||||
* Thrown when a multi provider and a regular provider are bound to the same token.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```typescript
|
||||
* expect(() => Injector.resolveAndCreate([
|
||||
* new Provider("Strings", {useValue: "string1", multi: true}),
|
||||
* new Provider("Strings", {useValue: "string2", multi: false})
|
||||
* ])).toThrowError();
|
||||
* ```
|
||||
*/
|
||||
export class MixingMultiProvidersWithRegularProvidersError extends BaseException {
|
||||
constructor(provider1, provider2) {
|
||||
super("Cannot mix multi providers and regular providers, got: " + provider1.toString() + " " +
|
||||
provider2.toString());
|
||||
}
|
||||
}
|
889
modules/@angular/core/src/di/reflective_injector.ts
Normal file
889
modules/@angular/core/src/di/reflective_injector.ts
Normal file
@ -0,0 +1,889 @@
|
||||
import {Map, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {Provider, ProviderBuilder, provide} from './provider';
|
||||
import {
|
||||
ResolvedReflectiveProvider,
|
||||
ReflectiveDependency,
|
||||
ResolvedReflectiveFactory,
|
||||
resolveReflectiveProviders
|
||||
} from './reflective_provider';
|
||||
import {
|
||||
AbstractProviderError,
|
||||
NoProviderError,
|
||||
CyclicDependencyError,
|
||||
InstantiationError,
|
||||
InvalidProviderError,
|
||||
OutOfBoundsError
|
||||
} from './reflective_exceptions';
|
||||
import {Type, isPresent} from 'angular2/src/facade/lang';
|
||||
import {BaseException, unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {ReflectiveKey} from './reflective_key';
|
||||
import {SelfMetadata, HostMetadata, SkipSelfMetadata} from './metadata';
|
||||
import {Injector, THROW_IF_NOT_FOUND} from './injector';
|
||||
|
||||
var __unused: Type; // avoid unused import when Type union types are erased
|
||||
|
||||
// Threshold for the dynamic version
|
||||
const _MAX_CONSTRUCTION_COUNTER = 10;
|
||||
const UNDEFINED = /*@ts2dart_const*/ new Object();
|
||||
|
||||
export interface ReflectiveProtoInjectorStrategy {
|
||||
getProviderAtIndex(index: number): ResolvedReflectiveProvider;
|
||||
createInjectorStrategy(inj: ReflectiveInjector_): ReflectiveInjectorStrategy;
|
||||
}
|
||||
|
||||
export class ReflectiveProtoInjectorInlineStrategy implements ReflectiveProtoInjectorStrategy {
|
||||
provider0: ResolvedReflectiveProvider = null;
|
||||
provider1: ResolvedReflectiveProvider = null;
|
||||
provider2: ResolvedReflectiveProvider = null;
|
||||
provider3: ResolvedReflectiveProvider = null;
|
||||
provider4: ResolvedReflectiveProvider = null;
|
||||
provider5: ResolvedReflectiveProvider = null;
|
||||
provider6: ResolvedReflectiveProvider = null;
|
||||
provider7: ResolvedReflectiveProvider = null;
|
||||
provider8: ResolvedReflectiveProvider = null;
|
||||
provider9: ResolvedReflectiveProvider = null;
|
||||
|
||||
keyId0: number = null;
|
||||
keyId1: number = null;
|
||||
keyId2: number = null;
|
||||
keyId3: number = null;
|
||||
keyId4: number = null;
|
||||
keyId5: number = null;
|
||||
keyId6: number = null;
|
||||
keyId7: number = null;
|
||||
keyId8: number = null;
|
||||
keyId9: number = null;
|
||||
|
||||
constructor(protoEI: ReflectiveProtoInjector, providers: ResolvedReflectiveProvider[]) {
|
||||
var length = providers.length;
|
||||
|
||||
if (length > 0) {
|
||||
this.provider0 = providers[0];
|
||||
this.keyId0 = providers[0].key.id;
|
||||
}
|
||||
if (length > 1) {
|
||||
this.provider1 = providers[1];
|
||||
this.keyId1 = providers[1].key.id;
|
||||
}
|
||||
if (length > 2) {
|
||||
this.provider2 = providers[2];
|
||||
this.keyId2 = providers[2].key.id;
|
||||
}
|
||||
if (length > 3) {
|
||||
this.provider3 = providers[3];
|
||||
this.keyId3 = providers[3].key.id;
|
||||
}
|
||||
if (length > 4) {
|
||||
this.provider4 = providers[4];
|
||||
this.keyId4 = providers[4].key.id;
|
||||
}
|
||||
if (length > 5) {
|
||||
this.provider5 = providers[5];
|
||||
this.keyId5 = providers[5].key.id;
|
||||
}
|
||||
if (length > 6) {
|
||||
this.provider6 = providers[6];
|
||||
this.keyId6 = providers[6].key.id;
|
||||
}
|
||||
if (length > 7) {
|
||||
this.provider7 = providers[7];
|
||||
this.keyId7 = providers[7].key.id;
|
||||
}
|
||||
if (length > 8) {
|
||||
this.provider8 = providers[8];
|
||||
this.keyId8 = providers[8].key.id;
|
||||
}
|
||||
if (length > 9) {
|
||||
this.provider9 = providers[9];
|
||||
this.keyId9 = providers[9].key.id;
|
||||
}
|
||||
}
|
||||
|
||||
getProviderAtIndex(index: number): ResolvedReflectiveProvider {
|
||||
if (index == 0) return this.provider0;
|
||||
if (index == 1) return this.provider1;
|
||||
if (index == 2) return this.provider2;
|
||||
if (index == 3) return this.provider3;
|
||||
if (index == 4) return this.provider4;
|
||||
if (index == 5) return this.provider5;
|
||||
if (index == 6) return this.provider6;
|
||||
if (index == 7) return this.provider7;
|
||||
if (index == 8) return this.provider8;
|
||||
if (index == 9) return this.provider9;
|
||||
throw new OutOfBoundsError(index);
|
||||
}
|
||||
|
||||
createInjectorStrategy(injector: ReflectiveInjector_): ReflectiveInjectorStrategy {
|
||||
return new ReflectiveInjectorInlineStrategy(injector, this);
|
||||
}
|
||||
}
|
||||
|
||||
export class ReflectiveProtoInjectorDynamicStrategy implements ReflectiveProtoInjectorStrategy {
|
||||
keyIds: number[];
|
||||
|
||||
constructor(protoInj: ReflectiveProtoInjector, public providers: ResolvedReflectiveProvider[]) {
|
||||
var len = providers.length;
|
||||
|
||||
this.keyIds = ListWrapper.createFixedSize(len);
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
this.keyIds[i] = providers[i].key.id;
|
||||
}
|
||||
}
|
||||
|
||||
getProviderAtIndex(index: number): ResolvedReflectiveProvider {
|
||||
if (index < 0 || index >= this.providers.length) {
|
||||
throw new OutOfBoundsError(index);
|
||||
}
|
||||
return this.providers[index];
|
||||
}
|
||||
|
||||
createInjectorStrategy(ei: ReflectiveInjector_): ReflectiveInjectorStrategy {
|
||||
return new ReflectiveInjectorDynamicStrategy(this, ei);
|
||||
}
|
||||
}
|
||||
|
||||
export class ReflectiveProtoInjector {
|
||||
static fromResolvedProviders(providers: ResolvedReflectiveProvider[]): ReflectiveProtoInjector {
|
||||
return new ReflectiveProtoInjector(providers);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_strategy: ReflectiveProtoInjectorStrategy;
|
||||
numberOfProviders: number;
|
||||
|
||||
constructor(providers: ResolvedReflectiveProvider[]) {
|
||||
this.numberOfProviders = providers.length;
|
||||
this._strategy = providers.length > _MAX_CONSTRUCTION_COUNTER ?
|
||||
new ReflectiveProtoInjectorDynamicStrategy(this, providers) :
|
||||
new ReflectiveProtoInjectorInlineStrategy(this, providers);
|
||||
}
|
||||
|
||||
getProviderAtIndex(index: number): ResolvedReflectiveProvider {
|
||||
return this._strategy.getProviderAtIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export interface ReflectiveInjectorStrategy {
|
||||
getObjByKeyId(keyId: number): any;
|
||||
getObjAtIndex(index: number): any;
|
||||
getMaxNumberOfObjects(): number;
|
||||
|
||||
resetConstructionCounter(): void;
|
||||
instantiateProvider(provider: ResolvedReflectiveProvider): any;
|
||||
}
|
||||
|
||||
export class ReflectiveInjectorInlineStrategy implements ReflectiveInjectorStrategy {
|
||||
obj0: any = UNDEFINED;
|
||||
obj1: any = UNDEFINED;
|
||||
obj2: any = UNDEFINED;
|
||||
obj3: any = UNDEFINED;
|
||||
obj4: any = UNDEFINED;
|
||||
obj5: any = UNDEFINED;
|
||||
obj6: any = UNDEFINED;
|
||||
obj7: any = UNDEFINED;
|
||||
obj8: any = UNDEFINED;
|
||||
obj9: any = UNDEFINED;
|
||||
|
||||
constructor(public injector: ReflectiveInjector_,
|
||||
public protoStrategy: ReflectiveProtoInjectorInlineStrategy) {}
|
||||
|
||||
resetConstructionCounter(): void { this.injector._constructionCounter = 0; }
|
||||
|
||||
instantiateProvider(provider: ResolvedReflectiveProvider): any {
|
||||
return this.injector._new(provider);
|
||||
}
|
||||
|
||||
getObjByKeyId(keyId: number): any {
|
||||
var p = this.protoStrategy;
|
||||
var inj = this.injector;
|
||||
|
||||
if (p.keyId0 === keyId) {
|
||||
if (this.obj0 === UNDEFINED) {
|
||||
this.obj0 = inj._new(p.provider0);
|
||||
}
|
||||
return this.obj0;
|
||||
}
|
||||
if (p.keyId1 === keyId) {
|
||||
if (this.obj1 === UNDEFINED) {
|
||||
this.obj1 = inj._new(p.provider1);
|
||||
}
|
||||
return this.obj1;
|
||||
}
|
||||
if (p.keyId2 === keyId) {
|
||||
if (this.obj2 === UNDEFINED) {
|
||||
this.obj2 = inj._new(p.provider2);
|
||||
}
|
||||
return this.obj2;
|
||||
}
|
||||
if (p.keyId3 === keyId) {
|
||||
if (this.obj3 === UNDEFINED) {
|
||||
this.obj3 = inj._new(p.provider3);
|
||||
}
|
||||
return this.obj3;
|
||||
}
|
||||
if (p.keyId4 === keyId) {
|
||||
if (this.obj4 === UNDEFINED) {
|
||||
this.obj4 = inj._new(p.provider4);
|
||||
}
|
||||
return this.obj4;
|
||||
}
|
||||
if (p.keyId5 === keyId) {
|
||||
if (this.obj5 === UNDEFINED) {
|
||||
this.obj5 = inj._new(p.provider5);
|
||||
}
|
||||
return this.obj5;
|
||||
}
|
||||
if (p.keyId6 === keyId) {
|
||||
if (this.obj6 === UNDEFINED) {
|
||||
this.obj6 = inj._new(p.provider6);
|
||||
}
|
||||
return this.obj6;
|
||||
}
|
||||
if (p.keyId7 === keyId) {
|
||||
if (this.obj7 === UNDEFINED) {
|
||||
this.obj7 = inj._new(p.provider7);
|
||||
}
|
||||
return this.obj7;
|
||||
}
|
||||
if (p.keyId8 === keyId) {
|
||||
if (this.obj8 === UNDEFINED) {
|
||||
this.obj8 = inj._new(p.provider8);
|
||||
}
|
||||
return this.obj8;
|
||||
}
|
||||
if (p.keyId9 === keyId) {
|
||||
if (this.obj9 === UNDEFINED) {
|
||||
this.obj9 = inj._new(p.provider9);
|
||||
}
|
||||
return this.obj9;
|
||||
}
|
||||
|
||||
return UNDEFINED;
|
||||
}
|
||||
|
||||
getObjAtIndex(index: number): any {
|
||||
if (index == 0) return this.obj0;
|
||||
if (index == 1) return this.obj1;
|
||||
if (index == 2) return this.obj2;
|
||||
if (index == 3) return this.obj3;
|
||||
if (index == 4) return this.obj4;
|
||||
if (index == 5) return this.obj5;
|
||||
if (index == 6) return this.obj6;
|
||||
if (index == 7) return this.obj7;
|
||||
if (index == 8) return this.obj8;
|
||||
if (index == 9) return this.obj9;
|
||||
throw new OutOfBoundsError(index);
|
||||
}
|
||||
|
||||
getMaxNumberOfObjects(): number { return _MAX_CONSTRUCTION_COUNTER; }
|
||||
}
|
||||
|
||||
|
||||
export class ReflectiveInjectorDynamicStrategy implements ReflectiveInjectorStrategy {
|
||||
objs: any[];
|
||||
|
||||
constructor(public protoStrategy: ReflectiveProtoInjectorDynamicStrategy,
|
||||
public injector: ReflectiveInjector_) {
|
||||
this.objs = ListWrapper.createFixedSize(protoStrategy.providers.length);
|
||||
ListWrapper.fill(this.objs, UNDEFINED);
|
||||
}
|
||||
|
||||
resetConstructionCounter(): void { this.injector._constructionCounter = 0; }
|
||||
|
||||
instantiateProvider(provider: ResolvedReflectiveProvider): any {
|
||||
return this.injector._new(provider);
|
||||
}
|
||||
|
||||
getObjByKeyId(keyId: number): any {
|
||||
var p = this.protoStrategy;
|
||||
|
||||
for (var i = 0; i < p.keyIds.length; i++) {
|
||||
if (p.keyIds[i] === keyId) {
|
||||
if (this.objs[i] === UNDEFINED) {
|
||||
this.objs[i] = this.injector._new(p.providers[i]);
|
||||
}
|
||||
|
||||
return this.objs[i];
|
||||
}
|
||||
}
|
||||
|
||||
return UNDEFINED;
|
||||
}
|
||||
|
||||
getObjAtIndex(index: number): any {
|
||||
if (index < 0 || index >= this.objs.length) {
|
||||
throw new OutOfBoundsError(index);
|
||||
}
|
||||
|
||||
return this.objs[index];
|
||||
}
|
||||
|
||||
getMaxNumberOfObjects(): number { return this.objs.length; }
|
||||
}
|
||||
|
||||
/**
|
||||
* A ReflectiveDependency injection container used for instantiating objects and resolving
|
||||
* dependencies.
|
||||
*
|
||||
* An `Injector` is a replacement for a `new` operator, which can automatically resolve the
|
||||
* constructor dependencies.
|
||||
*
|
||||
* In typical use, application code asks for the dependencies in the constructor and they are
|
||||
* resolved by the `Injector`.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/jzjec0?p=preview))
|
||||
*
|
||||
* The following example creates an `Injector` configured to create `Engine` and `Car`.
|
||||
*
|
||||
* ```typescript
|
||||
* @Injectable()
|
||||
* class Engine {
|
||||
* }
|
||||
*
|
||||
* @Injectable()
|
||||
* class Car {
|
||||
* constructor(public engine:Engine) {}
|
||||
* }
|
||||
*
|
||||
* var injector = ReflectiveInjector.resolveAndCreate([Car, Engine]);
|
||||
* var car = injector.get(Car);
|
||||
* expect(car instanceof Car).toBe(true);
|
||||
* expect(car.engine instanceof Engine).toBe(true);
|
||||
* ```
|
||||
*
|
||||
* Notice, we don't use the `new` operator because we explicitly want to have the `Injector`
|
||||
* resolve all of the object's dependencies automatically.
|
||||
*/
|
||||
export abstract class ReflectiveInjector implements Injector {
|
||||
/**
|
||||
* Turns an array of provider definitions into an array of resolved providers.
|
||||
*
|
||||
* A resolution is a process of flattening multiple nested arrays and converting individual
|
||||
* providers into an array of {@link ResolvedReflectiveProvider}s.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/AiXTHi?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Injectable()
|
||||
* class Engine {
|
||||
* }
|
||||
*
|
||||
* @Injectable()
|
||||
* class Car {
|
||||
* constructor(public engine:Engine) {}
|
||||
* }
|
||||
*
|
||||
* var providers = ReflectiveInjector.resolve([Car, [[Engine]]]);
|
||||
*
|
||||
* expect(providers.length).toEqual(2);
|
||||
*
|
||||
* expect(providers[0] instanceof ResolvedReflectiveProvider).toBe(true);
|
||||
* expect(providers[0].key.displayName).toBe("Car");
|
||||
* expect(providers[0].dependencies.length).toEqual(1);
|
||||
* expect(providers[0].factory).toBeDefined();
|
||||
*
|
||||
* expect(providers[1].key.displayName).toBe("Engine");
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* See {@link ReflectiveInjector#fromResolvedProviders} for more info.
|
||||
*/
|
||||
static resolve(providers: Array<Type | Provider | {[k: string]: any} | any[]>):
|
||||
ResolvedReflectiveProvider[] {
|
||||
return resolveReflectiveProviders(providers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves an array of providers and creates an injector from those providers.
|
||||
*
|
||||
* The passed-in providers can be an array of `Type`, {@link Provider},
|
||||
* or a recursive array of more providers.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/ePOccA?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Injectable()
|
||||
* class Engine {
|
||||
* }
|
||||
*
|
||||
* @Injectable()
|
||||
* class Car {
|
||||
* constructor(public engine:Engine) {}
|
||||
* }
|
||||
*
|
||||
* var injector = ReflectiveInjector.resolveAndCreate([Car, Engine]);
|
||||
* expect(injector.get(Car) instanceof Car).toBe(true);
|
||||
* ```
|
||||
*
|
||||
* This function is slower than the corresponding `fromResolvedProviders`
|
||||
* because it needs to resolve the passed-in providers first.
|
||||
* See {@link Injector#resolve} and {@link Injector#fromResolvedProviders}.
|
||||
*/
|
||||
static resolveAndCreate(providers: Array<Type | Provider | {[k: string]: any} | any[]>,
|
||||
parent: Injector = null): ReflectiveInjector {
|
||||
var ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
|
||||
return ReflectiveInjector.fromResolvedProviders(ResolvedReflectiveProviders, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an injector from previously resolved providers.
|
||||
*
|
||||
* This API is the recommended way to construct injectors in performance-sensitive parts.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/KrSMci?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Injectable()
|
||||
* class Engine {
|
||||
* }
|
||||
*
|
||||
* @Injectable()
|
||||
* class Car {
|
||||
* constructor(public engine:Engine) {}
|
||||
* }
|
||||
*
|
||||
* var providers = ReflectiveInjector.resolve([Car, Engine]);
|
||||
* var injector = ReflectiveInjector.fromResolvedProviders(providers);
|
||||
* expect(injector.get(Car) instanceof Car).toBe(true);
|
||||
* ```
|
||||
*/
|
||||
static fromResolvedProviders(providers: ResolvedReflectiveProvider[],
|
||||
parent: Injector = null): ReflectiveInjector {
|
||||
return new ReflectiveInjector_(ReflectiveProtoInjector.fromResolvedProviders(providers),
|
||||
parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
static fromResolvedBindings(providers: ResolvedReflectiveProvider[]): ReflectiveInjector {
|
||||
return ReflectiveInjector.fromResolvedProviders(providers);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parent of this injector.
|
||||
*
|
||||
* <!-- TODO: Add a link to the section of the user guide talking about hierarchical injection.
|
||||
* -->
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/eosMGo?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* var parent = ReflectiveInjector.resolveAndCreate([]);
|
||||
* var child = parent.resolveAndCreateChild([]);
|
||||
* expect(child.parent).toBe(parent);
|
||||
* ```
|
||||
*/
|
||||
get parent(): Injector { return unimplemented(); }
|
||||
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
debugContext(): any { return null; }
|
||||
|
||||
/**
|
||||
* Resolves an array of providers and creates a child injector from those providers.
|
||||
*
|
||||
* <!-- TODO: Add a link to the section of the user guide talking about hierarchical injection.
|
||||
* -->
|
||||
*
|
||||
* The passed-in providers can be an array of `Type`, {@link Provider},
|
||||
* or a recursive array of more providers.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/opB3T4?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* class ParentProvider {}
|
||||
* class ChildProvider {}
|
||||
*
|
||||
* var parent = ReflectiveInjector.resolveAndCreate([ParentProvider]);
|
||||
* var child = parent.resolveAndCreateChild([ChildProvider]);
|
||||
*
|
||||
* expect(child.get(ParentProvider) instanceof ParentProvider).toBe(true);
|
||||
* expect(child.get(ChildProvider) instanceof ChildProvider).toBe(true);
|
||||
* expect(child.get(ParentProvider)).toBe(parent.get(ParentProvider));
|
||||
* ```
|
||||
*
|
||||
* This function is slower than the corresponding `createChildFromResolved`
|
||||
* because it needs to resolve the passed-in providers first.
|
||||
* See {@link Injector#resolve} and {@link Injector#createChildFromResolved}.
|
||||
*/
|
||||
resolveAndCreateChild(
|
||||
providers: Array<Type | Provider | {[k: string]: any} | any[]>): ReflectiveInjector {
|
||||
return unimplemented();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a child injector from previously resolved providers.
|
||||
*
|
||||
* <!-- TODO: Add a link to the section of the user guide talking about hierarchical injection.
|
||||
* -->
|
||||
*
|
||||
* This API is the recommended way to construct injectors in performance-sensitive parts.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/VhyfjN?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* class ParentProvider {}
|
||||
* class ChildProvider {}
|
||||
*
|
||||
* var parentProviders = ReflectiveInjector.resolve([ParentProvider]);
|
||||
* var childProviders = ReflectiveInjector.resolve([ChildProvider]);
|
||||
*
|
||||
* var parent = ReflectiveInjector.fromResolvedProviders(parentProviders);
|
||||
* var child = parent.createChildFromResolved(childProviders);
|
||||
*
|
||||
* expect(child.get(ParentProvider) instanceof ParentProvider).toBe(true);
|
||||
* expect(child.get(ChildProvider) instanceof ChildProvider).toBe(true);
|
||||
* expect(child.get(ParentProvider)).toBe(parent.get(ParentProvider));
|
||||
* ```
|
||||
*/
|
||||
createChildFromResolved(providers: ResolvedReflectiveProvider[]): ReflectiveInjector {
|
||||
return unimplemented();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a provider and instantiates an object in the context of the injector.
|
||||
*
|
||||
* The created object does not get cached by the injector.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/yvVXoB?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Injectable()
|
||||
* class Engine {
|
||||
* }
|
||||
*
|
||||
* @Injectable()
|
||||
* class Car {
|
||||
* constructor(public engine:Engine) {}
|
||||
* }
|
||||
*
|
||||
* var injector = ReflectiveInjector.resolveAndCreate([Engine]);
|
||||
*
|
||||
* var car = injector.resolveAndInstantiate(Car);
|
||||
* expect(car.engine).toBe(injector.get(Engine));
|
||||
* expect(car).not.toBe(injector.resolveAndInstantiate(Car));
|
||||
* ```
|
||||
*/
|
||||
resolveAndInstantiate(provider: Type | Provider): any { return unimplemented(); }
|
||||
|
||||
/**
|
||||
* Instantiates an object using a resolved provider in the context of the injector.
|
||||
*
|
||||
* The created object does not get cached by the injector.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/ptCImQ?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Injectable()
|
||||
* class Engine {
|
||||
* }
|
||||
*
|
||||
* @Injectable()
|
||||
* class Car {
|
||||
* constructor(public engine:Engine) {}
|
||||
* }
|
||||
*
|
||||
* var injector = ReflectiveInjector.resolveAndCreate([Engine]);
|
||||
* var carProvider = ReflectiveInjector.resolve([Car])[0];
|
||||
* var car = injector.instantiateResolved(carProvider);
|
||||
* expect(car.engine).toBe(injector.get(Engine));
|
||||
* expect(car).not.toBe(injector.instantiateResolved(carProvider));
|
||||
* ```
|
||||
*/
|
||||
instantiateResolved(provider: ResolvedReflectiveProvider): any { return unimplemented(); }
|
||||
|
||||
abstract get(token: any, notFoundValue?: any): any;
|
||||
}
|
||||
|
||||
export class ReflectiveInjector_ implements ReflectiveInjector {
|
||||
private _strategy: ReflectiveInjectorStrategy;
|
||||
/** @internal */
|
||||
_constructionCounter: number = 0;
|
||||
/** @internal */
|
||||
public _proto: any /* ProtoInjector */;
|
||||
/** @internal */
|
||||
public _parent: Injector;
|
||||
/**
|
||||
* Private
|
||||
*/
|
||||
constructor(_proto: any /* ProtoInjector */, _parent: Injector = null,
|
||||
private _debugContext: Function = null) {
|
||||
this._proto = _proto;
|
||||
this._parent = _parent;
|
||||
this._strategy = _proto._strategy.createInjectorStrategy(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
debugContext(): any { return this._debugContext(); }
|
||||
|
||||
get(token: any, notFoundValue: any = /*@ts2dart_const*/ THROW_IF_NOT_FOUND): any {
|
||||
return this._getByKey(ReflectiveKey.get(token), null, null, notFoundValue);
|
||||
}
|
||||
|
||||
getAt(index: number): any { return this._strategy.getObjAtIndex(index); }
|
||||
|
||||
get parent(): Injector { return this._parent; }
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Internal. Do not use.
|
||||
* We return `any` not to export the InjectorStrategy type.
|
||||
*/
|
||||
get internalStrategy(): any { return this._strategy; }
|
||||
|
||||
resolveAndCreateChild(providers: Array<Type | Provider | any[]>): ReflectiveInjector {
|
||||
var ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
|
||||
return this.createChildFromResolved(ResolvedReflectiveProviders);
|
||||
}
|
||||
|
||||
createChildFromResolved(providers: ResolvedReflectiveProvider[]): ReflectiveInjector {
|
||||
var proto = new ReflectiveProtoInjector(providers);
|
||||
var inj = new ReflectiveInjector_(proto);
|
||||
inj._parent = this;
|
||||
return inj;
|
||||
}
|
||||
|
||||
resolveAndInstantiate(provider: Type | Provider): any {
|
||||
return this.instantiateResolved(ReflectiveInjector.resolve([provider])[0]);
|
||||
}
|
||||
|
||||
instantiateResolved(provider: ResolvedReflectiveProvider): any {
|
||||
return this._instantiateProvider(provider);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_new(provider: ResolvedReflectiveProvider): any {
|
||||
if (this._constructionCounter++ > this._strategy.getMaxNumberOfObjects()) {
|
||||
throw new CyclicDependencyError(this, provider.key);
|
||||
}
|
||||
return this._instantiateProvider(provider);
|
||||
}
|
||||
|
||||
private _instantiateProvider(provider: ResolvedReflectiveProvider): any {
|
||||
if (provider.multiProvider) {
|
||||
var res = ListWrapper.createFixedSize(provider.resolvedFactories.length);
|
||||
for (var i = 0; i < provider.resolvedFactories.length; ++i) {
|
||||
res[i] = this._instantiate(provider, provider.resolvedFactories[i]);
|
||||
}
|
||||
return res;
|
||||
} else {
|
||||
return this._instantiate(provider, provider.resolvedFactories[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private _instantiate(provider: ResolvedReflectiveProvider,
|
||||
ResolvedReflectiveFactory: ResolvedReflectiveFactory): any {
|
||||
var factory = ResolvedReflectiveFactory.factory;
|
||||
var deps = ResolvedReflectiveFactory.dependencies;
|
||||
var length = deps.length;
|
||||
|
||||
var d0: any;
|
||||
var d1: any;
|
||||
var d2: any;
|
||||
var d3: any;
|
||||
var d4: any;
|
||||
var d5: any;
|
||||
var d6: any;
|
||||
var d7: any;
|
||||
var d8: any;
|
||||
var d9: any;
|
||||
var d10: any;
|
||||
var d11: any;
|
||||
var d12: any;
|
||||
var d13: any;
|
||||
var d14: any;
|
||||
var d15: any;
|
||||
var d16: any;
|
||||
var d17: any;
|
||||
var d18: any;
|
||||
var d19: any;
|
||||
try {
|
||||
d0 = length > 0 ? this._getByReflectiveDependency(provider, deps[0]) : null;
|
||||
d1 = length > 1 ? this._getByReflectiveDependency(provider, deps[1]) : null;
|
||||
d2 = length > 2 ? this._getByReflectiveDependency(provider, deps[2]) : null;
|
||||
d3 = length > 3 ? this._getByReflectiveDependency(provider, deps[3]) : null;
|
||||
d4 = length > 4 ? this._getByReflectiveDependency(provider, deps[4]) : null;
|
||||
d5 = length > 5 ? this._getByReflectiveDependency(provider, deps[5]) : null;
|
||||
d6 = length > 6 ? this._getByReflectiveDependency(provider, deps[6]) : null;
|
||||
d7 = length > 7 ? this._getByReflectiveDependency(provider, deps[7]) : null;
|
||||
d8 = length > 8 ? this._getByReflectiveDependency(provider, deps[8]) : null;
|
||||
d9 = length > 9 ? this._getByReflectiveDependency(provider, deps[9]) : null;
|
||||
d10 = length > 10 ? this._getByReflectiveDependency(provider, deps[10]) : null;
|
||||
d11 = length > 11 ? this._getByReflectiveDependency(provider, deps[11]) : null;
|
||||
d12 = length > 12 ? this._getByReflectiveDependency(provider, deps[12]) : null;
|
||||
d13 = length > 13 ? this._getByReflectiveDependency(provider, deps[13]) : null;
|
||||
d14 = length > 14 ? this._getByReflectiveDependency(provider, deps[14]) : null;
|
||||
d15 = length > 15 ? this._getByReflectiveDependency(provider, deps[15]) : null;
|
||||
d16 = length > 16 ? this._getByReflectiveDependency(provider, deps[16]) : null;
|
||||
d17 = length > 17 ? this._getByReflectiveDependency(provider, deps[17]) : null;
|
||||
d18 = length > 18 ? this._getByReflectiveDependency(provider, deps[18]) : null;
|
||||
d19 = length > 19 ? this._getByReflectiveDependency(provider, deps[19]) : null;
|
||||
} catch (e) {
|
||||
if (e instanceof AbstractProviderError || e instanceof InstantiationError) {
|
||||
e.addKey(this, provider.key);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
var obj;
|
||||
try {
|
||||
switch (length) {
|
||||
case 0:
|
||||
obj = factory();
|
||||
break;
|
||||
case 1:
|
||||
obj = factory(d0);
|
||||
break;
|
||||
case 2:
|
||||
obj = factory(d0, d1);
|
||||
break;
|
||||
case 3:
|
||||
obj = factory(d0, d1, d2);
|
||||
break;
|
||||
case 4:
|
||||
obj = factory(d0, d1, d2, d3);
|
||||
break;
|
||||
case 5:
|
||||
obj = factory(d0, d1, d2, d3, d4);
|
||||
break;
|
||||
case 6:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5);
|
||||
break;
|
||||
case 7:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6);
|
||||
break;
|
||||
case 8:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7);
|
||||
break;
|
||||
case 9:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8);
|
||||
break;
|
||||
case 10:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9);
|
||||
break;
|
||||
case 11:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10);
|
||||
break;
|
||||
case 12:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11);
|
||||
break;
|
||||
case 13:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12);
|
||||
break;
|
||||
case 14:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13);
|
||||
break;
|
||||
case 15:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14);
|
||||
break;
|
||||
case 16:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15);
|
||||
break;
|
||||
case 17:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16);
|
||||
break;
|
||||
case 18:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16,
|
||||
d17);
|
||||
break;
|
||||
case 19:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16,
|
||||
d17, d18);
|
||||
break;
|
||||
case 20:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16,
|
||||
d17, d18, d19);
|
||||
break;
|
||||
default:
|
||||
throw new BaseException(
|
||||
`Cannot instantiate '${provider.key.displayName}' because it has more than 20 dependencies`);
|
||||
}
|
||||
} catch (e) {
|
||||
throw new InstantiationError(this, e, e.stack, provider.key);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
private _getByReflectiveDependency(provider: ResolvedReflectiveProvider,
|
||||
dep: ReflectiveDependency): any {
|
||||
return this._getByKey(dep.key, dep.lowerBoundVisibility, dep.upperBoundVisibility,
|
||||
dep.optional ? null : THROW_IF_NOT_FOUND);
|
||||
}
|
||||
|
||||
private _getByKey(key: ReflectiveKey, lowerBoundVisibility: Object, upperBoundVisibility: Object,
|
||||
notFoundValue: any): any {
|
||||
if (key === INJECTOR_KEY) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (upperBoundVisibility instanceof SelfMetadata) {
|
||||
return this._getByKeySelf(key, notFoundValue);
|
||||
|
||||
} else {
|
||||
return this._getByKeyDefault(key, notFoundValue, lowerBoundVisibility);
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_throwOrNull(key: ReflectiveKey, notFoundValue: any): any {
|
||||
if (notFoundValue !== THROW_IF_NOT_FOUND) {
|
||||
return notFoundValue;
|
||||
} else {
|
||||
throw new NoProviderError(this, key);
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_getByKeySelf(key: ReflectiveKey, notFoundValue: any): any {
|
||||
var obj = this._strategy.getObjByKeyId(key.id);
|
||||
return (obj !== UNDEFINED) ? obj : this._throwOrNull(key, notFoundValue);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_getByKeyDefault(key: ReflectiveKey, notFoundValue: any, lowerBoundVisibility: Object): any {
|
||||
var inj: Injector;
|
||||
|
||||
if (lowerBoundVisibility instanceof SkipSelfMetadata) {
|
||||
inj = this._parent;
|
||||
} else {
|
||||
inj = this;
|
||||
}
|
||||
|
||||
while (inj instanceof ReflectiveInjector_) {
|
||||
var inj_ = <ReflectiveInjector_>inj;
|
||||
var obj = inj_._strategy.getObjByKeyId(key.id);
|
||||
if (obj !== UNDEFINED) return obj;
|
||||
inj = inj_._parent;
|
||||
}
|
||||
if (inj !== null) {
|
||||
return inj.get(key.token, notFoundValue);
|
||||
} else {
|
||||
return this._throwOrNull(key, notFoundValue);
|
||||
}
|
||||
}
|
||||
|
||||
get displayName(): string {
|
||||
return `ReflectiveInjector(providers: [${_mapProviders(this, (b: ResolvedReflectiveProvider) => ` "${b.key.displayName}" `).join(", ")}])`;
|
||||
}
|
||||
|
||||
toString(): string { return this.displayName; }
|
||||
}
|
||||
|
||||
var INJECTOR_KEY = ReflectiveKey.get(Injector);
|
||||
|
||||
function _mapProviders(injector: ReflectiveInjector_, fn: Function): any[] {
|
||||
var res = [];
|
||||
for (var i = 0; i < injector._proto.numberOfProviders; ++i) {
|
||||
res.push(fn(injector._proto.getProviderAtIndex(i)));
|
||||
}
|
||||
return res;
|
||||
}
|
69
modules/@angular/core/src/di/reflective_key.ts
Normal file
69
modules/@angular/core/src/di/reflective_key.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import {stringify, Type, isBlank} from 'angular2/src/facade/lang';
|
||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
import {resolveForwardRef} from './forward_ref';
|
||||
|
||||
/**
|
||||
* A unique object used for retrieving items from the {@link ReflectiveInjector}.
|
||||
*
|
||||
* Keys have:
|
||||
* - a system-wide unique `id`.
|
||||
* - a `token`.
|
||||
*
|
||||
* `Key` is used internally by {@link ReflectiveInjector} because its system-wide unique `id` allows
|
||||
* the
|
||||
* injector to store created objects in a more efficient way.
|
||||
*
|
||||
* `Key` should not be created directly. {@link ReflectiveInjector} creates keys automatically when
|
||||
* resolving
|
||||
* providers.
|
||||
*/
|
||||
export class ReflectiveKey {
|
||||
/**
|
||||
* Private
|
||||
*/
|
||||
constructor(public token: Object, public id: number) {
|
||||
if (isBlank(token)) {
|
||||
throw new BaseException('Token must be defined!');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stringified token.
|
||||
*/
|
||||
get displayName(): string { return stringify(this.token); }
|
||||
|
||||
/**
|
||||
* Retrieves a `Key` for a token.
|
||||
*/
|
||||
static get(token: Object): ReflectiveKey {
|
||||
return _globalKeyRegistry.get(resolveForwardRef(token));
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns the number of keys registered in the system.
|
||||
*/
|
||||
static get numberOfKeys(): number { return _globalKeyRegistry.numberOfKeys; }
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class KeyRegistry {
|
||||
private _allKeys = new Map<Object, ReflectiveKey>();
|
||||
|
||||
get(token: Object): ReflectiveKey {
|
||||
if (token instanceof ReflectiveKey) return token;
|
||||
|
||||
if (this._allKeys.has(token)) {
|
||||
return this._allKeys.get(token);
|
||||
}
|
||||
|
||||
var newKey = new ReflectiveKey(token, ReflectiveKey.numberOfKeys);
|
||||
this._allKeys.set(token, newKey);
|
||||
return newKey;
|
||||
}
|
||||
|
||||
get numberOfKeys(): number { return this._allKeys.size; }
|
||||
}
|
||||
|
||||
var _globalKeyRegistry = new KeyRegistry();
|
289
modules/@angular/core/src/di/reflective_provider.ts
Normal file
289
modules/@angular/core/src/di/reflective_provider.ts
Normal file
@ -0,0 +1,289 @@
|
||||
import {Type, isBlank, isPresent, isArray, isType} from 'angular2/src/facade/lang';
|
||||
import {MapWrapper, ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||
import {ReflectiveKey} from './reflective_key';
|
||||
import {
|
||||
InjectMetadata,
|
||||
InjectableMetadata,
|
||||
OptionalMetadata,
|
||||
SelfMetadata,
|
||||
HostMetadata,
|
||||
SkipSelfMetadata,
|
||||
DependencyMetadata
|
||||
} from './metadata';
|
||||
import {
|
||||
NoAnnotationError,
|
||||
MixingMultiProvidersWithRegularProvidersError,
|
||||
InvalidProviderError
|
||||
} from './reflective_exceptions';
|
||||
import {resolveForwardRef} from './forward_ref';
|
||||
import {Provider, ProviderBuilder, provide} from './provider';
|
||||
import {isProviderLiteral, createProvider} from './provider_util';
|
||||
|
||||
/**
|
||||
* `Dependency` is used by the framework to extend DI.
|
||||
* This is internal to Angular and should not be used directly.
|
||||
*/
|
||||
export class ReflectiveDependency {
|
||||
constructor(public key: ReflectiveKey, public optional: boolean, public lowerBoundVisibility: any,
|
||||
public upperBoundVisibility: any, public properties: any[]) {}
|
||||
|
||||
static fromKey(key: ReflectiveKey): ReflectiveDependency {
|
||||
return new ReflectiveDependency(key, false, null, null, []);
|
||||
}
|
||||
}
|
||||
|
||||
const _EMPTY_LIST = /*@ts2dart_const*/[];
|
||||
|
||||
/**
|
||||
* An internal resolved representation of a {@link Provider} used by the {@link Injector}.
|
||||
*
|
||||
* It is usually created automatically by `Injector.resolveAndCreate`.
|
||||
*
|
||||
* It can be created manually, as follows:
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/RfEnhh8kUEI0G3qsnIeT?p%3Dpreview&p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* var resolvedProviders = Injector.resolve([new Provider('message', {useValue: 'Hello'})]);
|
||||
* var injector = Injector.fromResolvedProviders(resolvedProviders);
|
||||
*
|
||||
* expect(injector.get('message')).toEqual('Hello');
|
||||
* ```
|
||||
*/
|
||||
export interface ResolvedReflectiveProvider {
|
||||
/**
|
||||
* A key, usually a `Type`.
|
||||
*/
|
||||
key: ReflectiveKey;
|
||||
|
||||
/**
|
||||
* Factory function which can return an instance of an object represented by a key.
|
||||
*/
|
||||
resolvedFactories: ResolvedReflectiveFactory[];
|
||||
|
||||
/**
|
||||
* Indicates if the provider is a multi-provider or a regular provider.
|
||||
*/
|
||||
multiProvider: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link ResolvedProvider} instead.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
export interface ResolvedReflectiveBinding extends ResolvedReflectiveProvider {}
|
||||
|
||||
export class ResolvedReflectiveProvider_ implements ResolvedReflectiveBinding {
|
||||
constructor(public key: ReflectiveKey, public resolvedFactories: ResolvedReflectiveFactory[],
|
||||
public multiProvider: boolean) {}
|
||||
|
||||
get resolvedFactory(): ResolvedReflectiveFactory { return this.resolvedFactories[0]; }
|
||||
}
|
||||
|
||||
/**
|
||||
* An internal resolved representation of a factory function created by resolving {@link Provider}.
|
||||
*/
|
||||
export class ResolvedReflectiveFactory {
|
||||
constructor(
|
||||
/**
|
||||
* Factory function which can return an instance of an object represented by a key.
|
||||
*/
|
||||
public factory: Function,
|
||||
|
||||
/**
|
||||
* Arguments (dependencies) to the `factory` function.
|
||||
*/
|
||||
public dependencies: ReflectiveDependency[]) {}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolve a single provider.
|
||||
*/
|
||||
export function resolveReflectiveFactory(provider: Provider): ResolvedReflectiveFactory {
|
||||
var factoryFn: Function;
|
||||
var resolvedDeps;
|
||||
if (isPresent(provider.useClass)) {
|
||||
var useClass = resolveForwardRef(provider.useClass);
|
||||
factoryFn = reflector.factory(useClass);
|
||||
resolvedDeps = _dependenciesFor(useClass);
|
||||
} else if (isPresent(provider.useExisting)) {
|
||||
factoryFn = (aliasInstance) => aliasInstance;
|
||||
resolvedDeps = [ReflectiveDependency.fromKey(ReflectiveKey.get(provider.useExisting))];
|
||||
} else if (isPresent(provider.useFactory)) {
|
||||
factoryFn = provider.useFactory;
|
||||
resolvedDeps = constructDependencies(provider.useFactory, provider.dependencies);
|
||||
} else {
|
||||
factoryFn = () => provider.useValue;
|
||||
resolvedDeps = _EMPTY_LIST;
|
||||
}
|
||||
return new ResolvedReflectiveFactory(factoryFn, resolvedDeps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the {@link Provider} into {@link ResolvedProvider}.
|
||||
*
|
||||
* {@link Injector} internally only uses {@link ResolvedProvider}, {@link Provider} contains
|
||||
* convenience provider syntax.
|
||||
*/
|
||||
export function resolveReflectiveProvider(provider: Provider): ResolvedReflectiveProvider {
|
||||
return new ResolvedReflectiveProvider_(ReflectiveKey.get(provider.token),
|
||||
[resolveReflectiveFactory(provider)], provider.multi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a list of Providers.
|
||||
*/
|
||||
export function resolveReflectiveProviders(
|
||||
providers: Array<Type | Provider | {[k: string]: any} | any[]>): ResolvedReflectiveProvider[] {
|
||||
var normalized = _normalizeProviders(providers, []);
|
||||
var resolved = normalized.map(resolveReflectiveProvider);
|
||||
return MapWrapper.values(
|
||||
mergeResolvedReflectiveProviders(resolved, new Map<number, ResolvedReflectiveProvider>()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges a list of ResolvedProviders into a list where
|
||||
* each key is contained exactly once and multi providers
|
||||
* have been merged.
|
||||
*/
|
||||
export function mergeResolvedReflectiveProviders(
|
||||
providers: ResolvedReflectiveProvider[],
|
||||
normalizedProvidersMap: Map<number, ResolvedReflectiveProvider>):
|
||||
Map<number, ResolvedReflectiveProvider> {
|
||||
for (var i = 0; i < providers.length; i++) {
|
||||
var provider = providers[i];
|
||||
var existing = normalizedProvidersMap.get(provider.key.id);
|
||||
if (isPresent(existing)) {
|
||||
if (provider.multiProvider !== existing.multiProvider) {
|
||||
throw new MixingMultiProvidersWithRegularProvidersError(existing, provider);
|
||||
}
|
||||
if (provider.multiProvider) {
|
||||
for (var j = 0; j < provider.resolvedFactories.length; j++) {
|
||||
existing.resolvedFactories.push(provider.resolvedFactories[j]);
|
||||
}
|
||||
} else {
|
||||
normalizedProvidersMap.set(provider.key.id, provider);
|
||||
}
|
||||
} else {
|
||||
var resolvedProvider;
|
||||
if (provider.multiProvider) {
|
||||
resolvedProvider = new ResolvedReflectiveProvider_(
|
||||
provider.key, ListWrapper.clone(provider.resolvedFactories), provider.multiProvider);
|
||||
} else {
|
||||
resolvedProvider = provider;
|
||||
}
|
||||
normalizedProvidersMap.set(provider.key.id, resolvedProvider);
|
||||
}
|
||||
}
|
||||
return normalizedProvidersMap;
|
||||
}
|
||||
|
||||
function _normalizeProviders(
|
||||
providers: Array<Type | Provider | {[k: string]: any} | ProviderBuilder | any[]>,
|
||||
res: Provider[]): Provider[] {
|
||||
providers.forEach(b => {
|
||||
if (b instanceof Type) {
|
||||
res.push(provide(b, {useClass: b}));
|
||||
|
||||
} else if (b instanceof Provider) {
|
||||
res.push(b);
|
||||
|
||||
} else if (isProviderLiteral(b)) {
|
||||
res.push(createProvider(b));
|
||||
|
||||
} else if (b instanceof Array) {
|
||||
_normalizeProviders(b, res);
|
||||
|
||||
} else if (b instanceof ProviderBuilder) {
|
||||
throw new InvalidProviderError(b.token);
|
||||
|
||||
} else {
|
||||
throw new InvalidProviderError(b);
|
||||
}
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
export function constructDependencies(typeOrFunc: any,
|
||||
dependencies: any[]): ReflectiveDependency[] {
|
||||
if (isBlank(dependencies)) {
|
||||
return _dependenciesFor(typeOrFunc);
|
||||
} else {
|
||||
var params: any[][] = dependencies.map(t => [t]);
|
||||
return dependencies.map(t => _extractToken(typeOrFunc, t, params));
|
||||
}
|
||||
}
|
||||
|
||||
function _dependenciesFor(typeOrFunc: any): ReflectiveDependency[] {
|
||||
var params = reflector.parameters(typeOrFunc);
|
||||
if (isBlank(params)) return [];
|
||||
if (params.some(isBlank)) {
|
||||
throw new NoAnnotationError(typeOrFunc, params);
|
||||
}
|
||||
return params.map((p: any[]) => _extractToken(typeOrFunc, p, params));
|
||||
}
|
||||
|
||||
function _extractToken(typeOrFunc, metadata /*any[] | any*/,
|
||||
params: any[][]): ReflectiveDependency {
|
||||
var depProps = [];
|
||||
var token = null;
|
||||
var optional = false;
|
||||
|
||||
if (!isArray(metadata)) {
|
||||
if (metadata instanceof InjectMetadata) {
|
||||
return _createDependency(metadata.token, optional, null, null, depProps);
|
||||
} else {
|
||||
return _createDependency(metadata, optional, null, null, depProps);
|
||||
}
|
||||
}
|
||||
|
||||
var lowerBoundVisibility = null;
|
||||
var upperBoundVisibility = null;
|
||||
|
||||
for (var i = 0; i < metadata.length; ++i) {
|
||||
var paramMetadata = metadata[i];
|
||||
|
||||
if (paramMetadata instanceof Type) {
|
||||
token = paramMetadata;
|
||||
|
||||
} else if (paramMetadata instanceof InjectMetadata) {
|
||||
token = paramMetadata.token;
|
||||
|
||||
} else if (paramMetadata instanceof OptionalMetadata) {
|
||||
optional = true;
|
||||
|
||||
} else if (paramMetadata instanceof SelfMetadata) {
|
||||
upperBoundVisibility = paramMetadata;
|
||||
|
||||
} else if (paramMetadata instanceof HostMetadata) {
|
||||
upperBoundVisibility = paramMetadata;
|
||||
|
||||
} else if (paramMetadata instanceof SkipSelfMetadata) {
|
||||
lowerBoundVisibility = paramMetadata;
|
||||
|
||||
} else if (paramMetadata instanceof DependencyMetadata) {
|
||||
if (isPresent(paramMetadata.token)) {
|
||||
token = paramMetadata.token;
|
||||
}
|
||||
depProps.push(paramMetadata);
|
||||
}
|
||||
}
|
||||
|
||||
token = resolveForwardRef(token);
|
||||
|
||||
if (isPresent(token)) {
|
||||
return _createDependency(token, optional, lowerBoundVisibility, upperBoundVisibility, depProps);
|
||||
} else {
|
||||
throw new NoAnnotationError(typeOrFunc, params);
|
||||
}
|
||||
}
|
||||
|
||||
function _createDependency(token, optional, lowerBoundVisibility, upperBoundVisibility,
|
||||
depProps): ReflectiveDependency {
|
||||
return new ReflectiveDependency(ReflectiveKey.get(token), optional, lowerBoundVisibility,
|
||||
upperBoundVisibility, depProps);
|
||||
}
|
10
modules/@angular/core/src/linker.ts
Normal file
10
modules/@angular/core/src/linker.ts
Normal file
@ -0,0 +1,10 @@
|
||||
// Public API for compiler
|
||||
export {ComponentResolver} from './linker/component_resolver';
|
||||
export {QueryList} from './linker/query_list';
|
||||
export {DynamicComponentLoader} from './linker/dynamic_component_loader';
|
||||
export {ElementRef} from './linker/element_ref';
|
||||
export {TemplateRef} from './linker/template_ref';
|
||||
export {EmbeddedViewRef, ViewRef} from './linker/view_ref';
|
||||
export {ViewContainerRef} from './linker/view_container_ref';
|
||||
export {ComponentRef, ComponentFactory} from './linker/component_factory';
|
||||
export {ExpressionChangedAfterItHasBeenCheckedException} from './linker/exceptions';
|
95
modules/@angular/core/src/linker/component_factory.ts
Normal file
95
modules/@angular/core/src/linker/component_factory.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import {Injector} from 'angular2/src/core/di';
|
||||
import {Type, isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {ElementRef} from './element_ref';
|
||||
import {ViewRef, ViewRef_} from './view_ref';
|
||||
import {AppElement} from './element';
|
||||
import {ViewUtils} from './view_utils';
|
||||
import {ChangeDetectorRef} from '../change_detection/change_detection';
|
||||
|
||||
/**
|
||||
* Represents an instance of a Component created via a {@link ComponentFactory}.
|
||||
*
|
||||
* `ComponentRef` provides access to the Component Instance as well other objects related to this
|
||||
* Component Instance and allows you to destroy the Component Instance via the {@link #destroy}
|
||||
* method.
|
||||
*/
|
||||
export abstract class ComponentRef<C> {
|
||||
/**
|
||||
* Location of the Host Element of this Component Instance.
|
||||
*/
|
||||
get location(): ElementRef { return unimplemented(); }
|
||||
|
||||
/**
|
||||
* The injector on which the component instance exists.
|
||||
*/
|
||||
get injector(): Injector { return unimplemented(); }
|
||||
|
||||
/**
|
||||
* The instance of the Component.
|
||||
*/
|
||||
get instance(): C { return unimplemented(); };
|
||||
|
||||
/**
|
||||
* The {@link ViewRef} of the Host View of this Component instance.
|
||||
*/
|
||||
get hostView(): ViewRef { return unimplemented(); };
|
||||
|
||||
/**
|
||||
* The {@link ChangeDetectorRef} of the Component instance.
|
||||
*/
|
||||
get changeDetectorRef(): ChangeDetectorRef { return unimplemented(); }
|
||||
|
||||
/**
|
||||
* The component type.
|
||||
*/
|
||||
get componentType(): Type { return unimplemented(); }
|
||||
|
||||
/**
|
||||
* Destroys the component instance and all of the data structures associated with it.
|
||||
*/
|
||||
abstract destroy(): void;
|
||||
|
||||
/**
|
||||
* Allows to register a callback that will be called when the component is destroyed.
|
||||
*/
|
||||
abstract onDestroy(callback: Function): void;
|
||||
}
|
||||
|
||||
export class ComponentRef_<C> extends ComponentRef<C> {
|
||||
constructor(private _hostElement: AppElement, private _componentType: Type) { super(); }
|
||||
get location(): ElementRef { return this._hostElement.elementRef; }
|
||||
get injector(): Injector { return this._hostElement.injector; }
|
||||
get instance(): C { return this._hostElement.component; };
|
||||
get hostView(): ViewRef { return this._hostElement.parentView.ref; };
|
||||
get changeDetectorRef(): ChangeDetectorRef { return this._hostElement.parentView.ref; };
|
||||
get componentType(): Type { return this._componentType; }
|
||||
|
||||
destroy(): void { this._hostElement.parentView.destroy(); }
|
||||
onDestroy(callback: Function): void { this.hostView.onDestroy(callback); }
|
||||
}
|
||||
|
||||
const EMPTY_CONTEXT = /*@ts2dart_const*/ new Object();
|
||||
|
||||
/*@ts2dart_const*/
|
||||
export class ComponentFactory<C> {
|
||||
constructor(public selector: string, private _viewFactory: Function,
|
||||
private _componentType: Type) {}
|
||||
|
||||
get componentType(): Type { return this._componentType; }
|
||||
|
||||
/**
|
||||
* Creates a new component.
|
||||
*/
|
||||
create(injector: Injector, projectableNodes: any[][] = null,
|
||||
rootSelectorOrNode: string | any = null): ComponentRef<C> {
|
||||
var vu: ViewUtils = injector.get(ViewUtils);
|
||||
if (isBlank(projectableNodes)) {
|
||||
projectableNodes = [];
|
||||
}
|
||||
// Note: Host views don't need a declarationAppElement!
|
||||
var hostView = this._viewFactory(vu, injector, null);
|
||||
var hostElement = hostView.create(EMPTY_CONTEXT, projectableNodes, rootSelectorOrNode);
|
||||
return new ComponentRef_<C>(hostElement, this._componentType);
|
||||
}
|
||||
}
|
33
modules/@angular/core/src/linker/component_resolver.ts
Normal file
33
modules/@angular/core/src/linker/component_resolver.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import {Type, isBlank, stringify} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||
import {ComponentFactory} from './component_factory';
|
||||
|
||||
/**
|
||||
* Low-level service for loading {@link ComponentFactory}s, which
|
||||
* can later be used to create and render a Component instance.
|
||||
*/
|
||||
export abstract class ComponentResolver {
|
||||
abstract resolveComponent(componentType: Type): Promise<ComponentFactory<any>>;
|
||||
abstract clearCache();
|
||||
}
|
||||
|
||||
function _isComponentFactory(type: any): boolean {
|
||||
return type instanceof ComponentFactory;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReflectorComponentResolver extends ComponentResolver {
|
||||
resolveComponent(componentType: Type): Promise<ComponentFactory<any>> {
|
||||
var metadatas = reflector.annotations(componentType);
|
||||
var componentFactory = metadatas.find(_isComponentFactory);
|
||||
|
||||
if (isBlank(componentFactory)) {
|
||||
throw new BaseException(`No precompiled component ${stringify(componentType)} found`);
|
||||
}
|
||||
return PromiseWrapper.resolve(componentFactory);
|
||||
}
|
||||
clearCache() {}
|
||||
}
|
72
modules/@angular/core/src/linker/debug_context.ts
Normal file
72
modules/@angular/core/src/linker/debug_context.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {Injector} from 'angular2/src/core/di';
|
||||
import {RenderDebugInfo} from 'angular2/src/core/render/api';
|
||||
import {DebugAppView} from './view';
|
||||
import {ViewType} from './view_type';
|
||||
|
||||
/* @ts2dart_const */
|
||||
export class StaticNodeDebugInfo {
|
||||
constructor(public providerTokens: any[], public componentToken: any,
|
||||
public refTokens: {[key: string]: any}) {}
|
||||
}
|
||||
|
||||
export class DebugContext implements RenderDebugInfo {
|
||||
constructor(private _view: DebugAppView<any>, private _nodeIndex: number, private _tplRow: number,
|
||||
private _tplCol: number) {}
|
||||
|
||||
private get _staticNodeInfo(): StaticNodeDebugInfo {
|
||||
return isPresent(this._nodeIndex) ? this._view.staticNodeDebugInfos[this._nodeIndex] : null;
|
||||
}
|
||||
|
||||
get context() { return this._view.context; }
|
||||
get component() {
|
||||
var staticNodeInfo = this._staticNodeInfo;
|
||||
if (isPresent(staticNodeInfo) && isPresent(staticNodeInfo.componentToken)) {
|
||||
return this.injector.get(staticNodeInfo.componentToken);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
get componentRenderElement() {
|
||||
var componentView = this._view;
|
||||
while (isPresent(componentView.declarationAppElement) &&
|
||||
componentView.type !== ViewType.COMPONENT) {
|
||||
componentView = <DebugAppView<any>>componentView.declarationAppElement.parentView;
|
||||
}
|
||||
return isPresent(componentView.declarationAppElement) ?
|
||||
componentView.declarationAppElement.nativeElement :
|
||||
null;
|
||||
}
|
||||
get injector(): Injector { return this._view.injector(this._nodeIndex); }
|
||||
get renderNode(): any {
|
||||
if (isPresent(this._nodeIndex) && isPresent(this._view.allNodes)) {
|
||||
return this._view.allNodes[this._nodeIndex];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
get providerTokens(): any[] {
|
||||
var staticNodeInfo = this._staticNodeInfo;
|
||||
return isPresent(staticNodeInfo) ? staticNodeInfo.providerTokens : null;
|
||||
}
|
||||
get source(): string {
|
||||
return `${this._view.componentType.templateUrl}:${this._tplRow}:${this._tplCol}`;
|
||||
}
|
||||
get references(): {[key: string]: any} {
|
||||
var varValues: {[key: string]: string} = {};
|
||||
var staticNodeInfo = this._staticNodeInfo;
|
||||
if (isPresent(staticNodeInfo)) {
|
||||
var refs = staticNodeInfo.refTokens;
|
||||
StringMapWrapper.forEach(refs, (refToken, refName) => {
|
||||
var varValue;
|
||||
if (isBlank(refToken)) {
|
||||
varValue = isPresent(this._view.allNodes) ? this._view.allNodes[this._nodeIndex] : null;
|
||||
} else {
|
||||
varValue = this._view.injectorGet(refToken, this._nodeIndex, null);
|
||||
}
|
||||
varValues[refName] = varValue;
|
||||
});
|
||||
}
|
||||
return varValues;
|
||||
}
|
||||
}
|
146
modules/@angular/core/src/linker/dynamic_component_loader.ts
Normal file
146
modules/@angular/core/src/linker/dynamic_component_loader.ts
Normal file
@ -0,0 +1,146 @@
|
||||
import {
|
||||
Injector,
|
||||
ResolvedReflectiveProvider,
|
||||
Injectable,
|
||||
ReflectiveInjector
|
||||
} from 'angular2/src/core/di';
|
||||
import {ComponentResolver} from './component_resolver';
|
||||
import {isType, Type, stringify, isPresent} from 'angular2/src/facade/lang';
|
||||
import {ComponentRef} from './component_factory';
|
||||
import {ViewContainerRef} from './view_container_ref';
|
||||
|
||||
/**
|
||||
* Use ComponentResolver and ViewContainerRef directly.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
export abstract class DynamicComponentLoader {
|
||||
/**
|
||||
* Creates an instance of a Component `type` and attaches it to the first element in the
|
||||
* platform-specific global view that matches the component's selector.
|
||||
*
|
||||
* In a browser the platform-specific global view is the main DOM Document.
|
||||
*
|
||||
* If needed, the component's selector can be overridden via `overrideSelector`.
|
||||
*
|
||||
* You can optionally provide `injector` and this {@link Injector} will be used to instantiate the
|
||||
* Component.
|
||||
*
|
||||
* To be notified when this Component instance is destroyed, you can also optionally provide
|
||||
* `onDispose` callback.
|
||||
*
|
||||
* Returns a promise for the {@link ComponentRef} representing the newly created Component.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* @Component({
|
||||
* selector: 'child-component',
|
||||
* template: 'Child'
|
||||
* })
|
||||
* class ChildComponent {
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'my-app',
|
||||
* template: 'Parent (<child id="child"></child>)'
|
||||
* })
|
||||
* class MyApp {
|
||||
* constructor(dcl: DynamicComponentLoader, injector: Injector) {
|
||||
* dcl.loadAsRoot(ChildComponent, '#child', injector);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* bootstrap(MyApp);
|
||||
* ```
|
||||
*
|
||||
* Resulting DOM:
|
||||
*
|
||||
* ```
|
||||
* <my-app>
|
||||
* Parent (
|
||||
* <child id="child">Child</child>
|
||||
* )
|
||||
* </my-app>
|
||||
* ```
|
||||
*/
|
||||
abstract loadAsRoot(type: Type, overrideSelectorOrNode: string | any, injector: Injector,
|
||||
onDispose?: () => void,
|
||||
projectableNodes?: any[][]): Promise<ComponentRef<any>>;
|
||||
|
||||
|
||||
/**
|
||||
* Creates an instance of a Component and attaches it to the View Container found at the
|
||||
* `location` specified as {@link ViewContainerRef}.
|
||||
*
|
||||
* You can optionally provide `providers` to configure the {@link Injector} provisioned for this
|
||||
* Component Instance.
|
||||
*
|
||||
* Returns a promise for the {@link ComponentRef} representing the newly created Component.
|
||||
*
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* @Component({
|
||||
* selector: 'child-component',
|
||||
* template: 'Child'
|
||||
* })
|
||||
* class ChildComponent {
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'my-app',
|
||||
* template: 'Parent'
|
||||
* })
|
||||
* class MyApp {
|
||||
* constructor(dcl: DynamicComponentLoader, viewContainerRef: ViewContainerRef) {
|
||||
* dcl.loadNextToLocation(ChildComponent, viewContainerRef);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* bootstrap(MyApp);
|
||||
* ```
|
||||
*
|
||||
* Resulting DOM:
|
||||
*
|
||||
* ```
|
||||
* <my-app>Parent</my-app>
|
||||
* <child-component>Child</child-component>
|
||||
* ```
|
||||
*/
|
||||
abstract loadNextToLocation(type: Type, location: ViewContainerRef,
|
||||
providers?: ResolvedReflectiveProvider[],
|
||||
projectableNodes?: any[][]): Promise<ComponentRef<any>>;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DynamicComponentLoader_ extends DynamicComponentLoader {
|
||||
constructor(private _compiler: ComponentResolver) { super(); }
|
||||
|
||||
loadAsRoot(type: Type, overrideSelectorOrNode: string | any, injector: Injector,
|
||||
onDispose?: () => void, projectableNodes?: any[][]): Promise<ComponentRef<any>> {
|
||||
return this._compiler.resolveComponent(type).then(componentFactory => {
|
||||
var componentRef = componentFactory.create(
|
||||
injector, projectableNodes,
|
||||
isPresent(overrideSelectorOrNode) ? overrideSelectorOrNode : componentFactory.selector);
|
||||
if (isPresent(onDispose)) {
|
||||
componentRef.onDestroy(onDispose);
|
||||
}
|
||||
return componentRef;
|
||||
});
|
||||
}
|
||||
|
||||
loadNextToLocation(type: Type, location: ViewContainerRef,
|
||||
providers: ResolvedReflectiveProvider[] = null,
|
||||
projectableNodes: any[][] = null): Promise<ComponentRef<any>> {
|
||||
return this._compiler.resolveComponent(type).then(componentFactory => {
|
||||
var contextInjector = location.parentInjector;
|
||||
var childInjector = isPresent(providers) && providers.length > 0 ?
|
||||
ReflectiveInjector.fromResolvedProviders(providers, contextInjector) :
|
||||
contextInjector;
|
||||
return location.createComponent(componentFactory, location.length, childInjector,
|
||||
projectableNodes);
|
||||
});
|
||||
}
|
||||
}
|
91
modules/@angular/core/src/linker/element.ts
Normal file
91
modules/@angular/core/src/linker/element.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import {isPresent, isBlank, Type} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
|
||||
import {Injector} from 'angular2/src/core/di';
|
||||
|
||||
import {AppView} from './view';
|
||||
import {ViewType} from './view_type';
|
||||
import {ElementRef} from './element_ref';
|
||||
|
||||
import {ViewContainerRef, ViewContainerRef_} from './view_container_ref';
|
||||
|
||||
import {QueryList} from './query_list';
|
||||
|
||||
/**
|
||||
* An AppElement is created for elements that have a ViewContainerRef,
|
||||
* a nested component or a <template> element to keep data around
|
||||
* that is needed for later instantiations.
|
||||
*/
|
||||
export class AppElement {
|
||||
public nestedViews: AppView<any>[] = null;
|
||||
public componentView: AppView<any> = null;
|
||||
|
||||
public component: any;
|
||||
public componentConstructorViewQueries: QueryList<any>[];
|
||||
|
||||
constructor(public index: number, public parentIndex: number, public parentView: AppView<any>,
|
||||
public nativeElement: any) {}
|
||||
|
||||
get elementRef(): ElementRef { return new ElementRef(this.nativeElement); }
|
||||
|
||||
get vcRef(): ViewContainerRef_ { return new ViewContainerRef_(this); }
|
||||
|
||||
initComponent(component: any, componentConstructorViewQueries: QueryList<any>[],
|
||||
view: AppView<any>) {
|
||||
this.component = component;
|
||||
this.componentConstructorViewQueries = componentConstructorViewQueries;
|
||||
this.componentView = view;
|
||||
}
|
||||
|
||||
get parentInjector(): Injector { return this.parentView.injector(this.parentIndex); }
|
||||
get injector(): Injector { return this.parentView.injector(this.index); }
|
||||
|
||||
mapNestedViews(nestedViewClass: any, callback: Function): any[] {
|
||||
var result = [];
|
||||
if (isPresent(this.nestedViews)) {
|
||||
this.nestedViews.forEach((nestedView) => {
|
||||
if (nestedView.clazz === nestedViewClass) {
|
||||
result.push(callback(nestedView));
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
attachView(view: AppView<any>, viewIndex: number) {
|
||||
if (view.type === ViewType.COMPONENT) {
|
||||
throw new BaseException(`Component views can't be moved!`);
|
||||
}
|
||||
var nestedViews = this.nestedViews;
|
||||
if (nestedViews == null) {
|
||||
nestedViews = [];
|
||||
this.nestedViews = nestedViews;
|
||||
}
|
||||
ListWrapper.insert(nestedViews, viewIndex, view);
|
||||
var refRenderNode;
|
||||
if (viewIndex > 0) {
|
||||
var prevView = nestedViews[viewIndex - 1];
|
||||
refRenderNode = prevView.lastRootNode;
|
||||
} else {
|
||||
refRenderNode = this.nativeElement;
|
||||
}
|
||||
if (isPresent(refRenderNode)) {
|
||||
view.renderer.attachViewAfter(refRenderNode, view.flatRootNodes);
|
||||
}
|
||||
view.addToContentChildren(this);
|
||||
}
|
||||
|
||||
detachView(viewIndex: number): AppView<any> {
|
||||
var view = ListWrapper.removeAt(this.nestedViews, viewIndex);
|
||||
if (view.type === ViewType.COMPONENT) {
|
||||
throw new BaseException(`Component views can't be moved!`);
|
||||
}
|
||||
|
||||
view.renderer.detachView(view.flatRootNodes);
|
||||
|
||||
view.removeFromContentChildren(this);
|
||||
return view;
|
||||
}
|
||||
}
|
19
modules/@angular/core/src/linker/element_injector.ts
Normal file
19
modules/@angular/core/src/linker/element_injector.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import {Injector, THROW_IF_NOT_FOUND} from 'angular2/src/core/di/injector';
|
||||
import {AppView} from './view';
|
||||
|
||||
const _UNDEFINED = /*@ts2dart_const*/ new Object();
|
||||
|
||||
export class ElementInjector extends Injector {
|
||||
constructor(private _view: AppView<any>, private _nodeIndex: number) { super(); }
|
||||
|
||||
get(token: any, notFoundValue: any = THROW_IF_NOT_FOUND): any {
|
||||
var result = _UNDEFINED;
|
||||
if (result === _UNDEFINED) {
|
||||
result = this._view.injectorGet(token, this._nodeIndex, _UNDEFINED);
|
||||
}
|
||||
if (result === _UNDEFINED) {
|
||||
result = this._view.parentInjector.get(token, notFoundValue);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
33
modules/@angular/core/src/linker/element_ref.ts
Normal file
33
modules/@angular/core/src/linker/element_ref.ts
Normal file
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* A wrapper around a native element inside of a View.
|
||||
*
|
||||
* An `ElementRef` is backed by a render-specific element. In the browser, this is usually a DOM
|
||||
* element.
|
||||
*/
|
||||
// Note: We don't expose things like `Injector`, `ViewContainer`, ... here,
|
||||
// i.e. users have to ask for what they need. With that, we can build better analysis tools
|
||||
// and could do better codegen in the future.
|
||||
export class ElementRef {
|
||||
/**
|
||||
* The underlying native element or `null` if direct access to native elements is not supported
|
||||
* (e.g. when the application runs in a web worker).
|
||||
*
|
||||
* <div class="callout is-critical">
|
||||
* <header>Use with caution</header>
|
||||
* <p>
|
||||
* Use this API as the last resort when direct access to DOM is needed. Use templating and
|
||||
* data-binding provided by Angular instead. Alternatively you take a look at {@link Renderer}
|
||||
* which provides API that can safely be used even when direct access to native elements is not
|
||||
* supported.
|
||||
* </p>
|
||||
* <p>
|
||||
* Relying on direct DOM access creates tight coupling between your application and rendering
|
||||
* layers which will make it impossible to separate the two and deploy your application into a
|
||||
* web worker.
|
||||
* </p>
|
||||
* </div>
|
||||
*/
|
||||
public nativeElement: any;
|
||||
|
||||
constructor(nativeElement: any) { this.nativeElement = nativeElement; }
|
||||
}
|
64
modules/@angular/core/src/linker/exceptions.ts
Normal file
64
modules/@angular/core/src/linker/exceptions.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import {BaseException, WrappedException} from "angular2/src/facade/exceptions";
|
||||
|
||||
/**
|
||||
* An error thrown if application changes model breaking the top-down data flow.
|
||||
*
|
||||
* This exception is only thrown in dev mode.
|
||||
*
|
||||
* <!-- TODO: Add a link once the dev mode option is configurable -->
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: 'parent',
|
||||
* template: `
|
||||
* <child [prop]="parentProp"></child>
|
||||
* `,
|
||||
* directives: [forwardRef(() => Child)]
|
||||
* })
|
||||
* class Parent {
|
||||
* parentProp = "init";
|
||||
* }
|
||||
*
|
||||
* @Directive({selector: 'child', inputs: ['prop']})
|
||||
* class Child {
|
||||
* constructor(public parent: Parent) {}
|
||||
*
|
||||
* set prop(v) {
|
||||
* // this updates the parent property, which is disallowed during change detection
|
||||
* // this will result in ExpressionChangedAfterItHasBeenCheckedException
|
||||
* this.parent.parentProp = "updated";
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export class ExpressionChangedAfterItHasBeenCheckedException extends BaseException {
|
||||
constructor(oldValue: any, currValue: any, context: any) {
|
||||
super(`Expression has changed after it was checked. ` +
|
||||
`Previous value: '${oldValue}'. Current value: '${currValue}'`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when an exception was raised during view creation, change detection or destruction.
|
||||
*
|
||||
* This error wraps the original exception to attach additional contextual information that can
|
||||
* be useful for debugging.
|
||||
*/
|
||||
export class ViewWrappedException extends WrappedException {
|
||||
constructor(originalException: any, originalStack: any, context: any) {
|
||||
super(`Error in ${context.source}`, originalException, originalStack, context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a destroyed view is used.
|
||||
*
|
||||
* This error indicates a bug in the framework.
|
||||
*
|
||||
* This is an internal Angular error.
|
||||
*/
|
||||
export class ViewDestroyedException extends BaseException {
|
||||
constructor(details: string) { super(`Attempt to use a destroyed view: ${details}`); }
|
||||
}
|
44
modules/@angular/core/src/linker/query_list.dart
Normal file
44
modules/@angular/core/src/linker/query_list.dart
Normal file
@ -0,0 +1,44 @@
|
||||
library angular2.src.core.compiler.query_list;
|
||||
|
||||
import 'dart:collection';
|
||||
import 'package:angular2/src/facade/collection.dart';
|
||||
import 'package:angular2/src/facade/async.dart';
|
||||
|
||||
/**
|
||||
* See query_list.ts
|
||||
*/
|
||||
class QueryList<T> extends Object with IterableMixin<T> {
|
||||
bool _dirty = true;
|
||||
List<T> _results = [];
|
||||
EventEmitter _emitter = new EventEmitter();
|
||||
|
||||
Iterator<T> get iterator => _results.iterator;
|
||||
|
||||
Stream<Iterable<T>> get changes => _emitter;
|
||||
|
||||
int get length => _results.length;
|
||||
T get first => _results.length > 0 ? _results.first : null;
|
||||
T get last => _results.length > 0 ? _results.last : null;
|
||||
String toString() {
|
||||
return _results.toString();
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
void reset(List<T> newList) {
|
||||
_results = ListWrapper.flatten(newList);
|
||||
_dirty = false;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
void notifyOnChanges() {
|
||||
_emitter.add(this);
|
||||
}
|
||||
|
||||
/** @internal **/
|
||||
bool get dirty => _dirty;
|
||||
|
||||
/** @internal **/
|
||||
void setDirty() {
|
||||
_dirty = true;
|
||||
}
|
||||
}
|
85
modules/@angular/core/src/linker/query_list.ts
Normal file
85
modules/@angular/core/src/linker/query_list.ts
Normal file
@ -0,0 +1,85 @@
|
||||
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {getSymbolIterator} from 'angular2/src/facade/lang';
|
||||
import {Observable, EventEmitter} from 'angular2/src/facade/async';
|
||||
|
||||
|
||||
/**
|
||||
* An unmodifiable list of items that Angular keeps up to date when the state
|
||||
* of the application changes.
|
||||
*
|
||||
* The type of object that {@link QueryMetadata} and {@link ViewQueryMetadata} provide.
|
||||
*
|
||||
* Implements an iterable interface, therefore it can be used in both ES6
|
||||
* javascript `for (var i of items)` loops as well as in Angular templates with
|
||||
* `*ngFor="let i of myList"`.
|
||||
*
|
||||
* Changes can be observed by subscribing to the changes `Observable`.
|
||||
*
|
||||
* NOTE: In the future this class will implement an `Observable` interface.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/RX8sJnQYl9FWuSCWme5z?p=preview))
|
||||
* ```typescript
|
||||
* @Component({...})
|
||||
* class Container {
|
||||
* constructor(@Query(Item) items: QueryList<Item>) {
|
||||
* items.changes.subscribe(_ => console.log(items.length));
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export class QueryList<T> {
|
||||
private _dirty = true;
|
||||
private _results: Array<T> = [];
|
||||
private _emitter = new EventEmitter();
|
||||
|
||||
get changes(): Observable<any> { return this._emitter; }
|
||||
get length(): number { return this._results.length; }
|
||||
get first(): T { return ListWrapper.first(this._results); }
|
||||
get last(): T { return ListWrapper.last(this._results); }
|
||||
|
||||
/**
|
||||
* returns a new array with the passed in function applied to each element.
|
||||
*/
|
||||
map<U>(fn: (item: T) => U): U[] { return this._results.map(fn); }
|
||||
|
||||
/**
|
||||
* returns a filtered array.
|
||||
*/
|
||||
filter(fn: (item: T) => boolean): T[] { return this._results.filter(fn); }
|
||||
|
||||
/**
|
||||
* returns a reduced value.
|
||||
*/
|
||||
reduce<U>(fn: (acc: U, item: T) => U, init: U): U { return this._results.reduce(fn, init); }
|
||||
|
||||
/**
|
||||
* executes function for each element in a query.
|
||||
*/
|
||||
forEach(fn: (item: T) => void): void { this._results.forEach(fn); }
|
||||
|
||||
/**
|
||||
* converts QueryList into an array
|
||||
*/
|
||||
toArray(): T[] { return ListWrapper.clone(this._results); }
|
||||
|
||||
[getSymbolIterator()](): any { return this._results[getSymbolIterator()](); }
|
||||
|
||||
toString(): string { return this._results.toString(); }
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
reset(res: Array<T | any[]>): void {
|
||||
this._results = ListWrapper.flatten(res);
|
||||
this._dirty = false;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
notifyOnChanges(): void { this._emitter.emit(this); }
|
||||
|
||||
/** internal */
|
||||
setDirty() { this._dirty = true; }
|
||||
|
||||
/** internal */
|
||||
get dirty() { return this._dirty; }
|
||||
}
|
53
modules/@angular/core/src/linker/template_ref.ts
Normal file
53
modules/@angular/core/src/linker/template_ref.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import {isBlank} from 'angular2/src/facade/lang';
|
||||
import {ElementRef} from './element_ref';
|
||||
import {AppElement} from './element';
|
||||
import {AppView} from './view';
|
||||
import {EmbeddedViewRef} from './view_ref';
|
||||
|
||||
const EMPTY_CONTEXT = /*@ts2dart_const*/ new Object();
|
||||
|
||||
/**
|
||||
* Represents an Embedded Template that can be used to instantiate Embedded Views.
|
||||
*
|
||||
* You can access a `TemplateRef`, in two ways. Via a directive placed on a `<template>` element (or
|
||||
* directive prefixed with `*`) and have the `TemplateRef` for this Embedded View injected into the
|
||||
* constructor of the directive using the `TemplateRef` Token. Alternatively you can query for the
|
||||
* `TemplateRef` from a Component or a Directive via {@link Query}.
|
||||
*
|
||||
* To instantiate Embedded Views based on a Template, use
|
||||
* {@link ViewContainerRef#createEmbeddedView}, which will create the View and attach it to the
|
||||
* View Container.
|
||||
*/
|
||||
export abstract class TemplateRef<C> {
|
||||
/**
|
||||
* The location in the View where the Embedded View logically belongs to.
|
||||
*
|
||||
* The data-binding and injection contexts of Embedded Views created from this `TemplateRef`
|
||||
* inherit from the contexts of this location.
|
||||
*
|
||||
* Typically new Embedded Views are attached to the View Container of this location, but in
|
||||
* advanced use-cases, the View can be attached to a different container while keeping the
|
||||
* data-binding and injection context from the original location.
|
||||
*
|
||||
*/
|
||||
// TODO(i): rename to anchor or location
|
||||
get elementRef(): ElementRef { return null; }
|
||||
|
||||
abstract createEmbeddedView(context: C): EmbeddedViewRef<C>;
|
||||
}
|
||||
|
||||
export class TemplateRef_<C> extends TemplateRef<C> {
|
||||
constructor(private _appElement: AppElement, private _viewFactory: Function) { super(); }
|
||||
|
||||
createEmbeddedView(context: C): EmbeddedViewRef<C> {
|
||||
var view: AppView<C> = this._viewFactory(this._appElement.parentView.viewUtils,
|
||||
this._appElement.parentInjector, this._appElement);
|
||||
if (isBlank(context)) {
|
||||
context = <any>EMPTY_CONTEXT;
|
||||
}
|
||||
view.create(context, null, null);
|
||||
return view.ref;
|
||||
}
|
||||
|
||||
get elementRef(): ElementRef { return this._appElement.elementRef; }
|
||||
}
|
409
modules/@angular/core/src/linker/view.ts
Normal file
409
modules/@angular/core/src/linker/view.ts
Normal file
@ -0,0 +1,409 @@
|
||||
import {
|
||||
ListWrapper,
|
||||
MapWrapper,
|
||||
Map,
|
||||
StringMapWrapper,
|
||||
isListLikeIterable,
|
||||
areIterablesEqual
|
||||
} from 'angular2/src/facade/collection';
|
||||
|
||||
import {Injector} from 'angular2/src/core/di';
|
||||
import {AppElement} from './element';
|
||||
import {
|
||||
assertionsEnabled,
|
||||
isPresent,
|
||||
isBlank,
|
||||
Type,
|
||||
isArray,
|
||||
isNumber,
|
||||
stringify,
|
||||
isPrimitive,
|
||||
isString
|
||||
} from 'angular2/src/facade/lang';
|
||||
|
||||
import {ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {
|
||||
Renderer,
|
||||
RootRenderer,
|
||||
RenderComponentType,
|
||||
RenderDebugInfo
|
||||
} from 'angular2/src/core/render/api';
|
||||
import {ViewRef_} from './view_ref';
|
||||
|
||||
import {ViewType} from './view_type';
|
||||
import {
|
||||
ViewUtils,
|
||||
flattenNestedViewRenderNodes,
|
||||
ensureSlotCount,
|
||||
arrayLooseIdentical,
|
||||
mapLooseIdentical
|
||||
} from './view_utils';
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorState,
|
||||
isDefaultChangeDetectionStrategy,
|
||||
devModeEqual
|
||||
} from 'angular2/src/core/change_detection/change_detection';
|
||||
import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile';
|
||||
import {
|
||||
ExpressionChangedAfterItHasBeenCheckedException,
|
||||
ViewDestroyedException,
|
||||
ViewWrappedException
|
||||
} from './exceptions';
|
||||
import {StaticNodeDebugInfo, DebugContext} from './debug_context';
|
||||
import {ElementInjector} from './element_injector';
|
||||
|
||||
var _scope_check: WtfScopeFn = wtfCreateScope(`AppView#check(ascii id)`);
|
||||
|
||||
/**
|
||||
* Cost of making objects: http://jsperf.com/instantiate-size-of-object
|
||||
*
|
||||
*/
|
||||
export abstract class AppView<T> {
|
||||
ref: ViewRef_<T>;
|
||||
rootNodesOrAppElements: any[];
|
||||
allNodes: any[];
|
||||
disposables: Function[];
|
||||
subscriptions: any[];
|
||||
contentChildren: AppView<any>[] = [];
|
||||
viewChildren: AppView<any>[] = [];
|
||||
viewContainerElement: AppElement = null;
|
||||
|
||||
// The names of the below fields must be kept in sync with codegen_name_util.ts or
|
||||
// change detection will fail.
|
||||
cdState: ChangeDetectorState = ChangeDetectorState.NeverChecked;
|
||||
|
||||
projectableNodes: Array<any | any[]>;
|
||||
|
||||
destroyed: boolean = false;
|
||||
|
||||
renderer: Renderer;
|
||||
|
||||
private _hasExternalHostElement: boolean;
|
||||
|
||||
public context: T;
|
||||
|
||||
constructor(public clazz: any, public componentType: RenderComponentType, public type: ViewType,
|
||||
public viewUtils: ViewUtils, public parentInjector: Injector,
|
||||
public declarationAppElement: AppElement, public cdMode: ChangeDetectionStrategy) {
|
||||
this.ref = new ViewRef_(this);
|
||||
if (type === ViewType.COMPONENT || type === ViewType.HOST) {
|
||||
this.renderer = viewUtils.renderComponent(componentType);
|
||||
} else {
|
||||
this.renderer = declarationAppElement.parentView.renderer;
|
||||
}
|
||||
}
|
||||
|
||||
create(context: T, givenProjectableNodes: Array<any | any[]>,
|
||||
rootSelectorOrNode: string | any): AppElement {
|
||||
this.context = context;
|
||||
var projectableNodes;
|
||||
switch (this.type) {
|
||||
case ViewType.COMPONENT:
|
||||
projectableNodes = ensureSlotCount(givenProjectableNodes, this.componentType.slotCount);
|
||||
break;
|
||||
case ViewType.EMBEDDED:
|
||||
projectableNodes = this.declarationAppElement.parentView.projectableNodes;
|
||||
break;
|
||||
case ViewType.HOST:
|
||||
// Note: Don't ensure the slot count for the projectableNodes as we store
|
||||
// them only for the contained component view (which will later check the slot count...)
|
||||
projectableNodes = givenProjectableNodes;
|
||||
break;
|
||||
}
|
||||
this._hasExternalHostElement = isPresent(rootSelectorOrNode);
|
||||
this.projectableNodes = projectableNodes;
|
||||
return this.createInternal(rootSelectorOrNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwritten by implementations.
|
||||
* Returns the AppElement for the host element for ViewType.HOST.
|
||||
*/
|
||||
createInternal(rootSelectorOrNode: string | any): AppElement { return null; }
|
||||
|
||||
init(rootNodesOrAppElements: any[], allNodes: any[], disposables: Function[],
|
||||
subscriptions: any[]) {
|
||||
this.rootNodesOrAppElements = rootNodesOrAppElements;
|
||||
this.allNodes = allNodes;
|
||||
this.disposables = disposables;
|
||||
this.subscriptions = subscriptions;
|
||||
if (this.type === ViewType.COMPONENT) {
|
||||
// Note: the render nodes have been attached to their host element
|
||||
// in the ViewFactory already.
|
||||
this.declarationAppElement.parentView.viewChildren.push(this);
|
||||
this.dirtyParentQueriesInternal();
|
||||
}
|
||||
}
|
||||
|
||||
selectOrCreateHostElement(elementName: string, rootSelectorOrNode: string | any,
|
||||
debugInfo: RenderDebugInfo): any {
|
||||
var hostElement;
|
||||
if (isPresent(rootSelectorOrNode)) {
|
||||
hostElement = this.renderer.selectRootElement(rootSelectorOrNode, debugInfo);
|
||||
} else {
|
||||
hostElement = this.renderer.createElement(null, elementName, debugInfo);
|
||||
}
|
||||
return hostElement;
|
||||
}
|
||||
|
||||
injectorGet(token: any, nodeIndex: number, notFoundResult: any): any {
|
||||
return this.injectorGetInternal(token, nodeIndex, notFoundResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwritten by implementations
|
||||
*/
|
||||
injectorGetInternal(token: any, nodeIndex: number, notFoundResult: any): any {
|
||||
return notFoundResult;
|
||||
}
|
||||
|
||||
injector(nodeIndex: number): Injector {
|
||||
if (isPresent(nodeIndex)) {
|
||||
return new ElementInjector(this, nodeIndex);
|
||||
} else {
|
||||
return this.parentInjector;
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this._hasExternalHostElement) {
|
||||
this.renderer.detachView(this.flatRootNodes);
|
||||
} else if (isPresent(this.viewContainerElement)) {
|
||||
this.viewContainerElement.detachView(this.viewContainerElement.nestedViews.indexOf(this));
|
||||
}
|
||||
this._destroyRecurse();
|
||||
}
|
||||
|
||||
private _destroyRecurse() {
|
||||
if (this.destroyed) {
|
||||
return;
|
||||
}
|
||||
var children = this.contentChildren;
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
children[i]._destroyRecurse();
|
||||
}
|
||||
children = this.viewChildren;
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
children[i]._destroyRecurse();
|
||||
}
|
||||
this.destroyLocal();
|
||||
|
||||
this.destroyed = true;
|
||||
}
|
||||
|
||||
destroyLocal() {
|
||||
var hostElement =
|
||||
this.type === ViewType.COMPONENT ? this.declarationAppElement.nativeElement : null;
|
||||
for (var i = 0; i < this.disposables.length; i++) {
|
||||
this.disposables[i]();
|
||||
}
|
||||
for (var i = 0; i < this.subscriptions.length; i++) {
|
||||
ObservableWrapper.dispose(this.subscriptions[i]);
|
||||
}
|
||||
this.destroyInternal();
|
||||
if (this._hasExternalHostElement) {
|
||||
this.renderer.detachView(this.flatRootNodes);
|
||||
} else if (isPresent(this.viewContainerElement)) {
|
||||
this.viewContainerElement.detachView(this.viewContainerElement.nestedViews.indexOf(this));
|
||||
} else {
|
||||
this.dirtyParentQueriesInternal();
|
||||
}
|
||||
this.renderer.destroyView(hostElement, this.allNodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwritten by implementations
|
||||
*/
|
||||
destroyInternal(): void {}
|
||||
|
||||
get changeDetectorRef(): ChangeDetectorRef { return this.ref; }
|
||||
|
||||
get parent(): AppView<any> {
|
||||
return isPresent(this.declarationAppElement) ? this.declarationAppElement.parentView : null;
|
||||
}
|
||||
|
||||
get flatRootNodes(): any[] { return flattenNestedViewRenderNodes(this.rootNodesOrAppElements); }
|
||||
|
||||
get lastRootNode(): any {
|
||||
var lastNode = this.rootNodesOrAppElements.length > 0 ?
|
||||
this.rootNodesOrAppElements[this.rootNodesOrAppElements.length - 1] :
|
||||
null;
|
||||
return _findLastRenderNode(lastNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwritten by implementations
|
||||
*/
|
||||
dirtyParentQueriesInternal(): void {}
|
||||
|
||||
detectChanges(throwOnChange: boolean): void {
|
||||
var s = _scope_check(this.clazz);
|
||||
if (this.cdMode === ChangeDetectionStrategy.Detached ||
|
||||
this.cdMode === ChangeDetectionStrategy.Checked ||
|
||||
this.cdState === ChangeDetectorState.Errored)
|
||||
return;
|
||||
if (this.destroyed) {
|
||||
this.throwDestroyedError('detectChanges');
|
||||
}
|
||||
this.detectChangesInternal(throwOnChange);
|
||||
if (this.cdMode === ChangeDetectionStrategy.CheckOnce)
|
||||
this.cdMode = ChangeDetectionStrategy.Checked;
|
||||
|
||||
this.cdState = ChangeDetectorState.CheckedBefore;
|
||||
wtfLeave(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwritten by implementations
|
||||
*/
|
||||
detectChangesInternal(throwOnChange: boolean): void {
|
||||
this.detectContentChildrenChanges(throwOnChange);
|
||||
this.detectViewChildrenChanges(throwOnChange);
|
||||
}
|
||||
|
||||
detectContentChildrenChanges(throwOnChange: boolean) {
|
||||
for (var i = 0; i < this.contentChildren.length; ++i) {
|
||||
this.contentChildren[i].detectChanges(throwOnChange);
|
||||
}
|
||||
}
|
||||
|
||||
detectViewChildrenChanges(throwOnChange: boolean) {
|
||||
for (var i = 0; i < this.viewChildren.length; ++i) {
|
||||
this.viewChildren[i].detectChanges(throwOnChange);
|
||||
}
|
||||
}
|
||||
|
||||
addToContentChildren(renderAppElement: AppElement): void {
|
||||
renderAppElement.parentView.contentChildren.push(this);
|
||||
this.viewContainerElement = renderAppElement;
|
||||
this.dirtyParentQueriesInternal();
|
||||
}
|
||||
|
||||
removeFromContentChildren(renderAppElement: AppElement): void {
|
||||
ListWrapper.remove(renderAppElement.parentView.contentChildren, this);
|
||||
this.dirtyParentQueriesInternal();
|
||||
this.viewContainerElement = null;
|
||||
}
|
||||
|
||||
markAsCheckOnce(): void { this.cdMode = ChangeDetectionStrategy.CheckOnce; }
|
||||
|
||||
markPathToRootAsCheckOnce(): void {
|
||||
let c: AppView<any> = this;
|
||||
while (isPresent(c) && c.cdMode !== ChangeDetectionStrategy.Detached) {
|
||||
if (c.cdMode === ChangeDetectionStrategy.Checked) {
|
||||
c.cdMode = ChangeDetectionStrategy.CheckOnce;
|
||||
}
|
||||
let parentEl =
|
||||
c.type === ViewType.COMPONENT ? c.declarationAppElement : c.viewContainerElement;
|
||||
c = isPresent(parentEl) ? parentEl.parentView : null;
|
||||
}
|
||||
}
|
||||
|
||||
eventHandler(cb: Function): Function { return cb; }
|
||||
|
||||
throwDestroyedError(details: string): void { throw new ViewDestroyedException(details); }
|
||||
}
|
||||
|
||||
export class DebugAppView<T> extends AppView<T> {
|
||||
private _currentDebugContext: DebugContext = null;
|
||||
|
||||
constructor(clazz: any, componentType: RenderComponentType, type: ViewType, viewUtils: ViewUtils,
|
||||
parentInjector: Injector, declarationAppElement: AppElement,
|
||||
cdMode: ChangeDetectionStrategy, public staticNodeDebugInfos: StaticNodeDebugInfo[]) {
|
||||
super(clazz, componentType, type, viewUtils, parentInjector, declarationAppElement, cdMode);
|
||||
}
|
||||
|
||||
create(context: T, givenProjectableNodes: Array<any | any[]>,
|
||||
rootSelectorOrNode: string | any): AppElement {
|
||||
this._resetDebug();
|
||||
try {
|
||||
return super.create(context, givenProjectableNodes, rootSelectorOrNode);
|
||||
} catch (e) {
|
||||
this._rethrowWithContext(e, e.stack);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
injectorGet(token: any, nodeIndex: number, notFoundResult: any): any {
|
||||
this._resetDebug();
|
||||
try {
|
||||
return super.injectorGet(token, nodeIndex, notFoundResult);
|
||||
} catch (e) {
|
||||
this._rethrowWithContext(e, e.stack);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
destroyLocal() {
|
||||
this._resetDebug();
|
||||
try {
|
||||
super.destroyLocal();
|
||||
} catch (e) {
|
||||
this._rethrowWithContext(e, e.stack);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
detectChanges(throwOnChange: boolean): void {
|
||||
this._resetDebug();
|
||||
try {
|
||||
super.detectChanges(throwOnChange);
|
||||
} catch (e) {
|
||||
this._rethrowWithContext(e, e.stack);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private _resetDebug() { this._currentDebugContext = null; }
|
||||
|
||||
debug(nodeIndex: number, rowNum: number, colNum: number): DebugContext {
|
||||
return this._currentDebugContext = new DebugContext(this, nodeIndex, rowNum, colNum);
|
||||
}
|
||||
|
||||
private _rethrowWithContext(e: any, stack: any) {
|
||||
if (!(e instanceof ViewWrappedException)) {
|
||||
if (!(e instanceof ExpressionChangedAfterItHasBeenCheckedException)) {
|
||||
this.cdState = ChangeDetectorState.Errored;
|
||||
}
|
||||
if (isPresent(this._currentDebugContext)) {
|
||||
throw new ViewWrappedException(e, stack, this._currentDebugContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eventHandler(cb: Function): Function {
|
||||
var superHandler = super.eventHandler(cb);
|
||||
return (event) => {
|
||||
this._resetDebug();
|
||||
try {
|
||||
return superHandler(event);
|
||||
} catch (e) {
|
||||
this._rethrowWithContext(e, e.stack);
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function _findLastRenderNode(node: any): any {
|
||||
var lastNode;
|
||||
if (node instanceof AppElement) {
|
||||
var appEl = <AppElement>node;
|
||||
lastNode = appEl.nativeElement;
|
||||
if (isPresent(appEl.nestedViews)) {
|
||||
// Note: Views might have no root nodes at all!
|
||||
for (var i = appEl.nestedViews.length - 1; i >= 0; i--) {
|
||||
var nestedView = appEl.nestedViews[i];
|
||||
if (nestedView.rootNodesOrAppElements.length > 0) {
|
||||
lastNode = _findLastRenderNode(
|
||||
nestedView.rootNodesOrAppElements[nestedView.rootNodesOrAppElements.length - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
lastNode = node;
|
||||
}
|
||||
return lastNode;
|
||||
}
|
196
modules/@angular/core/src/linker/view_container_ref.ts
Normal file
196
modules/@angular/core/src/linker/view_container_ref.ts
Normal file
@ -0,0 +1,196 @@
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {Injector} from 'angular2/src/core/di/injector';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile';
|
||||
|
||||
import {AppElement} from './element';
|
||||
|
||||
import {ElementRef} from './element_ref';
|
||||
import {TemplateRef, TemplateRef_} from './template_ref';
|
||||
import {EmbeddedViewRef, ViewRef, ViewRef_} from './view_ref';
|
||||
import {ComponentFactory, ComponentRef} from './component_factory';
|
||||
|
||||
/**
|
||||
* Represents a container where one or more Views can be attached.
|
||||
*
|
||||
* The container can contain two kinds of Views. Host Views, created by instantiating a
|
||||
* {@link Component} via {@link #createComponent}, and Embedded Views, created by instantiating an
|
||||
* {@link TemplateRef Embedded Template} via {@link #createEmbeddedView}.
|
||||
*
|
||||
* The location of the View Container within the containing View is specified by the Anchor
|
||||
* `element`. Each View Container can have only one Anchor Element and each Anchor Element can only
|
||||
* have a single View Container.
|
||||
*
|
||||
* Root elements of Views attached to this container become siblings of the Anchor Element in
|
||||
* the Rendered View.
|
||||
*
|
||||
* To access a `ViewContainerRef` of an Element, you can either place a {@link Directive} injected
|
||||
* with `ViewContainerRef` on the Element, or you obtain it via a {@link ViewChild} query.
|
||||
*/
|
||||
export abstract class ViewContainerRef {
|
||||
/**
|
||||
* Anchor element that specifies the location of this container in the containing View.
|
||||
* <!-- TODO: rename to anchorElement -->
|
||||
*/
|
||||
get element(): ElementRef { return <ElementRef>unimplemented(); }
|
||||
|
||||
get injector(): Injector { return <Injector>unimplemented(); }
|
||||
|
||||
get parentInjector(): Injector { return <Injector>unimplemented(); }
|
||||
|
||||
/**
|
||||
* Destroys all Views in this container.
|
||||
*/
|
||||
abstract clear(): void;
|
||||
|
||||
/**
|
||||
* Returns the {@link ViewRef} for the View located in this container at the specified index.
|
||||
*/
|
||||
abstract get(index: number): ViewRef;
|
||||
|
||||
/**
|
||||
* Returns the number of Views currently attached to this container.
|
||||
*/
|
||||
get length(): number { return <number>unimplemented(); };
|
||||
|
||||
/**
|
||||
* Instantiates an Embedded View based on the {@link TemplateRef `templateRef`} and inserts it
|
||||
* into this container at the specified `index`.
|
||||
*
|
||||
* If `index` is not specified, the new View will be inserted as the last View in the container.
|
||||
*
|
||||
* Returns the {@link ViewRef} for the newly created View.
|
||||
*/
|
||||
abstract createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C,
|
||||
index?: number): EmbeddedViewRef<C>;
|
||||
|
||||
/**
|
||||
* Instantiates a single {@link Component} and inserts its Host View into this container at the
|
||||
* specified `index`.
|
||||
*
|
||||
* The component is instantiated using its {@link ComponentFactory} which can be
|
||||
* obtained via {@link ComponentResolver#resolveComponent}.
|
||||
*
|
||||
* If `index` is not specified, the new View will be inserted as the last View in the container.
|
||||
*
|
||||
* You can optionally specify the {@link Injector} that will be used as parent for the Component.
|
||||
*
|
||||
* Returns the {@link ComponentRef} of the Host View created for the newly instantiated Component.
|
||||
*/
|
||||
abstract createComponent<C>(componentFactory: ComponentFactory<C>, index?: number,
|
||||
injector?: Injector, projectableNodes?: any[][]): ComponentRef<C>;
|
||||
|
||||
/**
|
||||
* Inserts a View identified by a {@link ViewRef} into the container at the specified `index`.
|
||||
*
|
||||
* If `index` is not specified, the new View will be inserted as the last View in the container.
|
||||
*
|
||||
* Returns the inserted {@link ViewRef}.
|
||||
*/
|
||||
abstract insert(viewRef: ViewRef, index?: number): ViewRef;
|
||||
|
||||
/**
|
||||
* Returns the index of the View, specified via {@link ViewRef}, within the current container or
|
||||
* `-1` if this container doesn't contain the View.
|
||||
*/
|
||||
abstract indexOf(viewRef: ViewRef): number;
|
||||
|
||||
/**
|
||||
* Destroys a View attached to this container at the specified `index`.
|
||||
*
|
||||
* If `index` is not specified, the last View in the container will be removed.
|
||||
*/
|
||||
abstract remove(index?: number): void;
|
||||
|
||||
/**
|
||||
* Use along with {@link #insert} to move a View within the current container.
|
||||
*
|
||||
* If the `index` param is omitted, the last {@link ViewRef} is detached.
|
||||
*/
|
||||
abstract detach(index?: number): ViewRef;
|
||||
}
|
||||
|
||||
export class ViewContainerRef_ implements ViewContainerRef {
|
||||
constructor(private _element: AppElement) {}
|
||||
|
||||
get(index: number): ViewRef { return this._element.nestedViews[index].ref; }
|
||||
get length(): number {
|
||||
var views = this._element.nestedViews;
|
||||
return isPresent(views) ? views.length : 0;
|
||||
}
|
||||
|
||||
get element(): ElementRef { return this._element.elementRef; }
|
||||
|
||||
get injector(): Injector { return this._element.injector; }
|
||||
|
||||
get parentInjector(): Injector { return this._element.parentInjector; }
|
||||
|
||||
// TODO(rado): profile and decide whether bounds checks should be added
|
||||
// to the methods below.
|
||||
createEmbeddedView<C>(templateRef: TemplateRef<C>, context: C = null,
|
||||
index: number = -1): EmbeddedViewRef<C> {
|
||||
var viewRef: EmbeddedViewRef<any> = templateRef.createEmbeddedView(context);
|
||||
this.insert(viewRef, index);
|
||||
return viewRef;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_createComponentInContainerScope: WtfScopeFn =
|
||||
wtfCreateScope('ViewContainerRef#createComponent()');
|
||||
|
||||
createComponent<C>(componentFactory: ComponentFactory<C>, index: number = -1,
|
||||
injector: Injector = null, projectableNodes: any[][] = null): ComponentRef<C> {
|
||||
var s = this._createComponentInContainerScope();
|
||||
var contextInjector = isPresent(injector) ? injector : this._element.parentInjector;
|
||||
var componentRef = componentFactory.create(contextInjector, projectableNodes);
|
||||
this.insert(componentRef.hostView, index);
|
||||
return wtfLeave(s, componentRef);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_insertScope = wtfCreateScope('ViewContainerRef#insert()');
|
||||
|
||||
// TODO(i): refactor insert+remove into move
|
||||
insert(viewRef: ViewRef, index: number = -1): ViewRef {
|
||||
var s = this._insertScope();
|
||||
if (index == -1) index = this.length;
|
||||
var viewRef_ = <ViewRef_<any>>viewRef;
|
||||
this._element.attachView(viewRef_.internalView, index);
|
||||
return wtfLeave(s, viewRef_);
|
||||
}
|
||||
|
||||
indexOf(viewRef: ViewRef): number {
|
||||
return ListWrapper.indexOf(this._element.nestedViews, (<ViewRef_<any>>viewRef).internalView);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_removeScope = wtfCreateScope('ViewContainerRef#remove()');
|
||||
|
||||
// TODO(i): rename to destroy
|
||||
remove(index: number = -1): void {
|
||||
var s = this._removeScope();
|
||||
if (index == -1) index = this.length - 1;
|
||||
var view = this._element.detachView(index);
|
||||
view.destroy();
|
||||
// view is intentionally not returned to the client.
|
||||
wtfLeave(s);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_detachScope = wtfCreateScope('ViewContainerRef#detach()');
|
||||
|
||||
// TODO(i): refactor insert+remove into move
|
||||
detach(index: number = -1): ViewRef {
|
||||
var s = this._detachScope();
|
||||
if (index == -1) index = this.length - 1;
|
||||
var view = this._element.detachView(index);
|
||||
return wtfLeave(s, view.ref);
|
||||
}
|
||||
|
||||
clear() {
|
||||
for (var i = this.length - 1; i >= 0; i--) {
|
||||
this.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
100
modules/@angular/core/src/linker/view_ref.ts
Normal file
100
modules/@angular/core/src/linker/view_ref.ts
Normal file
@ -0,0 +1,100 @@
|
||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {ChangeDetectorRef} from '../change_detection/change_detector_ref';
|
||||
import {AppView} from './view';
|
||||
import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection/constants';
|
||||
|
||||
export abstract class ViewRef {
|
||||
get destroyed(): boolean { return <boolean>unimplemented(); }
|
||||
|
||||
abstract onDestroy(callback: Function);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an Angular View.
|
||||
*
|
||||
* <!-- TODO: move the next two paragraphs to the dev guide -->
|
||||
* A View is a fundamental building block of the application UI. It is the smallest grouping of
|
||||
* Elements which are created and destroyed together.
|
||||
*
|
||||
* Properties of elements in a View can change, but the structure (number and order) of elements in
|
||||
* a View cannot. Changing the structure of Elements can only be done by inserting, moving or
|
||||
* removing nested Views via a {@link ViewContainerRef}. Each View can contain many View Containers.
|
||||
* <!-- /TODO -->
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* Given this template...
|
||||
*
|
||||
* ```
|
||||
* Count: {{items.length}}
|
||||
* <ul>
|
||||
* <li *ngFor="let item of items">{{item}}</li>
|
||||
* </ul>
|
||||
* ```
|
||||
*
|
||||
* ... we have two {@link TemplateRef}s:
|
||||
*
|
||||
* Outer {@link TemplateRef}:
|
||||
* ```
|
||||
* Count: {{items.length}}
|
||||
* <ul>
|
||||
* <template ngFor let-item [ngForOf]="items"></template>
|
||||
* </ul>
|
||||
* ```
|
||||
*
|
||||
* Inner {@link TemplateRef}:
|
||||
* ```
|
||||
* <li>{{item}}</li>
|
||||
* ```
|
||||
*
|
||||
* Notice that the original template is broken down into two separate {@link TemplateRef}s.
|
||||
*
|
||||
* The outer/inner {@link TemplateRef}s are then assembled into views like so:
|
||||
*
|
||||
* ```
|
||||
* <!-- ViewRef: outer-0 -->
|
||||
* Count: 2
|
||||
* <ul>
|
||||
* <template view-container-ref></template>
|
||||
* <!-- ViewRef: inner-1 --><li>first</li><!-- /ViewRef: inner-1 -->
|
||||
* <!-- ViewRef: inner-2 --><li>second</li><!-- /ViewRef: inner-2 -->
|
||||
* </ul>
|
||||
* <!-- /ViewRef: outer-0 -->
|
||||
* ```
|
||||
*/
|
||||
export abstract class EmbeddedViewRef<C> extends ViewRef {
|
||||
get context(): C { return unimplemented(); }
|
||||
|
||||
get rootNodes(): any[] { return <any[]>unimplemented(); };
|
||||
|
||||
/**
|
||||
* Destroys the view and all of the data structures associated with it.
|
||||
*/
|
||||
abstract destroy();
|
||||
}
|
||||
|
||||
export class ViewRef_<C> implements EmbeddedViewRef<C>, ChangeDetectorRef {
|
||||
constructor(private _view: AppView<C>) { this._view = _view; }
|
||||
|
||||
get internalView(): AppView<C> { return this._view; }
|
||||
|
||||
get rootNodes(): any[] { return this._view.flatRootNodes; }
|
||||
|
||||
get context() { return this._view.context; }
|
||||
|
||||
get destroyed(): boolean { return this._view.destroyed; }
|
||||
|
||||
markForCheck(): void { this._view.markPathToRootAsCheckOnce(); }
|
||||
detach(): void { this._view.cdMode = ChangeDetectionStrategy.Detached; }
|
||||
detectChanges(): void { this._view.detectChanges(false); }
|
||||
checkNoChanges(): void { this._view.detectChanges(true); }
|
||||
reattach(): void {
|
||||
this._view.cdMode = ChangeDetectionStrategy.CheckAlways;
|
||||
this.markForCheck();
|
||||
}
|
||||
|
||||
onDestroy(callback: Function) { this._view.disposables.push(callback); }
|
||||
|
||||
destroy() { this._view.destroy(); }
|
||||
}
|
11
modules/@angular/core/src/linker/view_type.ts
Normal file
11
modules/@angular/core/src/linker/view_type.ts
Normal file
@ -0,0 +1,11 @@
|
||||
export enum ViewType {
|
||||
// A view that contains the host element with bound component directive.
|
||||
// Contains a COMPONENT view
|
||||
HOST,
|
||||
// The view of the component
|
||||
// Can contain 0 to n EMBEDDED views
|
||||
COMPONENT,
|
||||
// A view that is embedded into another View via a <template> element
|
||||
// inside of a COMPONENT view
|
||||
EMBEDDED
|
||||
}
|
362
modules/@angular/core/src/linker/view_utils.ts
Normal file
362
modules/@angular/core/src/linker/view_utils.ts
Normal file
@ -0,0 +1,362 @@
|
||||
import {isBlank, isPresent, Type, stringify, looseIdentical} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {AppElement} from './element';
|
||||
import {ExpressionChangedAfterItHasBeenCheckedException} from './exceptions';
|
||||
import {devModeEqual, uninitialized} from 'angular2/src/core/change_detection/change_detection';
|
||||
import {Inject, Injectable} from 'angular2/src/core/di';
|
||||
import {RootRenderer, RenderComponentType, Renderer} from 'angular2/src/core/render/api';
|
||||
import {APP_ID} from 'angular2/src/core/application_tokens';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||
|
||||
@Injectable()
|
||||
export class ViewUtils {
|
||||
private _nextCompTypeId: number = 0;
|
||||
|
||||
constructor(private _renderer: RootRenderer, @Inject(APP_ID) private _appId: string) {}
|
||||
|
||||
/**
|
||||
* Used by the generated code
|
||||
*/
|
||||
createRenderComponentType(templateUrl: string, slotCount: number,
|
||||
encapsulation: ViewEncapsulation,
|
||||
styles: Array<string | any[]>): RenderComponentType {
|
||||
return new RenderComponentType(`${this._appId}-${this._nextCompTypeId++}`, templateUrl,
|
||||
slotCount, encapsulation, styles);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
renderComponent(renderComponentType: RenderComponentType): Renderer {
|
||||
return this._renderer.renderComponent(renderComponentType);
|
||||
}
|
||||
}
|
||||
|
||||
export function flattenNestedViewRenderNodes(nodes: any[]): any[] {
|
||||
return _flattenNestedViewRenderNodes(nodes, []);
|
||||
}
|
||||
|
||||
function _flattenNestedViewRenderNodes(nodes: any[], renderNodes: any[]): any[] {
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var node = nodes[i];
|
||||
if (node instanceof AppElement) {
|
||||
var appEl = <AppElement>node;
|
||||
renderNodes.push(appEl.nativeElement);
|
||||
if (isPresent(appEl.nestedViews)) {
|
||||
for (var k = 0; k < appEl.nestedViews.length; k++) {
|
||||
_flattenNestedViewRenderNodes(appEl.nestedViews[k].rootNodesOrAppElements, renderNodes);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
renderNodes.push(node);
|
||||
}
|
||||
}
|
||||
return renderNodes;
|
||||
}
|
||||
|
||||
const EMPTY_ARR = /*@ts2dart_const*/[];
|
||||
|
||||
export function ensureSlotCount(projectableNodes: any[][], expectedSlotCount: number): any[][] {
|
||||
var res;
|
||||
if (isBlank(projectableNodes)) {
|
||||
res = EMPTY_ARR;
|
||||
} else if (projectableNodes.length < expectedSlotCount) {
|
||||
var givenSlotCount = projectableNodes.length;
|
||||
res = ListWrapper.createFixedSize(expectedSlotCount);
|
||||
for (var i = 0; i < expectedSlotCount; i++) {
|
||||
res[i] = (i < givenSlotCount) ? projectableNodes[i] : EMPTY_ARR;
|
||||
}
|
||||
} else {
|
||||
res = projectableNodes;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
export const MAX_INTERPOLATION_VALUES = 9;
|
||||
|
||||
export function interpolate(valueCount: number, c0: string, a1: any, c1: string, a2?: any,
|
||||
c2?: string, a3?: any, c3?: string, a4?: any, c4?: string, a5?: any,
|
||||
c5?: string, a6?: any, c6?: string, a7?: any, c7?: string, a8?: any,
|
||||
c8?: string, a9?: any, c9?: string): string {
|
||||
switch (valueCount) {
|
||||
case 1:
|
||||
return c0 + _toStringWithNull(a1) + c1;
|
||||
case 2:
|
||||
return c0 + _toStringWithNull(a1) + c1 + _toStringWithNull(a2) + c2;
|
||||
case 3:
|
||||
return c0 + _toStringWithNull(a1) + c1 + _toStringWithNull(a2) + c2 + _toStringWithNull(a3) +
|
||||
c3;
|
||||
case 4:
|
||||
return c0 + _toStringWithNull(a1) + c1 + _toStringWithNull(a2) + c2 + _toStringWithNull(a3) +
|
||||
c3 + _toStringWithNull(a4) + c4;
|
||||
case 5:
|
||||
return c0 + _toStringWithNull(a1) + c1 + _toStringWithNull(a2) + c2 + _toStringWithNull(a3) +
|
||||
c3 + _toStringWithNull(a4) + c4 + _toStringWithNull(a5) + c5;
|
||||
case 6:
|
||||
return c0 + _toStringWithNull(a1) + c1 + _toStringWithNull(a2) + c2 + _toStringWithNull(a3) +
|
||||
c3 + _toStringWithNull(a4) + c4 + _toStringWithNull(a5) + c5 + _toStringWithNull(a6) +
|
||||
c6;
|
||||
case 7:
|
||||
return c0 + _toStringWithNull(a1) + c1 + _toStringWithNull(a2) + c2 + _toStringWithNull(a3) +
|
||||
c3 + _toStringWithNull(a4) + c4 + _toStringWithNull(a5) + c5 + _toStringWithNull(a6) +
|
||||
c6 + _toStringWithNull(a7) + c7;
|
||||
case 8:
|
||||
return c0 + _toStringWithNull(a1) + c1 + _toStringWithNull(a2) + c2 + _toStringWithNull(a3) +
|
||||
c3 + _toStringWithNull(a4) + c4 + _toStringWithNull(a5) + c5 + _toStringWithNull(a6) +
|
||||
c6 + _toStringWithNull(a7) + c7 + _toStringWithNull(a8) + c8;
|
||||
case 9:
|
||||
return c0 + _toStringWithNull(a1) + c1 + _toStringWithNull(a2) + c2 + _toStringWithNull(a3) +
|
||||
c3 + _toStringWithNull(a4) + c4 + _toStringWithNull(a5) + c5 + _toStringWithNull(a6) +
|
||||
c6 + _toStringWithNull(a7) + c7 + _toStringWithNull(a8) + c8 + _toStringWithNull(a9) +
|
||||
c9;
|
||||
default:
|
||||
throw new BaseException(`Does not support more than 9 expressions`);
|
||||
}
|
||||
}
|
||||
|
||||
function _toStringWithNull(v: any): string {
|
||||
return v != null ? v.toString() : '';
|
||||
}
|
||||
|
||||
export function checkBinding(throwOnChange: boolean, oldValue: any, newValue: any): boolean {
|
||||
if (throwOnChange) {
|
||||
if (!devModeEqual(oldValue, newValue)) {
|
||||
throw new ExpressionChangedAfterItHasBeenCheckedException(oldValue, newValue, null);
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return !looseIdentical(oldValue, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
export function arrayLooseIdentical(a: any[], b: any[]): boolean {
|
||||
if (a.length != b.length) return false;
|
||||
for (var i = 0; i < a.length; ++i) {
|
||||
if (!looseIdentical(a[i], b[i])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function mapLooseIdentical<V>(m1: {[key: string]: V}, m2: {[key: string]: V}): boolean {
|
||||
var k1 = StringMapWrapper.keys(m1);
|
||||
var k2 = StringMapWrapper.keys(m2);
|
||||
if (k1.length != k2.length) {
|
||||
return false;
|
||||
}
|
||||
var key;
|
||||
for (var i = 0; i < k1.length; i++) {
|
||||
key = k1[i];
|
||||
if (!looseIdentical(m1[key], m2[key])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function castByValue<T>(input: any, value: T): T {
|
||||
return <T>input;
|
||||
}
|
||||
|
||||
export const EMPTY_ARRAY = /*@ts2dart_const*/[];
|
||||
export const EMPTY_MAP = /*@ts2dart_const*/ {};
|
||||
|
||||
export function pureProxy1<P0, R>(fn: (p0: P0) => R): (p0: P0) => R {
|
||||
var result: R;
|
||||
var v0;
|
||||
v0 = uninitialized;
|
||||
return (p0) => {
|
||||
if (!looseIdentical(v0, p0)) {
|
||||
v0 = p0;
|
||||
result = fn(p0);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
export function pureProxy2<P0, P1, R>(fn: (p0: P0, p1: P1) => R): (p0: P0, p1: P1) => R {
|
||||
var result: R;
|
||||
var v0, v1;
|
||||
v0 = v1 = uninitialized;
|
||||
return (p0, p1) => {
|
||||
if (!looseIdentical(v0, p0) || !looseIdentical(v1, p1)) {
|
||||
v0 = p0;
|
||||
v1 = p1;
|
||||
result = fn(p0, p1);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
export function pureProxy3<P0, P1, P2, R>(fn: (p0: P0, p1: P1, p2: P2) => R): (p0: P0, p1: P1,
|
||||
p2: P2) => R {
|
||||
var result: R;
|
||||
var v0, v1, v2;
|
||||
v0 = v1 = v2 = uninitialized;
|
||||
return (p0, p1, p2) => {
|
||||
if (!looseIdentical(v0, p0) || !looseIdentical(v1, p1) || !looseIdentical(v2, p2)) {
|
||||
v0 = p0;
|
||||
v1 = p1;
|
||||
v2 = p2;
|
||||
result = fn(p0, p1, p2);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
export function pureProxy4<P0, P1, P2, P3, R>(fn: (p0: P0, p1: P1, p2: P2, p3: P3) => R): (
|
||||
p0: P0, p1: P1, p2: P2, p3: P3) => R {
|
||||
var result: R;
|
||||
var v0, v1, v2, v3;
|
||||
v0 = v1 = v2 = v3 = uninitialized;
|
||||
return (p0, p1, p2, p3) => {
|
||||
if (!looseIdentical(v0, p0) || !looseIdentical(v1, p1) || !looseIdentical(v2, p2) ||
|
||||
!looseIdentical(v3, p3)) {
|
||||
v0 = p0;
|
||||
v1 = p1;
|
||||
v2 = p2;
|
||||
v3 = p3;
|
||||
result = fn(p0, p1, p2, p3);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
export function pureProxy5<P0, P1, P2, P3, P4, R>(
|
||||
fn: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4) => R): (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4) =>
|
||||
R {
|
||||
var result: R;
|
||||
var v0, v1, v2, v3, v4;
|
||||
v0 = v1 = v2 = v3 = v4 = uninitialized;
|
||||
return (p0, p1, p2, p3, p4) => {
|
||||
if (!looseIdentical(v0, p0) || !looseIdentical(v1, p1) || !looseIdentical(v2, p2) ||
|
||||
!looseIdentical(v3, p3) || !looseIdentical(v4, p4)) {
|
||||
v0 = p0;
|
||||
v1 = p1;
|
||||
v2 = p2;
|
||||
v3 = p3;
|
||||
v4 = p4;
|
||||
result = fn(p0, p1, p2, p3, p4);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function pureProxy6<P0, P1, P2, P3, P4, P5, R>(
|
||||
fn: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => R): (p0: P0, p1: P1, p2: P2, p3: P3,
|
||||
p4: P4, p5: P5) => R {
|
||||
var result: R;
|
||||
var v0, v1, v2, v3, v4, v5;
|
||||
v0 = v1 = v2 = v3 = v4 = v5 = uninitialized;
|
||||
return (p0, p1, p2, p3, p4, p5) => {
|
||||
if (!looseIdentical(v0, p0) || !looseIdentical(v1, p1) || !looseIdentical(v2, p2) ||
|
||||
!looseIdentical(v3, p3) || !looseIdentical(v4, p4) || !looseIdentical(v5, p5)) {
|
||||
v0 = p0;
|
||||
v1 = p1;
|
||||
v2 = p2;
|
||||
v3 = p3;
|
||||
v4 = p4;
|
||||
v5 = p5;
|
||||
result = fn(p0, p1, p2, p3, p4, p5);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
export function pureProxy7<P0, P1, P2, P3, P4, P5, P6, R>(
|
||||
fn: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6) =>
|
||||
R): (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6) => R {
|
||||
var result: R;
|
||||
var v0, v1, v2, v3, v4, v5, v6;
|
||||
v0 = v1 = v2 = v3 = v4 = v5 = v6 = uninitialized;
|
||||
return (p0, p1, p2, p3, p4, p5, p6) => {
|
||||
if (!looseIdentical(v0, p0) || !looseIdentical(v1, p1) || !looseIdentical(v2, p2) ||
|
||||
!looseIdentical(v3, p3) || !looseIdentical(v4, p4) || !looseIdentical(v5, p5) ||
|
||||
!looseIdentical(v6, p6)) {
|
||||
v0 = p0;
|
||||
v1 = p1;
|
||||
v2 = p2;
|
||||
v3 = p3;
|
||||
v4 = p4;
|
||||
v5 = p5;
|
||||
v6 = p6;
|
||||
result = fn(p0, p1, p2, p3, p4, p5, p6);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
export function pureProxy8<P0, P1, P2, P3, P4, P5, P6, P7, R>(
|
||||
fn: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7) =>
|
||||
R): (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7) => R {
|
||||
var result: R;
|
||||
var v0, v1, v2, v3, v4, v5, v6, v7;
|
||||
v0 = v1 = v2 = v3 = v4 = v5 = v6 = v7 = uninitialized;
|
||||
return (p0, p1, p2, p3, p4, p5, p6, p7) => {
|
||||
if (!looseIdentical(v0, p0) || !looseIdentical(v1, p1) || !looseIdentical(v2, p2) ||
|
||||
!looseIdentical(v3, p3) || !looseIdentical(v4, p4) || !looseIdentical(v5, p5) ||
|
||||
!looseIdentical(v6, p6) || !looseIdentical(v7, p7)) {
|
||||
v0 = p0;
|
||||
v1 = p1;
|
||||
v2 = p2;
|
||||
v3 = p3;
|
||||
v4 = p4;
|
||||
v5 = p5;
|
||||
v6 = p6;
|
||||
v7 = p7;
|
||||
result = fn(p0, p1, p2, p3, p4, p5, p6, p7);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
export function pureProxy9<P0, P1, P2, P3, P4, P5, P6, P7, P8, R>(
|
||||
fn: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8) =>
|
||||
R): (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8) => R {
|
||||
var result: R;
|
||||
var v0, v1, v2, v3, v4, v5, v6, v7, v8;
|
||||
v0 = v1 = v2 = v3 = v4 = v5 = v6 = v7 = v8 = uninitialized;
|
||||
return (p0, p1, p2, p3, p4, p5, p6, p7, p8) => {
|
||||
if (!looseIdentical(v0, p0) || !looseIdentical(v1, p1) || !looseIdentical(v2, p2) ||
|
||||
!looseIdentical(v3, p3) || !looseIdentical(v4, p4) || !looseIdentical(v5, p5) ||
|
||||
!looseIdentical(v6, p6) || !looseIdentical(v7, p7) || !looseIdentical(v8, p8)) {
|
||||
v0 = p0;
|
||||
v1 = p1;
|
||||
v2 = p2;
|
||||
v3 = p3;
|
||||
v4 = p4;
|
||||
v5 = p5;
|
||||
v6 = p6;
|
||||
v7 = p7;
|
||||
v8 = p8;
|
||||
result = fn(p0, p1, p2, p3, p4, p5, p6, p7, p8);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
export function pureProxy10<P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, R>(
|
||||
fn: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9) =>
|
||||
R): (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9) => R {
|
||||
var result: R;
|
||||
var v0, v1, v2, v3, v4, v5, v6, v7, v8, v9;
|
||||
v0 = v1 = v2 = v3 = v4 = v5 = v6 = v7 = v8 = v9 = uninitialized;
|
||||
return (p0, p1, p2, p3, p4, p5, p6, p7, p8, p9) => {
|
||||
if (!looseIdentical(v0, p0) || !looseIdentical(v1, p1) || !looseIdentical(v2, p2) ||
|
||||
!looseIdentical(v3, p3) || !looseIdentical(v4, p4) || !looseIdentical(v5, p5) ||
|
||||
!looseIdentical(v6, p6) || !looseIdentical(v7, p7) || !looseIdentical(v8, p8) ||
|
||||
!looseIdentical(v9, p9)) {
|
||||
v0 = p0;
|
||||
v1 = p1;
|
||||
v2 = p2;
|
||||
v3 = p3;
|
||||
v4 = p4;
|
||||
v5 = p5;
|
||||
v6 = p6;
|
||||
v7 = p7;
|
||||
v8 = p8;
|
||||
v9 = p9;
|
||||
result = fn(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
219
modules/@angular/core/src/metadata.dart
Normal file
219
modules/@angular/core/src/metadata.dart
Normal file
@ -0,0 +1,219 @@
|
||||
library angular2.src.core.metadata;
|
||||
|
||||
import 'package:angular2/src/facade/collection.dart' show List;
|
||||
import 'package:angular2/src/core/change_detection/change_detection.dart';
|
||||
import './metadata/di.dart';
|
||||
import './metadata/directives.dart';
|
||||
import './metadata/view.dart';
|
||||
|
||||
export './metadata/di.dart';
|
||||
export './metadata/directives.dart';
|
||||
export './metadata/view.dart' hide VIEW_ENCAPSULATION_VALUES;
|
||||
export './metadata/lifecycle_hooks.dart' show
|
||||
AfterContentInit,
|
||||
AfterContentChecked,
|
||||
AfterViewInit,
|
||||
AfterViewChecked,
|
||||
OnChanges,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
DoCheck;
|
||||
|
||||
/**
|
||||
* See: [DirectiveMetadata] for docs.
|
||||
*/
|
||||
class Directive extends DirectiveMetadata {
|
||||
const Directive(
|
||||
{String selector,
|
||||
List<String> inputs,
|
||||
List<String> outputs,
|
||||
@Deprecated('Use `inputs` or `@Input` instead')
|
||||
List<String> properties,
|
||||
@Deprecated('Use `outputs` or `@Output` instead')
|
||||
List<String> events,
|
||||
Map<String, String> host,
|
||||
@Deprecated('Use `providers` instead')
|
||||
List bindings,
|
||||
List providers,
|
||||
String exportAs,
|
||||
Map<String, dynamic> queries})
|
||||
: super(
|
||||
selector: selector,
|
||||
inputs: inputs,
|
||||
outputs: outputs,
|
||||
properties: properties,
|
||||
events: events,
|
||||
host: host,
|
||||
bindings: bindings,
|
||||
providers: providers,
|
||||
exportAs: exportAs,
|
||||
queries: queries);
|
||||
}
|
||||
|
||||
/**
|
||||
* See: [ComponentMetadata] for docs.
|
||||
*/
|
||||
class Component extends ComponentMetadata {
|
||||
const Component(
|
||||
{String selector,
|
||||
List<String> inputs,
|
||||
List<String> outputs,
|
||||
@Deprecated('Use `inputs` or `@Input` instead')
|
||||
List<String> properties,
|
||||
@Deprecated('Use `outputs` or `@Output` instead')
|
||||
List<String> events,
|
||||
Map<String, String> host,
|
||||
@Deprecated('Use `providers` instead')
|
||||
List bindings,
|
||||
List providers,
|
||||
String exportAs,
|
||||
String moduleId,
|
||||
Map<String, dynamic> queries,
|
||||
@Deprecated('Use `viewProviders` instead')
|
||||
List viewBindings,
|
||||
List viewProviders,
|
||||
ChangeDetectionStrategy changeDetection,
|
||||
String templateUrl,
|
||||
String template,
|
||||
dynamic directives,
|
||||
dynamic pipes,
|
||||
ViewEncapsulation encapsulation,
|
||||
List<String> styles,
|
||||
List<String> styleUrls})
|
||||
: super(
|
||||
selector: selector,
|
||||
inputs: inputs,
|
||||
outputs: outputs,
|
||||
properties: properties,
|
||||
events: events,
|
||||
host: host,
|
||||
bindings: bindings,
|
||||
providers: providers,
|
||||
exportAs: exportAs,
|
||||
moduleId: moduleId,
|
||||
viewBindings: viewBindings,
|
||||
viewProviders: viewProviders,
|
||||
queries: queries,
|
||||
changeDetection: changeDetection,
|
||||
templateUrl: templateUrl,
|
||||
template: template,
|
||||
directives: directives,
|
||||
pipes: pipes,
|
||||
encapsulation: encapsulation,
|
||||
styles: styles,
|
||||
styleUrls: styleUrls);
|
||||
}
|
||||
|
||||
/**
|
||||
* See: [ViewMetadata] for docs.
|
||||
*/
|
||||
class View extends ViewMetadata {
|
||||
const View(
|
||||
{String templateUrl,
|
||||
String template,
|
||||
dynamic directives,
|
||||
dynamic pipes,
|
||||
ViewEncapsulation encapsulation,
|
||||
List<String> styles,
|
||||
List<String> styleUrls})
|
||||
: super(
|
||||
templateUrl: templateUrl,
|
||||
template: template,
|
||||
directives: directives,
|
||||
pipes: pipes,
|
||||
encapsulation: encapsulation,
|
||||
styles: styles,
|
||||
styleUrls: styleUrls);
|
||||
}
|
||||
|
||||
/**
|
||||
* See: [PipeMetadata] for docs.
|
||||
*/
|
||||
class Pipe extends PipeMetadata {
|
||||
const Pipe({name, pure}) : super(name: name, pure: pure);
|
||||
}
|
||||
|
||||
/**
|
||||
* See: [AttributeMetadata] for docs.
|
||||
*/
|
||||
class Attribute extends AttributeMetadata {
|
||||
const Attribute(String attributeName) : super(attributeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* See: [QueryMetadata] for docs.
|
||||
*/
|
||||
@Deprecated("Use ContentChildren/ContentChild instead")
|
||||
class Query extends QueryMetadata {
|
||||
const Query(dynamic /*Type | string*/ selector,
|
||||
{bool descendants: false, dynamic read: null})
|
||||
: super(selector, descendants: descendants, read: read);
|
||||
}
|
||||
|
||||
/**
|
||||
* See: [ContentChildrenMetadata] for docs.
|
||||
*/
|
||||
class ContentChildren extends ContentChildrenMetadata {
|
||||
const ContentChildren(dynamic /*Type | string*/ selector,
|
||||
{bool descendants: false, dynamic read: null})
|
||||
: super(selector, descendants: descendants, read: read);
|
||||
}
|
||||
|
||||
/**
|
||||
* See: [ContentChildMetadata] for docs.
|
||||
*/
|
||||
class ContentChild extends ContentChildMetadata {
|
||||
const ContentChild(dynamic /*Type | string*/ selector, {dynamic read: null}) : super(selector, read: read);
|
||||
}
|
||||
|
||||
/**
|
||||
* See: [ViewQueryMetadata] for docs.
|
||||
*/
|
||||
@Deprecated("Use ViewChildren/ViewChild instead")
|
||||
class ViewQuery extends ViewQueryMetadata {
|
||||
const ViewQuery(dynamic /*Type | string*/ selector, {dynamic read: null})
|
||||
: super(selector, descendants: true, read: read);
|
||||
}
|
||||
|
||||
/**
|
||||
* See: [ViewChildrenMetadata] for docs.
|
||||
*/
|
||||
class ViewChildren extends ViewChildrenMetadata {
|
||||
const ViewChildren(dynamic /*Type | string*/ selector, {dynamic read: null}) : super(selector, read: read);
|
||||
}
|
||||
|
||||
/**
|
||||
* See: [ViewChildMetadata] for docs.
|
||||
*/
|
||||
class ViewChild extends ViewChildMetadata {
|
||||
const ViewChild(dynamic /*Type | string*/ selector, {dynamic read: null}) : super(selector, read: read);
|
||||
}
|
||||
|
||||
/**
|
||||
* See: [InputMetadata] for docs.
|
||||
*/
|
||||
class Input extends InputMetadata {
|
||||
const Input([String bindingPropertyName]) : super(bindingPropertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* See: [OutputMetadata] for docs.
|
||||
*/
|
||||
class Output extends OutputMetadata {
|
||||
const Output([String bindingPropertyName]) : super(bindingPropertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* See: [HostBindingMetadata] for docs.
|
||||
*/
|
||||
class HostBinding extends HostBindingMetadata {
|
||||
const HostBinding([String hostPropertyName]) : super(hostPropertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* See: [HostListenerMetadata] for docs.
|
||||
*/
|
||||
class HostListener extends HostListenerMetadata {
|
||||
const HostListener(String eventName, [List<String> args])
|
||||
: super(eventName, args);
|
||||
}
|
1475
modules/@angular/core/src/metadata.ts
Normal file
1475
modules/@angular/core/src/metadata.ts
Normal file
File diff suppressed because it is too large
Load Diff
456
modules/@angular/core/src/metadata/di.ts
Normal file
456
modules/@angular/core/src/metadata/di.ts
Normal file
@ -0,0 +1,456 @@
|
||||
import {Type, stringify, isPresent, isString} from 'angular2/src/facade/lang';
|
||||
import {resolveForwardRef} from 'angular2/src/core/di';
|
||||
import {DependencyMetadata} from 'angular2/src/core/di/metadata';
|
||||
|
||||
/**
|
||||
* Specifies that a constant attribute value should be injected.
|
||||
*
|
||||
* The directive can inject constant string literals of host element attributes.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* Suppose we have an `<input>` element and want to know its `type`.
|
||||
*
|
||||
* ```html
|
||||
* <input type="text">
|
||||
* ```
|
||||
*
|
||||
* A decorator can inject string literal `text` like so:
|
||||
*
|
||||
* {@example core/ts/metadata/metadata.ts region='attributeMetadata'}
|
||||
* @ts2dart_const
|
||||
*/
|
||||
export class AttributeMetadata extends DependencyMetadata {
|
||||
constructor(public attributeName: string) { super(); }
|
||||
|
||||
get token(): AttributeMetadata {
|
||||
// Normally one would default a token to a type of an injected value but here
|
||||
// the type of a variable is "string" and we can't use primitive type as a return value
|
||||
// so we use instance of Attribute instead. This doesn't matter much in practice as arguments
|
||||
// with @Attribute annotation are injected by ElementInjector that doesn't take tokens into
|
||||
// account.
|
||||
return this;
|
||||
}
|
||||
toString(): string { return `@Attribute(${stringify(this.attributeName)})`; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares an injectable parameter to be a live list of directives or variable
|
||||
* bindings from the content children of a directive.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/lY9m8HLy7z06vDoUaSN2?p=preview))
|
||||
*
|
||||
* Assume that `<tabs>` component would like to get a list its children `<pane>`
|
||||
* components as shown in this example:
|
||||
*
|
||||
* ```html
|
||||
* <tabs>
|
||||
* <pane title="Overview">...</pane>
|
||||
* <pane *ngFor="let o of objects" [title]="o.title">{{o.text}}</pane>
|
||||
* </tabs>
|
||||
* ```
|
||||
*
|
||||
* The preferred solution is to query for `Pane` directives using this decorator.
|
||||
*
|
||||
* ```javascript
|
||||
* @Component({
|
||||
* selector: 'pane',
|
||||
* inputs: ['title']
|
||||
* })
|
||||
* class Pane {
|
||||
* title:string;
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'tabs',
|
||||
* template: `
|
||||
* <ul>
|
||||
* <li *ngFor="let pane of panes">{{pane.title}}</li>
|
||||
* </ul>
|
||||
* <ng-content></ng-content>
|
||||
* `
|
||||
* })
|
||||
* class Tabs {
|
||||
* panes: QueryList<Pane>;
|
||||
* constructor(@Query(Pane) panes:QueryList<Pane>) {
|
||||
* this.panes = panes;
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* A query can look for variable bindings by passing in a string with desired binding symbol.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/sT2j25cH1dURAyBRCKx1?p=preview))
|
||||
* ```html
|
||||
* <seeker>
|
||||
* <div #findme>...</div>
|
||||
* </seeker>
|
||||
*
|
||||
* @Component({ selector: 'seeker' })
|
||||
* class Seeker {
|
||||
* constructor(@Query('findme') elList: QueryList<ElementRef>) {...}
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In this case the object that is injected depend on the type of the variable
|
||||
* binding. It can be an ElementRef, a directive or a component.
|
||||
*
|
||||
* Passing in a comma separated list of variable bindings will query for all of them.
|
||||
*
|
||||
* ```html
|
||||
* <seeker>
|
||||
* <div #find-me>...</div>
|
||||
* <div #find-me-too>...</div>
|
||||
* </seeker>
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'seeker'
|
||||
* })
|
||||
* class Seeker {
|
||||
* constructor(@Query('findMe, findMeToo') elList: QueryList<ElementRef>) {...}
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Configure whether query looks for direct children or all descendants
|
||||
* of the querying element, by using the `descendants` parameter.
|
||||
* It is set to `false` by default.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/wtGeB977bv7qvA5FTYl9?p=preview))
|
||||
* ```html
|
||||
* <container #first>
|
||||
* <item>a</item>
|
||||
* <item>b</item>
|
||||
* <container #second>
|
||||
* <item>c</item>
|
||||
* </container>
|
||||
* </container>
|
||||
* ```
|
||||
*
|
||||
* When querying for items, the first container will see only `a` and `b` by default,
|
||||
* but with `Query(TextDirective, {descendants: true})` it will see `c` too.
|
||||
*
|
||||
* The queried directives are kept in a depth-first pre-order with respect to their
|
||||
* positions in the DOM.
|
||||
*
|
||||
* Query does not look deep into any subcomponent views.
|
||||
*
|
||||
* Query is updated as part of the change-detection cycle. Since change detection
|
||||
* happens after construction of a directive, QueryList will always be empty when observed in the
|
||||
* constructor.
|
||||
*
|
||||
* The injected object is an unmodifiable live list.
|
||||
* See {@link QueryList} for more details.
|
||||
* @ts2dart_const
|
||||
*/
|
||||
export class QueryMetadata extends DependencyMetadata {
|
||||
/**
|
||||
* whether we want to query only direct children (false) or all
|
||||
* children (true).
|
||||
*/
|
||||
descendants: boolean;
|
||||
first: boolean;
|
||||
/**
|
||||
* The DI token to read from an element that matches the selector.
|
||||
*/
|
||||
read: any;
|
||||
|
||||
constructor(private _selector: Type | string,
|
||||
{descendants = false, first = false,
|
||||
read = null}: {descendants?: boolean, first?: boolean, read?: any} = {}) {
|
||||
super();
|
||||
this.descendants = descendants;
|
||||
this.first = first;
|
||||
this.read = read;
|
||||
}
|
||||
|
||||
/**
|
||||
* always `false` to differentiate it with {@link ViewQueryMetadata}.
|
||||
*/
|
||||
get isViewQuery(): boolean { return false; }
|
||||
|
||||
/**
|
||||
* what this is querying for.
|
||||
*/
|
||||
get selector() { return resolveForwardRef(this._selector); }
|
||||
|
||||
/**
|
||||
* whether this is querying for a variable binding or a directive.
|
||||
*/
|
||||
get isVarBindingQuery(): boolean { return isString(this.selector); }
|
||||
|
||||
/**
|
||||
* returns a list of variable bindings this is querying for.
|
||||
* Only applicable if this is a variable bindings query.
|
||||
*/
|
||||
get varBindings(): string[] { return this.selector.split(','); }
|
||||
|
||||
toString(): string { return `@Query(${stringify(this.selector)})`; }
|
||||
}
|
||||
|
||||
// TODO: add an example after ContentChildren and ViewChildren are in master
|
||||
/**
|
||||
* Configures a content query.
|
||||
*
|
||||
* Content queries are set before the `ngAfterContentInit` callback is called.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* @Directive({
|
||||
* selector: 'someDir'
|
||||
* })
|
||||
* class SomeDir {
|
||||
* @ContentChildren(ChildDirective) contentChildren: QueryList<ChildDirective>;
|
||||
*
|
||||
* ngAfterContentInit() {
|
||||
* // contentChildren is set
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* @ts2dart_const
|
||||
*/
|
||||
export class ContentChildrenMetadata extends QueryMetadata {
|
||||
constructor(_selector: Type | string,
|
||||
{descendants = false, read = null}: {descendants?: boolean, read?: any} = {}) {
|
||||
super(_selector, {descendants: descendants, read: read});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: add an example after ContentChild and ViewChild are in master
|
||||
/**
|
||||
* Configures a content query.
|
||||
*
|
||||
* Content queries are set before the `ngAfterContentInit` callback is called.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* @Directive({
|
||||
* selector: 'someDir'
|
||||
* })
|
||||
* class SomeDir {
|
||||
* @ContentChild(ChildDirective) contentChild;
|
||||
*
|
||||
* ngAfterContentInit() {
|
||||
* // contentChild is set
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* @ts2dart_const
|
||||
*/
|
||||
export class ContentChildMetadata extends QueryMetadata {
|
||||
constructor(_selector: Type | string, {read = null}: {read?: any} = {}) {
|
||||
super(_selector, {descendants: true, first: true, read: read});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link QueryMetadata}, but querying the component view, instead of
|
||||
* the content children.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/eNsFHDf7YjyM6IzKxM1j?p=preview))
|
||||
*
|
||||
* ```javascript
|
||||
* @Component({
|
||||
* ...,
|
||||
* template: `
|
||||
* <item> a </item>
|
||||
* <item> b </item>
|
||||
* <item> c </item>
|
||||
* `
|
||||
* })
|
||||
* class MyComponent {
|
||||
* shown: boolean;
|
||||
*
|
||||
* constructor(private @ViewQuery(Item) items:QueryList<Item>) {
|
||||
* items.changes.subscribe(() => console.log(items.length));
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Supports the same querying parameters as {@link QueryMetadata}, except
|
||||
* `descendants`. This always queries the whole view.
|
||||
*
|
||||
* As `shown` is flipped between true and false, items will contain zero of one
|
||||
* items.
|
||||
*
|
||||
* Specifies that a {@link QueryList} should be injected.
|
||||
*
|
||||
* The injected object is an iterable and observable live list.
|
||||
* See {@link QueryList} for more details.
|
||||
* @ts2dart_const
|
||||
*/
|
||||
export class ViewQueryMetadata extends QueryMetadata {
|
||||
constructor(_selector: Type | string,
|
||||
{descendants = false, first = false,
|
||||
read = null}: {descendants?: boolean, first?: boolean, read?: any} = {}) {
|
||||
super(_selector, {descendants: descendants, first: first, read: read});
|
||||
}
|
||||
|
||||
/**
|
||||
* always `true` to differentiate it with {@link QueryMetadata}.
|
||||
*/
|
||||
get isViewQuery() { return true; }
|
||||
toString(): string { return `@ViewQuery(${stringify(this.selector)})`; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares a list of child element references.
|
||||
*
|
||||
* Angular automatically updates the list when the DOM is updated.
|
||||
*
|
||||
* `ViewChildren` takes an argument to select elements.
|
||||
*
|
||||
* - If the argument is a type, directives or components with the type will be bound.
|
||||
*
|
||||
* - If the argument is a string, the string is interpreted as a list of comma-separated selectors.
|
||||
* For each selector, an element containing the matching template variable (e.g. `#child`) will be
|
||||
* bound.
|
||||
*
|
||||
* View children are set before the `ngAfterViewInit` callback is called.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* With type selector:
|
||||
*
|
||||
* ```
|
||||
* @Component({
|
||||
* selector: 'child-cmp',
|
||||
* template: '<p>child</p>'
|
||||
* })
|
||||
* class ChildCmp {
|
||||
* doSomething() {}
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'some-cmp',
|
||||
* template: `
|
||||
* <child-cmp></child-cmp>
|
||||
* <child-cmp></child-cmp>
|
||||
* <child-cmp></child-cmp>
|
||||
* `,
|
||||
* directives: [ChildCmp]
|
||||
* })
|
||||
* class SomeCmp {
|
||||
* @ViewChildren(ChildCmp) children:QueryList<ChildCmp>;
|
||||
*
|
||||
* ngAfterViewInit() {
|
||||
* // children are set
|
||||
* this.children.toArray().forEach((child)=>child.doSomething());
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* With string selector:
|
||||
*
|
||||
* ```
|
||||
* @Component({
|
||||
* selector: 'child-cmp',
|
||||
* template: '<p>child</p>'
|
||||
* })
|
||||
* class ChildCmp {
|
||||
* doSomething() {}
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'some-cmp',
|
||||
* template: `
|
||||
* <child-cmp #child1></child-cmp>
|
||||
* <child-cmp #child2></child-cmp>
|
||||
* <child-cmp #child3></child-cmp>
|
||||
* `,
|
||||
* directives: [ChildCmp]
|
||||
* })
|
||||
* class SomeCmp {
|
||||
* @ViewChildren('child1,child2,child3') children:QueryList<ChildCmp>;
|
||||
*
|
||||
* ngAfterViewInit() {
|
||||
* // children are set
|
||||
* this.children.toArray().forEach((child)=>child.doSomething());
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* @ts2dart_const
|
||||
*/
|
||||
export class ViewChildrenMetadata extends ViewQueryMetadata {
|
||||
constructor(_selector: Type | string, {read = null}: {read?: any} = {}) {
|
||||
super(_selector, {descendants: true, read: read});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Declares a reference of child element.
|
||||
*
|
||||
* `ViewChildren` takes an argument to select elements.
|
||||
*
|
||||
* - If the argument is a type, a directive or a component with the type will be bound.
|
||||
*
|
||||
If the argument is a string, the string is interpreted as a selector. An element containing the
|
||||
matching template variable (e.g. `#child`) will be bound.
|
||||
*
|
||||
* In either case, `@ViewChild()` assigns the first (looking from above) element if there are
|
||||
multiple matches.
|
||||
*
|
||||
* View child is set before the `ngAfterViewInit` callback is called.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* With type selector:
|
||||
*
|
||||
* ```
|
||||
* @Component({
|
||||
* selector: 'child-cmp',
|
||||
* template: '<p>child</p>'
|
||||
* })
|
||||
* class ChildCmp {
|
||||
* doSomething() {}
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'some-cmp',
|
||||
* template: '<child-cmp></child-cmp>',
|
||||
* directives: [ChildCmp]
|
||||
* })
|
||||
* class SomeCmp {
|
||||
* @ViewChild(ChildCmp) child:ChildCmp;
|
||||
*
|
||||
* ngAfterViewInit() {
|
||||
* // child is set
|
||||
* this.child.doSomething();
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* With string selector:
|
||||
*
|
||||
* ```
|
||||
* @Component({
|
||||
* selector: 'child-cmp',
|
||||
* template: '<p>child</p>'
|
||||
* })
|
||||
* class ChildCmp {
|
||||
* doSomething() {}
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'some-cmp',
|
||||
* template: '<child-cmp #child></child-cmp>',
|
||||
* directives: [ChildCmp]
|
||||
* })
|
||||
* class SomeCmp {
|
||||
* @ViewChild('child') child:ChildCmp;
|
||||
*
|
||||
* ngAfterViewInit() {
|
||||
* // child is set
|
||||
* this.child.doSomething();
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* @ts2dart_const
|
||||
*/
|
||||
export class ViewChildMetadata extends ViewQueryMetadata {
|
||||
constructor(_selector: Type | string, {read = null}: {read?: any} = {}) {
|
||||
super(_selector, {descendants: true, first: true, read: read});
|
||||
}
|
||||
}
|
1132
modules/@angular/core/src/metadata/directives.ts
Normal file
1132
modules/@angular/core/src/metadata/directives.ts
Normal file
File diff suppressed because it is too large
Load Diff
477
modules/@angular/core/src/metadata/lifecycle_hooks.ts
Normal file
477
modules/@angular/core/src/metadata/lifecycle_hooks.ts
Normal file
@ -0,0 +1,477 @@
|
||||
import {SimpleChange} from 'angular2/src/core/change_detection/change_detection_util';
|
||||
|
||||
export enum LifecycleHooks {
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
DoCheck,
|
||||
OnChanges,
|
||||
AfterContentInit,
|
||||
AfterContentChecked,
|
||||
AfterViewInit,
|
||||
AfterViewChecked
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export var LIFECYCLE_HOOKS_VALUES = [
|
||||
LifecycleHooks.OnInit,
|
||||
LifecycleHooks.OnDestroy,
|
||||
LifecycleHooks.DoCheck,
|
||||
LifecycleHooks.OnChanges,
|
||||
LifecycleHooks.AfterContentInit,
|
||||
LifecycleHooks.AfterContentChecked,
|
||||
LifecycleHooks.AfterViewInit,
|
||||
LifecycleHooks.AfterViewChecked
|
||||
];
|
||||
|
||||
/**
|
||||
* Lifecycle hooks are guaranteed to be called in the following order:
|
||||
* - `OnChanges` (if any bindings have changed),
|
||||
* - `OnInit` (after the first check only),
|
||||
* - `DoCheck`,
|
||||
* - `AfterContentInit`,
|
||||
* - `AfterContentChecked`,
|
||||
* - `AfterViewInit`,
|
||||
* - `AfterViewChecked`,
|
||||
* - `OnDestroy` (at the very end before destruction)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implement this interface to get notified when any data-bound property of your directive changes.
|
||||
*
|
||||
* `ngOnChanges` is called right after the data-bound properties have been checked and before view
|
||||
* and content children are checked if at least one of them has changed.
|
||||
*
|
||||
* The `changes` parameter contains an entry for each of the changed data-bound property. The key is
|
||||
* the property name and the value is an instance of {@link SimpleChange}.
|
||||
*
|
||||
* ### Example ([live example](http://plnkr.co/edit/AHrB6opLqHDBPkt4KpdT?p=preview)):
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: 'my-cmp',
|
||||
* template: `<p>myProp = {{myProp}}</p>`
|
||||
* })
|
||||
* class MyComponent implements OnChanges {
|
||||
* @Input() myProp: any;
|
||||
*
|
||||
* ngOnChanges(changes: {[propName: string]: SimpleChange}) {
|
||||
* console.log('ngOnChanges - myProp = ' + changes['myProp'].currentValue);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `
|
||||
* <button (click)="value = value + 1">Change MyComponent</button>
|
||||
* <my-cmp [my-prop]="value"></my-cmp>`,
|
||||
* directives: [MyComponent]
|
||||
* })
|
||||
* export class App {
|
||||
* value = 0;
|
||||
* }
|
||||
*
|
||||
* bootstrap(App).catch(err => console.error(err));
|
||||
* ```
|
||||
*/
|
||||
export interface OnChanges { ngOnChanges(changes: {[key: string]: SimpleChange}); }
|
||||
|
||||
/**
|
||||
* Implement this interface to execute custom initialization logic after your directive's
|
||||
* data-bound properties have been initialized.
|
||||
*
|
||||
* `ngOnInit` is called right after the directive's data-bound properties have been checked for the
|
||||
* first time, and before any of its children have been checked. It is invoked only once when the
|
||||
* directive is instantiated.
|
||||
*
|
||||
* ### Example ([live example](http://plnkr.co/edit/1MBypRryXd64v4pV03Yn?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: 'my-cmp',
|
||||
* template: `<p>my-component</p>`
|
||||
* })
|
||||
* class MyComponent implements OnInit, OnDestroy {
|
||||
* ngOnInit() {
|
||||
* console.log('ngOnInit');
|
||||
* }
|
||||
*
|
||||
* ngOnDestroy() {
|
||||
* console.log('ngOnDestroy');
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `
|
||||
* <button (click)="hasChild = !hasChild">
|
||||
* {{hasChild ? 'Destroy' : 'Create'}} MyComponent
|
||||
* </button>
|
||||
* <my-cmp *ngIf="hasChild"></my-cmp>`,
|
||||
* directives: [MyComponent, NgIf]
|
||||
* })
|
||||
* export class App {
|
||||
* hasChild = true;
|
||||
* }
|
||||
*
|
||||
* bootstrap(App).catch(err => console.error(err));
|
||||
* ```
|
||||
*/
|
||||
export interface OnInit { ngOnInit(); }
|
||||
|
||||
/**
|
||||
* Implement this interface to override the default change detection algorithm for your directive.
|
||||
*
|
||||
* `ngDoCheck` gets called to check the changes in the directives instead of the default algorithm.
|
||||
*
|
||||
* The default change detection algorithm looks for differences by comparing bound-property values
|
||||
* by reference across change detection runs. When `DoCheck` is implemented, the default algorithm
|
||||
* is disabled and `ngDoCheck` is responsible for checking for changes.
|
||||
*
|
||||
* Implementing this interface allows improving performance by using insights about the component,
|
||||
* its implementation and data types of its properties.
|
||||
*
|
||||
* Note that a directive should not implement both `DoCheck` and {@link OnChanges} at the same time.
|
||||
* `ngOnChanges` would not be called when a directive implements `DoCheck`. Reaction to the changes
|
||||
* have to be handled from within the `ngDoCheck` callback.
|
||||
*
|
||||
* Use {@link KeyValueDiffers} and {@link IterableDiffers} to add your custom check mechanisms.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/QpnIlF0CR2i5bcYbHEUJ?p=preview))
|
||||
*
|
||||
* In the following example `ngDoCheck` uses an {@link IterableDiffers} to detect the updates to the
|
||||
* array `list`:
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: 'custom-check',
|
||||
* template: `
|
||||
* <p>Changes:</p>
|
||||
* <ul>
|
||||
* <li *ngFor="let line of logs">{{line}}</li>
|
||||
* </ul>`,
|
||||
* directives: [NgFor]
|
||||
* })
|
||||
* class CustomCheckComponent implements DoCheck {
|
||||
* @Input() list: any[];
|
||||
* differ: any;
|
||||
* logs = [];
|
||||
*
|
||||
* constructor(differs: IterableDiffers) {
|
||||
* this.differ = differs.find([]).create(null);
|
||||
* }
|
||||
*
|
||||
* ngDoCheck() {
|
||||
* var changes = this.differ.diff(this.list);
|
||||
*
|
||||
* if (changes) {
|
||||
* changes.forEachAddedItem(r => this.logs.push('added ' + r.item));
|
||||
* changes.forEachRemovedItem(r => this.logs.push('removed ' + r.item))
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `
|
||||
* <button (click)="list.push(list.length)">Push</button>
|
||||
* <button (click)="list.pop()">Pop</button>
|
||||
* <custom-check [list]="list"></custom-check>`,
|
||||
* directives: [CustomCheckComponent]
|
||||
* })
|
||||
* export class App {
|
||||
* list = [];
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export interface DoCheck { ngDoCheck(); }
|
||||
|
||||
/**
|
||||
* Implement this interface to get notified when your directive is destroyed.
|
||||
*
|
||||
* `ngOnDestroy` callback is typically used for any custom cleanup that needs to occur when the
|
||||
* instance is destroyed
|
||||
*
|
||||
* ### Example ([live example](http://plnkr.co/edit/1MBypRryXd64v4pV03Yn?p=preview))
|
||||
*
|
||||
* ```typesript
|
||||
* @Component({
|
||||
* selector: 'my-cmp',
|
||||
* template: `<p>my-component</p>`
|
||||
* })
|
||||
* class MyComponent implements OnInit, OnDestroy {
|
||||
* ngOnInit() {
|
||||
* console.log('ngOnInit');
|
||||
* }
|
||||
*
|
||||
* ngOnDestroy() {
|
||||
* console.log('ngOnDestroy');
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `
|
||||
* <button (click)="hasChild = !hasChild">
|
||||
* {{hasChild ? 'Destroy' : 'Create'}} MyComponent
|
||||
* </button>
|
||||
* <my-cmp *ngIf="hasChild"></my-cmp>`,
|
||||
* directives: [MyComponent, NgIf]
|
||||
* })
|
||||
* export class App {
|
||||
* hasChild = true;
|
||||
* }
|
||||
*
|
||||
* bootstrap(App).catch(err => console.error(err));
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* To create a stateful Pipe, you should implement this interface and set the `pure`
|
||||
* parameter to `false` in the {@link PipeMetadata}.
|
||||
*
|
||||
* A stateful pipe may produce different output, given the same input. It is
|
||||
* likely that a stateful pipe may contain state that should be cleaned up when
|
||||
* a binding is destroyed. For example, a subscription to a stream of data may need to
|
||||
* be disposed, or an interval may need to be cleared.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/i8pm5brO4sPaLxBx56MR?p=preview))
|
||||
*
|
||||
* In this example, a pipe is created to countdown its input value, updating it every
|
||||
* 50ms. Because it maintains an internal interval, it automatically clears
|
||||
* the interval when the binding is destroyed or the countdown completes.
|
||||
*
|
||||
* ```
|
||||
* import {OnDestroy, Pipe, PipeTransform} from 'angular2/core'
|
||||
* @Pipe({name: 'countdown', pure: false})
|
||||
* class CountDown implements PipeTransform, OnDestroy {
|
||||
* remainingTime:Number;
|
||||
* interval:SetInterval;
|
||||
* ngOnDestroy() {
|
||||
* if (this.interval) {
|
||||
* clearInterval(this.interval);
|
||||
* }
|
||||
* }
|
||||
* transform(value: any, args: any[] = []) {
|
||||
* if (!parseInt(value, 10)) return null;
|
||||
* if (typeof this.remainingTime !== 'number') {
|
||||
* this.remainingTime = parseInt(value, 10);
|
||||
* }
|
||||
* if (!this.interval) {
|
||||
* this.interval = setInterval(() => {
|
||||
* this.remainingTime-=50;
|
||||
* if (this.remainingTime <= 0) {
|
||||
* this.remainingTime = 0;
|
||||
* clearInterval(this.interval);
|
||||
* delete this.interval;
|
||||
* }
|
||||
* }, 50);
|
||||
* }
|
||||
* return this.remainingTime;
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Invoking `{{ 10000 | countdown }}` would cause the value to be decremented by 50,
|
||||
* every 50ms, until it reaches 0.
|
||||
*
|
||||
*/
|
||||
export interface OnDestroy { ngOnDestroy(); }
|
||||
|
||||
/**
|
||||
* Implement this interface to get notified when your directive's content has been fully
|
||||
* initialized.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/plamXUpsLQbIXpViZhUO?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: 'child-cmp',
|
||||
* template: `{{where}} child`
|
||||
* })
|
||||
* class ChildComponent {
|
||||
* @Input() where: string;
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'parent-cmp',
|
||||
* template: `<ng-content></ng-content>`
|
||||
* })
|
||||
* class ParentComponent implements AfterContentInit {
|
||||
* @ContentChild(ChildComponent) contentChild: ChildComponent;
|
||||
*
|
||||
* constructor() {
|
||||
* // contentChild is not initialized yet
|
||||
* console.log(this.getMessage(this.contentChild));
|
||||
* }
|
||||
*
|
||||
* ngAfterContentInit() {
|
||||
* // contentChild is updated after the content has been checked
|
||||
* console.log('AfterContentInit: ' + this.getMessage(this.contentChild));
|
||||
* }
|
||||
*
|
||||
* private getMessage(cmp: ChildComponent): string {
|
||||
* return cmp ? cmp.where + ' child' : 'no child';
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `
|
||||
* <parent-cmp>
|
||||
* <child-cmp where="content"></child-cmp>
|
||||
* </parent-cmp>`,
|
||||
* directives: [ParentComponent, ChildComponent]
|
||||
* })
|
||||
* export class App {
|
||||
* }
|
||||
*
|
||||
* bootstrap(App).catch(err => console.error(err));
|
||||
* ```
|
||||
*/
|
||||
export interface AfterContentInit { ngAfterContentInit(); }
|
||||
|
||||
/**
|
||||
* Implement this interface to get notified after every check of your directive's content.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/tGdrytNEKQnecIPkD7NU?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({selector: 'child-cmp', template: `{{where}} child`})
|
||||
* class ChildComponent {
|
||||
* @Input() where: string;
|
||||
* }
|
||||
*
|
||||
* @Component({selector: 'parent-cmp', template: `<ng-content></ng-content>`})
|
||||
* class ParentComponent implements AfterContentChecked {
|
||||
* @ContentChild(ChildComponent) contentChild: ChildComponent;
|
||||
*
|
||||
* constructor() {
|
||||
* // contentChild is not initialized yet
|
||||
* console.log(this.getMessage(this.contentChild));
|
||||
* }
|
||||
*
|
||||
* ngAfterContentChecked() {
|
||||
* // contentChild is updated after the content has been checked
|
||||
* console.log('AfterContentChecked: ' + this.getMessage(this.contentChild));
|
||||
* }
|
||||
*
|
||||
* private getMessage(cmp: ChildComponent): string {
|
||||
* return cmp ? cmp.where + ' child' : 'no child';
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `
|
||||
* <parent-cmp>
|
||||
* <button (click)="hasContent = !hasContent">Toggle content child</button>
|
||||
* <child-cmp *ngIf="hasContent" where="content"></child-cmp>
|
||||
* </parent-cmp>`,
|
||||
* directives: [NgIf, ParentComponent, ChildComponent]
|
||||
* })
|
||||
* export class App {
|
||||
* hasContent = true;
|
||||
* }
|
||||
*
|
||||
* bootstrap(App).catch(err => console.error(err));
|
||||
* ```
|
||||
*/
|
||||
export interface AfterContentChecked { ngAfterContentChecked(); }
|
||||
|
||||
/**
|
||||
* Implement this interface to get notified when your component's view has been fully initialized.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/LhTKVMEM0fkJgyp4CI1W?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({selector: 'child-cmp', template: `{{where}} child`})
|
||||
* class ChildComponent {
|
||||
* @Input() where: string;
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'parent-cmp',
|
||||
* template: `<child-cmp where="view"></child-cmp>`,
|
||||
* directives: [ChildComponent]
|
||||
* })
|
||||
* class ParentComponent implements AfterViewInit {
|
||||
* @ViewChild(ChildComponent) viewChild: ChildComponent;
|
||||
*
|
||||
* constructor() {
|
||||
* // viewChild is not initialized yet
|
||||
* console.log(this.getMessage(this.viewChild));
|
||||
* }
|
||||
*
|
||||
* ngAfterViewInit() {
|
||||
* // viewChild is updated after the view has been initialized
|
||||
* console.log('ngAfterViewInit: ' + this.getMessage(this.viewChild));
|
||||
* }
|
||||
*
|
||||
* private getMessage(cmp: ChildComponent): string {
|
||||
* return cmp ? cmp.where + ' child' : 'no child';
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `<parent-cmp></parent-cmp>`,
|
||||
* directives: [ParentComponent]
|
||||
* })
|
||||
* export class App {
|
||||
* }
|
||||
*
|
||||
* bootstrap(App).catch(err => console.error(err));
|
||||
* ```
|
||||
*/
|
||||
export interface AfterViewInit { ngAfterViewInit(); }
|
||||
|
||||
/**
|
||||
* Implement this interface to get notified after every check of your component's view.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/0qDGHcPQkc25CXhTNzKU?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({selector: 'child-cmp', template: `{{where}} child`})
|
||||
* class ChildComponent {
|
||||
* @Input() where: string;
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'parent-cmp',
|
||||
* template: `
|
||||
* <button (click)="showView = !showView">Toggle view child</button>
|
||||
* <child-cmp *ngIf="showView" where="view"></child-cmp>`,
|
||||
* directives: [NgIf, ChildComponent]
|
||||
* })
|
||||
* class ParentComponent implements AfterViewChecked {
|
||||
* @ViewChild(ChildComponent) viewChild: ChildComponent;
|
||||
* showView = true;
|
||||
*
|
||||
* constructor() {
|
||||
* // viewChild is not initialized yet
|
||||
* console.log(this.getMessage(this.viewChild));
|
||||
* }
|
||||
*
|
||||
* ngAfterViewChecked() {
|
||||
* // viewChild is updated after the view has been checked
|
||||
* console.log('AfterViewChecked: ' + this.getMessage(this.viewChild));
|
||||
* }
|
||||
*
|
||||
* private getMessage(cmp: ChildComponent): string {
|
||||
* return cmp ? cmp.where + ' child' : 'no child';
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `<parent-cmp></parent-cmp>`,
|
||||
* directives: [ParentComponent]
|
||||
* })
|
||||
* export class App {
|
||||
* }
|
||||
*
|
||||
* bootstrap(App).catch(err => console.error(err));
|
||||
* ```
|
||||
*/
|
||||
export interface AfterViewChecked { ngAfterViewChecked(); }
|
143
modules/@angular/core/src/metadata/view.ts
Normal file
143
modules/@angular/core/src/metadata/view.ts
Normal file
@ -0,0 +1,143 @@
|
||||
import {Type} from 'angular2/src/facade/lang';
|
||||
|
||||
/**
|
||||
* Defines template and style encapsulation options available for Component's {@link View}.
|
||||
*
|
||||
* See {@link ViewMetadata#encapsulation}.
|
||||
*/
|
||||
export enum ViewEncapsulation {
|
||||
/**
|
||||
* Emulate `Native` scoping of styles by adding an attribute containing surrogate id to the Host
|
||||
* Element and pre-processing the style rules provided via
|
||||
* {@link ViewMetadata#styles} or {@link ViewMetadata#stylesUrls}, and adding the new Host Element
|
||||
* attribute to all selectors.
|
||||
*
|
||||
* This is the default option.
|
||||
*/
|
||||
Emulated,
|
||||
/**
|
||||
* Use the native encapsulation mechanism of the renderer.
|
||||
*
|
||||
* For the DOM this means using [Shadow DOM](https://w3c.github.io/webcomponents/spec/shadow/) and
|
||||
* creating a ShadowRoot for Component's Host Element.
|
||||
*/
|
||||
Native,
|
||||
/**
|
||||
* Don't provide any template or style encapsulation.
|
||||
*/
|
||||
None
|
||||
}
|
||||
|
||||
export var VIEW_ENCAPSULATION_VALUES =
|
||||
[ViewEncapsulation.Emulated, ViewEncapsulation.Native, ViewEncapsulation.None];
|
||||
|
||||
|
||||
/**
|
||||
* Metadata properties available for configuring Views.
|
||||
*
|
||||
* Each Angular component requires a single `@Component` and at least one `@View` annotation. The
|
||||
* `@View` annotation specifies the HTML template to use, and lists the directives that are active
|
||||
* within the template.
|
||||
*
|
||||
* When a component is instantiated, the template is loaded into the component's shadow root, and
|
||||
* the expressions and statements in the template are evaluated against the component.
|
||||
*
|
||||
* For details on the `@Component` annotation, see {@link ComponentMetadata}.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* @Component({
|
||||
* selector: 'greet',
|
||||
* template: 'Hello {{name}}!',
|
||||
* directives: [GreetUser, Bold]
|
||||
* })
|
||||
* class Greet {
|
||||
* name: string;
|
||||
*
|
||||
* constructor() {
|
||||
* this.name = 'World';
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* @ts2dart_const
|
||||
*/
|
||||
export class ViewMetadata {
|
||||
/**
|
||||
* Specifies a template URL for an Angular component.
|
||||
*
|
||||
* NOTE: Only one of `templateUrl` or `template` can be defined per View.
|
||||
*
|
||||
* <!-- TODO: what's the url relative to? -->
|
||||
*/
|
||||
templateUrl: string;
|
||||
|
||||
/**
|
||||
* Specifies an inline template for an Angular component.
|
||||
*
|
||||
* NOTE: Only one of `templateUrl` or `template` can be defined per View.
|
||||
*/
|
||||
template: string;
|
||||
|
||||
/**
|
||||
* Specifies stylesheet URLs for an Angular component.
|
||||
*
|
||||
* <!-- TODO: what's the url relative to? -->
|
||||
*/
|
||||
styleUrls: string[];
|
||||
|
||||
/**
|
||||
* Specifies an inline stylesheet for an Angular component.
|
||||
*/
|
||||
styles: string[];
|
||||
|
||||
/**
|
||||
* Specifies a list of directives that can be used within a template.
|
||||
*
|
||||
* Directives must be listed explicitly to provide proper component encapsulation.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```javascript
|
||||
* @Component({
|
||||
* selector: 'my-component',
|
||||
* directives: [NgFor]
|
||||
* template: '
|
||||
* <ul>
|
||||
* <li *ngFor="let item of items">{{item}}</li>
|
||||
* </ul>'
|
||||
* })
|
||||
* class MyComponent {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
directives: Array<Type | any[]>;
|
||||
|
||||
pipes: Array<Type | any[]>;
|
||||
|
||||
/**
|
||||
* Specify how the template and the styles should be encapsulated.
|
||||
* The default is {@link ViewEncapsulation#Emulated `ViewEncapsulation.Emulated`} if the view
|
||||
* has styles,
|
||||
* otherwise {@link ViewEncapsulation#None `ViewEncapsulation.None`}.
|
||||
*/
|
||||
encapsulation: ViewEncapsulation;
|
||||
|
||||
constructor({templateUrl, template, directives, pipes, encapsulation, styles, styleUrls}: {
|
||||
templateUrl?: string,
|
||||
template?: string,
|
||||
directives?: Array<Type | any[]>,
|
||||
pipes?: Array<Type | any[]>,
|
||||
encapsulation?: ViewEncapsulation,
|
||||
styles?: string[],
|
||||
styleUrls?: string[],
|
||||
} = {}) {
|
||||
this.templateUrl = templateUrl;
|
||||
this.template = template;
|
||||
this.styleUrls = styleUrls;
|
||||
this.styles = styles;
|
||||
this.directives = directives;
|
||||
this.pipes = pipes;
|
||||
this.encapsulation = encapsulation;
|
||||
}
|
||||
}
|
24
modules/@angular/core/src/platform_common_providers.ts
Normal file
24
modules/@angular/core/src/platform_common_providers.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import {Type} from 'angular2/src/facade/lang';
|
||||
import {Provider} from 'angular2/src/core/di';
|
||||
import {Console} from 'angular2/src/core/console';
|
||||
import {Reflector, reflector} from './reflection/reflection';
|
||||
import {ReflectorReader} from './reflection/reflector_reader';
|
||||
import {TestabilityRegistry} from 'angular2/src/core/testability/testability';
|
||||
import {PLATFORM_CORE_PROVIDERS} from './application_ref';
|
||||
|
||||
function _reflector(): Reflector {
|
||||
return reflector;
|
||||
}
|
||||
|
||||
var __unused: Type; // prevent missing use Dart warning.
|
||||
|
||||
/**
|
||||
* A default set of providers which should be included in any Angular platform.
|
||||
*/
|
||||
export const PLATFORM_COMMON_PROVIDERS: Array<any | Type | Provider | any[]> = /*@ts2dart_const*/[
|
||||
PLATFORM_CORE_PROVIDERS,
|
||||
/*@ts2dart_Provider*/ {provide: Reflector, useFactory: _reflector, deps: []},
|
||||
/*@ts2dart_Provider*/ {provide: ReflectorReader, useExisting: Reflector},
|
||||
TestabilityRegistry,
|
||||
Console
|
||||
];
|
53
modules/@angular/core/src/platform_directives_and_pipes.ts
Normal file
53
modules/@angular/core/src/platform_directives_and_pipes.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import {OpaqueToken} from "angular2/src/core/di";
|
||||
|
||||
/**
|
||||
* A token that can be provided when bootstraping an application to make an array of directives
|
||||
* available in every component of the application.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```typescript
|
||||
* import {PLATFORM_DIRECTIVES} from 'angular2/core';
|
||||
* import {OtherDirective} from './myDirectives';
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'my-component',
|
||||
* template: `
|
||||
* <!-- can use other directive even though the component does not list it in `directives` -->
|
||||
* <other-directive></other-directive>
|
||||
* `
|
||||
* })
|
||||
* export class MyComponent {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* bootstrap(MyComponent, [provide(PLATFORM_DIRECTIVES, {useValue: [OtherDirective], multi:true})]);
|
||||
* ```
|
||||
*/
|
||||
export const PLATFORM_DIRECTIVES: OpaqueToken =
|
||||
/*@ts2dart_const*/ new OpaqueToken("Platform Directives");
|
||||
|
||||
/**
|
||||
* A token that can be provided when bootstraping an application to make an array of pipes
|
||||
* available in every component of the application.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```typescript
|
||||
* import {PLATFORM_PIPES} from 'angular2/core';
|
||||
* import {OtherPipe} from './myPipe';
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'my-component',
|
||||
* template: `
|
||||
* {{123 | other-pipe}}
|
||||
* `
|
||||
* })
|
||||
* export class MyComponent {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* bootstrap(MyComponent, [provide(PLATFORM_PIPES, {useValue: [OtherPipe], multi:true})]);
|
||||
* ```
|
||||
*/
|
||||
export const PLATFORM_PIPES: OpaqueToken = /*@ts2dart_const*/ new OpaqueToken("Platform Pipes");
|
1
modules/@angular/core/src/prod_mode.ts
Normal file
1
modules/@angular/core/src/prod_mode.ts
Normal file
@ -0,0 +1 @@
|
||||
export {enableProdMode} from 'angular2/src/facade/lang';
|
80
modules/@angular/core/src/profile/profile.ts
Normal file
80
modules/@angular/core/src/profile/profile.ts
Normal file
@ -0,0 +1,80 @@
|
||||
export {WtfScopeFn} from './wtf_impl';
|
||||
|
||||
import * as impl from "./wtf_impl";
|
||||
|
||||
// Change exports to const once https://github.com/angular/ts2dart/issues/150
|
||||
|
||||
/**
|
||||
* True if WTF is enabled.
|
||||
*/
|
||||
export var wtfEnabled = impl.detectWTF();
|
||||
|
||||
function noopScope(arg0?: any, arg1?: any): any {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create trace scope.
|
||||
*
|
||||
* Scopes must be strictly nested and are analogous to stack frames, but
|
||||
* do not have to follow the stack frames. Instead it is recommended that they follow logical
|
||||
* nesting. You may want to use
|
||||
* [Event
|
||||
* Signatures](http://google.github.io/tracing-framework/instrumenting-code.html#custom-events)
|
||||
* as they are defined in WTF.
|
||||
*
|
||||
* Used to mark scope entry. The return value is used to leave the scope.
|
||||
*
|
||||
* var myScope = wtfCreateScope('MyClass#myMethod(ascii someVal)');
|
||||
*
|
||||
* someMethod() {
|
||||
* var s = myScope('Foo'); // 'Foo' gets stored in tracing UI
|
||||
* // DO SOME WORK HERE
|
||||
* return wtfLeave(s, 123); // Return value 123
|
||||
* }
|
||||
*
|
||||
* Note, adding try-finally block around the work to ensure that `wtfLeave` gets called can
|
||||
* negatively impact the performance of your application. For this reason we recommend that
|
||||
* you don't add them to ensure that `wtfLeave` gets called. In production `wtfLeave` is a noop and
|
||||
* so try-finally block has no value. When debugging perf issues, skipping `wtfLeave`, do to
|
||||
* exception, will produce incorrect trace, but presence of exception signifies logic error which
|
||||
* needs to be fixed before the app should be profiled. Add try-finally only when you expect that
|
||||
* an exception is expected during normal execution while profiling.
|
||||
*
|
||||
*/
|
||||
export var wtfCreateScope: (signature: string, flags?: any) => impl.WtfScopeFn =
|
||||
wtfEnabled ? impl.createScope : (signature: string, flags?: any) => noopScope;
|
||||
|
||||
/**
|
||||
* Used to mark end of Scope.
|
||||
*
|
||||
* - `scope` to end.
|
||||
* - `returnValue` (optional) to be passed to the WTF.
|
||||
*
|
||||
* Returns the `returnValue for easy chaining.
|
||||
*/
|
||||
export var wtfLeave:<T>(scope: any, returnValue?: T) => T =
|
||||
wtfEnabled ? impl.leave : (s: any, r?: any) => r;
|
||||
|
||||
/**
|
||||
* Used to mark Async start. Async are similar to scope but they don't have to be strictly nested.
|
||||
* The return value is used in the call to [endAsync]. Async ranges only work if WTF has been
|
||||
* enabled.
|
||||
*
|
||||
* someMethod() {
|
||||
* var s = wtfStartTimeRange('HTTP:GET', 'some.url');
|
||||
* var future = new Future.delay(5).then((_) {
|
||||
* wtfEndTimeRange(s);
|
||||
* });
|
||||
* }
|
||||
*/
|
||||
export var wtfStartTimeRange: (rangeType: string, action: string) => any =
|
||||
wtfEnabled ? impl.startTimeRange : (rangeType: string, action: string) => null;
|
||||
|
||||
/**
|
||||
* Ends a async time range operation.
|
||||
* [range] is the return value from [wtfStartTimeRange] Async ranges only work if WTF has been
|
||||
* enabled.
|
||||
*/
|
||||
export var wtfEndTimeRange: (range: any) => void = wtfEnabled ? impl.endTimeRange : (r: any) =>
|
||||
null;
|
96
modules/@angular/core/src/profile/wtf_impl.dart
Normal file
96
modules/@angular/core/src/profile/wtf_impl.dart
Normal file
@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Tracing for Dart applications.
|
||||
*
|
||||
* The tracing API hooks up to either [WTF](http://google.github.io/tracing-framework/) or
|
||||
* [Dart Observatory](https://www.dartlang.org/tools/observatory/).
|
||||
*/
|
||||
library angular2.src.core.wtf_impl;
|
||||
|
||||
typedef dynamic WtfScopeFn([arg0, arg1]);
|
||||
|
||||
var context = null;
|
||||
var _trace;
|
||||
var _events;
|
||||
var _createScope;
|
||||
var _leaveScope;
|
||||
var _beginTimeRange;
|
||||
var _endTimeRange;
|
||||
final List _arg1 = [null];
|
||||
final List _arg2 = [null, null];
|
||||
|
||||
bool detectWTF() {
|
||||
if (context != null && context.hasProperty('wtf')) {
|
||||
var wtf = context['wtf'];
|
||||
if (wtf.hasProperty('trace')) {
|
||||
_trace = wtf['trace'];
|
||||
_events = _trace['events'];
|
||||
_createScope = _events['createScope'];
|
||||
_leaveScope = _trace['leaveScope'];
|
||||
_beginTimeRange = _trace['beginTimeRange'];
|
||||
_endTimeRange = _trace['endTimeRange'];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int getArgSize(String signature) {
|
||||
int start = signature.indexOf('(') + 1;
|
||||
int end = signature.indexOf(')', start);
|
||||
bool found = false;
|
||||
int count = 0;
|
||||
for (var i = start; i < end; i++) {
|
||||
var ch = signature[i];
|
||||
if (identical(ch, ',')) {
|
||||
found = false;
|
||||
}
|
||||
if (!found) {
|
||||
found = true;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
dynamic createScope(String signature, [flags]) {
|
||||
_arg2[0] = signature;
|
||||
_arg2[1] = flags;
|
||||
var jsScope = _createScope.apply(_arg2, thisArg: _events);
|
||||
switch (getArgSize(signature)) {
|
||||
case 0:
|
||||
return ([arg0, arg1]) {
|
||||
return jsScope.apply(const []);
|
||||
};
|
||||
case 1:
|
||||
return ([arg0, arg1]) {
|
||||
_arg1[0] = arg0;
|
||||
return jsScope.apply(_arg1);
|
||||
};
|
||||
case 2:
|
||||
return ([arg0, arg1]) {
|
||||
_arg2[0] = arg0;
|
||||
_arg2[1] = arg1;
|
||||
return jsScope.apply(_arg2);
|
||||
};
|
||||
default:
|
||||
throw "Max 2 arguments are supported.";
|
||||
}
|
||||
}
|
||||
|
||||
void leave(scope, [returnValue]) {
|
||||
_arg2[0] = scope;
|
||||
_arg2[1] = returnValue;
|
||||
_leaveScope.apply(_arg2, thisArg: _trace);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
dynamic startTimeRange(String rangeType, String action) {
|
||||
_arg2[0] = rangeType;
|
||||
_arg2[1] = action;
|
||||
return _beginTimeRange.apply(_arg2, thisArg: _trace);
|
||||
}
|
||||
|
||||
void endTimeRange(dynamic range) {
|
||||
_arg1[0] = range;
|
||||
_endTimeRange.apply(_arg1, thisArg: _trace);
|
||||
}
|
57
modules/@angular/core/src/profile/wtf_impl.ts
Normal file
57
modules/@angular/core/src/profile/wtf_impl.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import {global} from 'angular2/src/facade/lang';
|
||||
|
||||
/**
|
||||
* A scope function for the Web Tracing Framework (WTF).
|
||||
*/
|
||||
export interface WtfScopeFn { (arg0?: any, arg1?: any): any; }
|
||||
|
||||
interface WTF {
|
||||
trace: Trace;
|
||||
}
|
||||
|
||||
interface Trace {
|
||||
events: Events;
|
||||
leaveScope(scope: Scope, returnValue: any);
|
||||
beginTimeRange(rangeType: string, action: string): Range;
|
||||
endTimeRange(range: Range);
|
||||
}
|
||||
|
||||
export interface Range {}
|
||||
|
||||
interface Events {
|
||||
createScope(signature: string, flags: any): Scope;
|
||||
}
|
||||
|
||||
export interface Scope { (...args): any; }
|
||||
|
||||
var trace: Trace;
|
||||
var events: Events;
|
||||
|
||||
export function detectWTF(): boolean {
|
||||
var wtf: WTF = global['wtf'];
|
||||
if (wtf) {
|
||||
trace = wtf['trace'];
|
||||
if (trace) {
|
||||
events = trace['events'];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function createScope(signature: string, flags: any = null): any {
|
||||
return events.createScope(signature, flags);
|
||||
}
|
||||
|
||||
export function leave<T>(scope: Scope, returnValue?: T): T {
|
||||
trace.leaveScope(scope, returnValue);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
export function startTimeRange(rangeType: string, action: string): Range {
|
||||
return trace.beginTimeRange(rangeType, action);
|
||||
}
|
||||
|
||||
export function endTimeRange(range: Range): void {
|
||||
trace.endTimeRange(range);
|
||||
}
|
14
modules/@angular/core/src/profile/wtf_init.dart
Normal file
14
modules/@angular/core/src/profile/wtf_init.dart
Normal file
@ -0,0 +1,14 @@
|
||||
library angular2.src.core.wtf_init;
|
||||
|
||||
import 'dart:js' as js;
|
||||
import 'wtf_impl.dart' as impl;
|
||||
|
||||
/**
|
||||
* Must be executed explicitly in Dart to set the JS Context.
|
||||
*
|
||||
* NOTE: this is done explicitly to allow WTF api not to depend on
|
||||
* JS context and possible to run the noop WTF stubs outside the browser.
|
||||
*/
|
||||
wtfInit() {
|
||||
impl.context = js.context;
|
||||
}
|
4
modules/@angular/core/src/profile/wtf_init.ts
Normal file
4
modules/@angular/core/src/profile/wtf_init.ts
Normal file
@ -0,0 +1,4 @@
|
||||
/**
|
||||
* This is here because DART requires it. It is noop in JS.
|
||||
*/
|
||||
export function wtfInit() {}
|
@ -0,0 +1,63 @@
|
||||
library reflection.debug_reflection_capabilities;
|
||||
|
||||
import 'dart:mirrors';
|
||||
import 'package:logging/logging.dart' as log;
|
||||
import 'package:stack_trace/stack_trace.dart';
|
||||
import 'types.dart';
|
||||
import 'reflection_capabilities.dart' as standard;
|
||||
|
||||
class ReflectionCapabilities extends standard.ReflectionCapabilities {
|
||||
final bool _verbose;
|
||||
final log.Logger _log = new log.Logger('ReflectionCapabilities');
|
||||
|
||||
ReflectionCapabilities({bool verbose: false})
|
||||
: _verbose = verbose,
|
||||
super() {
|
||||
log.hierarchicalLoggingEnabled = true;
|
||||
_log.level = _verbose ? log.Level.ALL : log.Level.INFO;
|
||||
_log.onRecord.listen((log.LogRecord rec) {
|
||||
print('[${rec.loggerName}(${rec.level.name})]: ${rec.message}');
|
||||
});
|
||||
}
|
||||
|
||||
void _notify(String methodName, param) {
|
||||
var trace = _verbose ? ' ${Trace.format(new Trace.current())}' : '';
|
||||
_log.info('"$methodName" requested for "$param".$trace');
|
||||
}
|
||||
|
||||
Function factory(Type type) {
|
||||
ClassMirror classMirror = reflectType(type);
|
||||
_notify('factory', '${classMirror.qualifiedName}');
|
||||
return super.factory(type);
|
||||
}
|
||||
|
||||
List<List> parameters(typeOrFunc) {
|
||||
_notify('parameters', typeOrFunc);
|
||||
return super.parameters(typeOrFunc);
|
||||
}
|
||||
|
||||
List annotations(typeOrFunc) {
|
||||
_notify('annotations', typeOrFunc);
|
||||
return super.annotations(typeOrFunc);
|
||||
}
|
||||
|
||||
Map propMetadata(typeOrFunc) {
|
||||
_notify('propMetadata', typeOrFunc);
|
||||
return super.propMetadata(typeOrFunc);
|
||||
}
|
||||
|
||||
GetterFn getter(String name) {
|
||||
_notify('getter', name);
|
||||
return super.getter(name);
|
||||
}
|
||||
|
||||
SetterFn setter(String name) {
|
||||
_notify('setter', name);
|
||||
return super.setter(name);
|
||||
}
|
||||
|
||||
MethodFn method(String name) {
|
||||
_notify('method', name);
|
||||
return super.method(name);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import {Type} from 'angular2/src/facade/lang';
|
||||
import {GetterFn, SetterFn, MethodFn} from './types';
|
||||
|
||||
export interface PlatformReflectionCapabilities {
|
||||
isReflectionEnabled(): boolean;
|
||||
factory(type: Type): Function;
|
||||
interfaces(type: Type): any[];
|
||||
parameters(type: any): any[][];
|
||||
annotations(type: any): any[];
|
||||
propMetadata(typeOrFunc: any): {[key: string]: any[]};
|
||||
getter(name: string): GetterFn;
|
||||
setter(name: string): SetterFn;
|
||||
method(name: string): MethodFn;
|
||||
importUri(type: Type): string;
|
||||
}
|
59
modules/@angular/core/src/reflection/reflection.dart
Normal file
59
modules/@angular/core/src/reflection/reflection.dart
Normal file
@ -0,0 +1,59 @@
|
||||
library reflection.reflection;
|
||||
|
||||
import 'reflector.dart';
|
||||
import 'types.dart';
|
||||
export 'reflector.dart';
|
||||
import 'platform_reflection_capabilities.dart';
|
||||
import 'package:angular2/src/facade/lang.dart';
|
||||
|
||||
class NoReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||
@override
|
||||
bool isReflectionEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
Function factory(Type type) {
|
||||
throw "Cannot find reflection information on ${stringify(type)}";
|
||||
}
|
||||
|
||||
@override
|
||||
List interfaces(Type type) {
|
||||
throw "Cannot find reflection information on ${stringify(type)}";
|
||||
}
|
||||
|
||||
@override
|
||||
List<List> parameters(dynamic type) {
|
||||
throw "Cannot find reflection information on ${stringify(type)}";
|
||||
}
|
||||
|
||||
@override
|
||||
List annotations(dynamic type) {
|
||||
throw "Cannot find reflection information on ${stringify(type)}";
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, List> propMetadata(dynamic type) {
|
||||
throw "Cannot find reflection information on ${stringify(type)}";
|
||||
}
|
||||
|
||||
@override
|
||||
GetterFn getter(String name) {
|
||||
throw "Cannot find getter ${name}";
|
||||
}
|
||||
|
||||
@override
|
||||
SetterFn setter(String name) {
|
||||
throw "Cannot find setter ${name}";
|
||||
}
|
||||
|
||||
@override
|
||||
MethodFn method(String name) {
|
||||
throw "Cannot find method ${name}";
|
||||
}
|
||||
|
||||
@override
|
||||
String importUri(Type type) => './';
|
||||
}
|
||||
|
||||
final Reflector reflector = new Reflector(new NoReflectionCapabilities());
|
11
modules/@angular/core/src/reflection/reflection.ts
Normal file
11
modules/@angular/core/src/reflection/reflection.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import {Type, isPresent} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {Reflector} from './reflector';
|
||||
export {Reflector, ReflectionInfo} from './reflector';
|
||||
import {ReflectionCapabilities} from './reflection_capabilities';
|
||||
|
||||
/**
|
||||
* The {@link Reflector} used internally in Angular to access metadata
|
||||
* about symbols.
|
||||
*/
|
||||
export var reflector = new Reflector(new ReflectionCapabilities());
|
@ -0,0 +1,448 @@
|
||||
library reflection.reflection_capabilities;
|
||||
|
||||
import 'dart:mirrors';
|
||||
|
||||
import 'package:angular2/src/core/metadata/lifecycle_hooks.dart';
|
||||
import 'package:angular2/src/facade/lang.dart';
|
||||
|
||||
import 'platform_reflection_capabilities.dart';
|
||||
import 'types.dart';
|
||||
|
||||
import '../linker/template_ref.dart';
|
||||
|
||||
var DOT_REGEX = new RegExp('\\.');
|
||||
|
||||
class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||
Map<Symbol, Type> parameterizedTypeMapping = new Map<Symbol, Type>();
|
||||
|
||||
ReflectionCapabilities([metadataReader]) {
|
||||
// In Dart, there is no way of getting from a parameterized Type to
|
||||
// the underlying non parameterized type.
|
||||
// So we need to have a separate Map for the types that are generic
|
||||
// and used in our DI...
|
||||
parameterizedTypeMapping[reflectType(TemplateRef).qualifiedName] = TemplateRef;
|
||||
}
|
||||
|
||||
_typeFromMirror(TypeMirror typeMirror) {
|
||||
var result = parameterizedTypeMapping[typeMirror.qualifiedName];
|
||||
if (result == null && typeMirror.hasReflectedType && typeMirror.reflectedType != dynamic) {
|
||||
result = typeMirror.reflectedType;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool isReflectionEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
Function factory(Type type) {
|
||||
ClassMirror classMirror = reflectType(type);
|
||||
MethodMirror ctor = classMirror.declarations[classMirror.simpleName];
|
||||
Function create = classMirror.newInstance;
|
||||
Symbol name = ctor.constructorName;
|
||||
int length = ctor.parameters.length;
|
||||
|
||||
switch (length) {
|
||||
case 0:
|
||||
return () => create(name, []).reflectee;
|
||||
case 1:
|
||||
return (a1) => create(name, [a1]).reflectee;
|
||||
case 2:
|
||||
return (a1, a2) => create(name, [a1, a2]).reflectee;
|
||||
case 3:
|
||||
return (a1, a2, a3) => create(name, [a1, a2, a3]).reflectee;
|
||||
case 4:
|
||||
return (a1, a2, a3, a4) => create(name, [a1, a2, a3, a4]).reflectee;
|
||||
case 5:
|
||||
return (a1, a2, a3, a4, a5) =>
|
||||
create(name, [a1, a2, a3, a4, a5]).reflectee;
|
||||
case 6:
|
||||
return (a1, a2, a3, a4, a5, a6) =>
|
||||
create(name, [a1, a2, a3, a4, a5, a6]).reflectee;
|
||||
case 7:
|
||||
return (a1, a2, a3, a4, a5, a6, a7) =>
|
||||
create(name, [a1, a2, a3, a4, a5, a6, a7]).reflectee;
|
||||
case 8:
|
||||
return (a1, a2, a3, a4, a5, a6, a7, a8) =>
|
||||
create(name, [a1, a2, a3, a4, a5, a6, a7, a8]).reflectee;
|
||||
case 9:
|
||||
return (a1, a2, a3, a4, a5, a6, a7, a8, a9) =>
|
||||
create(name, [a1, a2, a3, a4, a5, a6, a7, a8, a9]).reflectee;
|
||||
case 10:
|
||||
return (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) =>
|
||||
create(name, [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10]).reflectee;
|
||||
case 11:
|
||||
return (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) =>
|
||||
create(name, [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11])
|
||||
.reflectee;
|
||||
case 12:
|
||||
return (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) =>
|
||||
create(name, [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12])
|
||||
.reflectee;
|
||||
case 13:
|
||||
return (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) =>
|
||||
create(name, [
|
||||
a1,
|
||||
a2,
|
||||
a3,
|
||||
a4,
|
||||
a5,
|
||||
a6,
|
||||
a7,
|
||||
a8,
|
||||
a9,
|
||||
a10,
|
||||
a11,
|
||||
a12,
|
||||
a13
|
||||
]).reflectee;
|
||||
case 14:
|
||||
return (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) =>
|
||||
create(name, [
|
||||
a1,
|
||||
a2,
|
||||
a3,
|
||||
a4,
|
||||
a5,
|
||||
a6,
|
||||
a7,
|
||||
a8,
|
||||
a9,
|
||||
a10,
|
||||
a11,
|
||||
a12,
|
||||
a13,
|
||||
a14
|
||||
]).reflectee;
|
||||
case 15:
|
||||
return (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14,
|
||||
a15) =>
|
||||
create(name, [
|
||||
a1,
|
||||
a2,
|
||||
a3,
|
||||
a4,
|
||||
a5,
|
||||
a6,
|
||||
a7,
|
||||
a8,
|
||||
a9,
|
||||
a10,
|
||||
a11,
|
||||
a12,
|
||||
a13,
|
||||
a14,
|
||||
a15
|
||||
]).reflectee;
|
||||
case 16:
|
||||
return (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14,
|
||||
a15, a16) =>
|
||||
create(name, [
|
||||
a1,
|
||||
a2,
|
||||
a3,
|
||||
a4,
|
||||
a5,
|
||||
a6,
|
||||
a7,
|
||||
a8,
|
||||
a9,
|
||||
a10,
|
||||
a11,
|
||||
a12,
|
||||
a13,
|
||||
a14,
|
||||
a15,
|
||||
a16
|
||||
]).reflectee;
|
||||
case 17:
|
||||
return (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14,
|
||||
a15, a16, a17) =>
|
||||
create(name, [
|
||||
a1,
|
||||
a2,
|
||||
a3,
|
||||
a4,
|
||||
a5,
|
||||
a6,
|
||||
a7,
|
||||
a8,
|
||||
a9,
|
||||
a10,
|
||||
a11,
|
||||
a12,
|
||||
a13,
|
||||
a14,
|
||||
a15,
|
||||
a16,
|
||||
a17
|
||||
]).reflectee;
|
||||
case 18:
|
||||
return (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14,
|
||||
a15, a16, a17, a18) =>
|
||||
create(name, [
|
||||
a1,
|
||||
a2,
|
||||
a3,
|
||||
a4,
|
||||
a5,
|
||||
a6,
|
||||
a7,
|
||||
a8,
|
||||
a9,
|
||||
a10,
|
||||
a11,
|
||||
a12,
|
||||
a13,
|
||||
a14,
|
||||
a15,
|
||||
a16,
|
||||
a17,
|
||||
a18
|
||||
]).reflectee;
|
||||
case 19:
|
||||
return (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14,
|
||||
a15, a16, a17, a18, a19) =>
|
||||
create(name, [
|
||||
a1,
|
||||
a2,
|
||||
a3,
|
||||
a4,
|
||||
a5,
|
||||
a6,
|
||||
a7,
|
||||
a8,
|
||||
a9,
|
||||
a10,
|
||||
a11,
|
||||
a12,
|
||||
a13,
|
||||
a14,
|
||||
a15,
|
||||
a16,
|
||||
a17,
|
||||
a18,
|
||||
a19
|
||||
]).reflectee;
|
||||
case 20:
|
||||
return (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14,
|
||||
a15, a16, a17, a18, a19, a20) =>
|
||||
create(name, [
|
||||
a1,
|
||||
a2,
|
||||
a3,
|
||||
a4,
|
||||
a5,
|
||||
a6,
|
||||
a7,
|
||||
a8,
|
||||
a9,
|
||||
a10,
|
||||
a11,
|
||||
a12,
|
||||
a13,
|
||||
a14,
|
||||
a15,
|
||||
a16,
|
||||
a17,
|
||||
a18,
|
||||
a19,
|
||||
a20
|
||||
]).reflectee;
|
||||
}
|
||||
|
||||
throw "Cannot create a factory for '${stringify(type)}' because its constructor has more than 20 arguments";
|
||||
}
|
||||
|
||||
List<List> parameters(typeOrFunc) {
|
||||
final parameters = typeOrFunc is Type
|
||||
? _constructorParameters(typeOrFunc)
|
||||
: _functionParameters(typeOrFunc);
|
||||
return parameters.map(_convertParameter).toList();
|
||||
}
|
||||
|
||||
List _convertParameter(ParameterMirror p) {
|
||||
var t = p.type;
|
||||
var type = _typeFromMirror(t);
|
||||
var res = type != null ? [type] : [];
|
||||
res.addAll(p.metadata.map((m) => m.reflectee));
|
||||
return res;
|
||||
}
|
||||
|
||||
List annotations(typeOrFunc) {
|
||||
final meta = typeOrFunc is Type
|
||||
? _constructorMetadata(typeOrFunc)
|
||||
: _functionMetadata(typeOrFunc);
|
||||
|
||||
return meta.map((m) => m.reflectee).toList();
|
||||
}
|
||||
|
||||
Map propMetadata(typeOrFunc) {
|
||||
final res = {};
|
||||
reflectClass(typeOrFunc).declarations.forEach((k, v) {
|
||||
var name = _normalizeName(MirrorSystem.getName(k));
|
||||
if (res[name] == null) res[name] = [];
|
||||
res[name].addAll(v.metadata.map((fm) => fm.reflectee));
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
String _normalizeName(String name) {
|
||||
return name.endsWith("=") ? name.substring(0, name.length - 1) : name;
|
||||
}
|
||||
|
||||
List interfaces(type) {
|
||||
final clazz = reflectType(type);
|
||||
_assertDeclaresLifecycleHooks(clazz);
|
||||
return _interfacesFromMirror(clazz);
|
||||
}
|
||||
|
||||
List _interfacesFromMirror(classMirror) {
|
||||
return classMirror.superinterfaces.map((si) => si.reflectedType).toList()
|
||||
..addAll(classMirror.superclass == null
|
||||
? []
|
||||
: _interfacesFromMirror(classMirror.superclass));
|
||||
}
|
||||
|
||||
GetterFn getter(String name) {
|
||||
var symbol = new Symbol(name);
|
||||
return (receiver) => reflect(receiver).getField(symbol).reflectee;
|
||||
}
|
||||
|
||||
SetterFn setter(String name) {
|
||||
var symbol = new Symbol(name);
|
||||
return (receiver, value) =>
|
||||
reflect(receiver).setField(symbol, value).reflectee;
|
||||
}
|
||||
|
||||
MethodFn method(String name) {
|
||||
var symbol = new Symbol(name);
|
||||
return (receiver, posArgs) =>
|
||||
reflect(receiver).invoke(symbol, posArgs).reflectee;
|
||||
}
|
||||
|
||||
List _functionParameters(Function func) {
|
||||
var closureMirror = reflect(func);
|
||||
return closureMirror.function.parameters;
|
||||
}
|
||||
|
||||
List _constructorParameters(Type type) {
|
||||
ClassMirror classMirror = reflectType(type);
|
||||
MethodMirror ctor = classMirror.declarations[classMirror.simpleName];
|
||||
return ctor.parameters;
|
||||
}
|
||||
|
||||
List _functionMetadata(Function func) {
|
||||
var closureMirror = reflect(func);
|
||||
return closureMirror.function.metadata;
|
||||
}
|
||||
|
||||
List _constructorMetadata(Type type) {
|
||||
ClassMirror classMirror = reflectType(type);
|
||||
return classMirror.metadata;
|
||||
}
|
||||
|
||||
String importUri(Type type) {
|
||||
return '${(reflectClass(type).owner as LibraryMirror).uri}';
|
||||
}
|
||||
}
|
||||
|
||||
final _lifecycleHookMirrors = <ClassMirror>[
|
||||
reflectType(AfterContentChecked),
|
||||
reflectType(AfterContentInit),
|
||||
reflectType(AfterViewChecked),
|
||||
reflectType(AfterViewInit),
|
||||
reflectType(DoCheck),
|
||||
reflectType(OnChanges),
|
||||
reflectType(OnDestroy),
|
||||
reflectType(OnInit),
|
||||
];
|
||||
|
||||
/// Checks whether [clazz] implements lifecycle ifaces without declaring them.
|
||||
///
|
||||
/// Due to Dart implementation details, lifecycle hooks are only called when a
|
||||
/// class explicitly declares that it implements the associated interface.
|
||||
/// See https://goo.gl/b07Kii for details.
|
||||
void _assertDeclaresLifecycleHooks(ClassMirror clazz) {
|
||||
final missingDeclarations = <ClassMirror>[];
|
||||
for (var iface in _lifecycleHookMirrors) {
|
||||
if (!_checkDeclares(clazz, iface: iface) &&
|
||||
_checkImplements(clazz, iface: iface)) {
|
||||
missingDeclarations.add(iface);
|
||||
}
|
||||
}
|
||||
if (missingDeclarations.isNotEmpty) {
|
||||
throw new MissingInterfaceError(clazz, missingDeclarations);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether [clazz] declares that it implements [iface].
|
||||
///
|
||||
/// Returns `false` if [clazz] implements [iface] but does not declare it.
|
||||
/// Returns `false` if [clazz]'s superclass declares that it
|
||||
/// implements [iface].
|
||||
bool _checkDeclares(ClassMirror clazz, {ClassMirror iface: null}) {
|
||||
if (iface == null) {
|
||||
throw new ArgumentError.notNull('iface');
|
||||
}
|
||||
return clazz.superinterfaces.contains(iface);
|
||||
}
|
||||
|
||||
/// Returns whether [clazz] implements [iface].
|
||||
///
|
||||
/// Returns `true` if [clazz] implements [iface] and does not declare it.
|
||||
/// Returns `true` if [clazz]'s superclass implements [iface].
|
||||
///
|
||||
/// This is an approximation of a JavaScript feature check:
|
||||
/// ```js
|
||||
/// var matches = true;
|
||||
/// for (var prop in iface) {
|
||||
/// if (iface.hasOwnProperty(prop)) {
|
||||
/// matches = matches && clazz.hasOwnProperty(prop);
|
||||
/// }
|
||||
/// }
|
||||
/// return matches;
|
||||
/// ```
|
||||
bool _checkImplements(ClassMirror clazz, {ClassMirror iface: null}) {
|
||||
if (iface == null) {
|
||||
throw new ArgumentError.notNull('iface');
|
||||
}
|
||||
|
||||
var matches = true;
|
||||
iface.declarations.forEach((symbol, declarationMirror) {
|
||||
if (!matches) return;
|
||||
if (declarationMirror.isConstructor || declarationMirror.isPrivate) return;
|
||||
matches = clazz.declarations.keys.contains(symbol);
|
||||
});
|
||||
if (!matches && clazz.superclass != null) {
|
||||
matches = _checkImplements(clazz.superclass, iface: iface);
|
||||
}
|
||||
if (!matches && clazz.mixin != clazz) {
|
||||
matches = _checkImplements(clazz.mixin, iface: iface);
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
/// Error thrown when a class implements a lifecycle iface it does not declare.
|
||||
class MissingInterfaceError extends Error {
|
||||
final ClassMirror clazz;
|
||||
final List<ClassMirror> missingDeclarations;
|
||||
|
||||
MissingInterfaceError(this.clazz, this.missingDeclarations);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
final buf = new StringBuffer();
|
||||
buf.write('${clazz.simpleName} implements ');
|
||||
if (missingDeclarations.length == 1) {
|
||||
buf.write('an interface but does not declare it: ');
|
||||
} else {
|
||||
buf.write('interfaces but does not declare them: ');
|
||||
}
|
||||
buf.write(
|
||||
missingDeclarations.map((d) => d.simpleName.toString()).join(', '));
|
||||
buf.write('. See https://goo.gl/b07Kii for more info.');
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
194
modules/@angular/core/src/reflection/reflection_capabilities.ts
Normal file
194
modules/@angular/core/src/reflection/reflection_capabilities.ts
Normal file
@ -0,0 +1,194 @@
|
||||
import {
|
||||
Type,
|
||||
isPresent,
|
||||
isFunction,
|
||||
global,
|
||||
stringify,
|
||||
ConcreteType
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {GetterFn, SetterFn, MethodFn} from './types';
|
||||
import {PlatformReflectionCapabilities} from './platform_reflection_capabilities';
|
||||
|
||||
export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||
private _reflect: any;
|
||||
|
||||
constructor(reflect?: any) { this._reflect = isPresent(reflect) ? reflect : global.Reflect; }
|
||||
|
||||
isReflectionEnabled(): boolean { return true; }
|
||||
|
||||
factory(t: ConcreteType): Function {
|
||||
switch (t.length) {
|
||||
case 0:
|
||||
return () => new t();
|
||||
case 1:
|
||||
return (a1: any) => new t(a1);
|
||||
case 2:
|
||||
return (a1: any, a2: any) => new t(a1, a2);
|
||||
case 3:
|
||||
return (a1: any, a2: any, a3: any) => new t(a1, a2, a3);
|
||||
case 4:
|
||||
return (a1: any, a2: any, a3: any, a4: any) => new t(a1, a2, a3, a4);
|
||||
case 5:
|
||||
return (a1: any, a2: any, a3: any, a4: any, a5: any) => new t(a1, a2, a3, a4, a5);
|
||||
case 6:
|
||||
return (a1: any, a2: any, a3: any, a4: any, a5: any, a6: any) =>
|
||||
new t(a1, a2, a3, a4, a5, a6);
|
||||
case 7:
|
||||
return (a1: any, a2: any, a3: any, a4: any, a5: any, a6: any, a7: any) =>
|
||||
new t(a1, a2, a3, a4, a5, a6, a7);
|
||||
case 8:
|
||||
return (a1: any, a2: any, a3: any, a4: any, a5: any, a6: any, a7: any, a8: any) =>
|
||||
new t(a1, a2, a3, a4, a5, a6, a7, a8);
|
||||
case 9:
|
||||
return (a1: any, a2: any, a3: any, a4: any, a5: any, a6: any, a7: any, a8: any, a9: any) =>
|
||||
new t(a1, a2, a3, a4, a5, a6, a7, a8, a9);
|
||||
case 10:
|
||||
return (a1: any, a2: any, a3: any, a4: any, a5: any, a6: any, a7: any, a8: any, a9: any,
|
||||
a10: any) => new t(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
|
||||
case 11:
|
||||
return (a1: any, a2: any, a3: any, a4: any, a5: any, a6: any, a7: any, a8: any, a9: any,
|
||||
a10: any, a11: any) => new t(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
|
||||
case 12:
|
||||
return (a1: any, a2: any, a3: any, a4: any, a5: any, a6: any, a7: any, a8: any, a9: any,
|
||||
a10: any, a11: any, a12: any) =>
|
||||
new t(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12);
|
||||
case 13:
|
||||
return (a1: any, a2: any, a3: any, a4: any, a5: any, a6: any, a7: any, a8: any, a9: any,
|
||||
a10: any, a11: any, a12: any, a13: any) =>
|
||||
new t(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13);
|
||||
case 14:
|
||||
return (a1: any, a2: any, a3: any, a4: any, a5: any, a6: any, a7: any, a8: any, a9: any,
|
||||
a10: any, a11: any, a12: any, a13: any, a14: any) =>
|
||||
new t(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14);
|
||||
case 15:
|
||||
return (a1: any, a2: any, a3: any, a4: any, a5: any, a6: any, a7: any, a8: any, a9: any,
|
||||
a10: any, a11: any, a12: any, a13: any, a14: any, a15: any) =>
|
||||
new t(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15);
|
||||
case 16:
|
||||
return (a1: any, a2: any, a3: any, a4: any, a5: any, a6: any, a7: any, a8: any, a9: any,
|
||||
a10: any, a11: any, a12: any, a13: any, a14: any, a15: any, a16: any) =>
|
||||
new t(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16);
|
||||
case 17:
|
||||
return (a1: any, a2: any, a3: any, a4: any, a5: any, a6: any, a7: any, a8: any, a9: any,
|
||||
a10: any, a11: any, a12: any, a13: any, a14: any, a15: any, a16: any, a17: any) =>
|
||||
new t(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16,
|
||||
a17);
|
||||
case 18:
|
||||
return (a1: any, a2: any, a3: any, a4: any, a5: any, a6: any, a7: any, a8: any, a9: any,
|
||||
a10: any, a11: any, a12: any, a13: any, a14: any, a15: any, a16: any, a17: any,
|
||||
a18: any) => new t(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,
|
||||
a16, a17, a18);
|
||||
case 19:
|
||||
return (a1: any, a2: any, a3: any, a4: any, a5: any, a6: any, a7: any, a8: any, a9: any,
|
||||
a10: any, a11: any, a12: any, a13: any, a14: any, a15: any, a16: any, a17: any,
|
||||
a18: any, a19: any) => new t(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13,
|
||||
a14, a15, a16, a17, a18, a19);
|
||||
case 20:
|
||||
return (a1: any, a2: any, a3: any, a4: any, a5: any, a6: any, a7: any, a8: any, a9: any,
|
||||
a10: any, a11: any, a12: any, a13: any, a14: any, a15: any, a16: any, a17: any,
|
||||
a18: any, a19: any, a20: any) => new t(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11,
|
||||
a12, a13, a14, a15, a16, a17, a18, a19, a20);
|
||||
};
|
||||
|
||||
throw new Error(
|
||||
`Cannot create a factory for '${stringify(t)}' because its constructor has more than 20 arguments`);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_zipTypesAndAnnotations(paramTypes, paramAnnotations): any[][] {
|
||||
var result;
|
||||
|
||||
if (typeof paramTypes === 'undefined') {
|
||||
result = new Array(paramAnnotations.length);
|
||||
} else {
|
||||
result = new Array(paramTypes.length);
|
||||
}
|
||||
|
||||
for (var i = 0; i < result.length; i++) {
|
||||
// TS outputs Object for parameters without types, while Traceur omits
|
||||
// the annotations. For now we preserve the Traceur behavior to aid
|
||||
// migration, but this can be revisited.
|
||||
if (typeof paramTypes === 'undefined') {
|
||||
result[i] = [];
|
||||
} else if (paramTypes[i] != Object) {
|
||||
result[i] = [paramTypes[i]];
|
||||
} else {
|
||||
result[i] = [];
|
||||
}
|
||||
if (isPresent(paramAnnotations) && isPresent(paramAnnotations[i])) {
|
||||
result[i] = result[i].concat(paramAnnotations[i]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
parameters(typeOrFunc: Type): any[][] {
|
||||
// Prefer the direct API.
|
||||
if (isPresent((<any>typeOrFunc).parameters)) {
|
||||
return (<any>typeOrFunc).parameters;
|
||||
}
|
||||
if (isPresent(this._reflect) && isPresent(this._reflect.getMetadata)) {
|
||||
var paramAnnotations = this._reflect.getMetadata('parameters', typeOrFunc);
|
||||
var paramTypes = this._reflect.getMetadata('design:paramtypes', typeOrFunc);
|
||||
if (isPresent(paramTypes) || isPresent(paramAnnotations)) {
|
||||
return this._zipTypesAndAnnotations(paramTypes, paramAnnotations);
|
||||
}
|
||||
}
|
||||
// The array has to be filled with `undefined` because holes would be skipped by `some`
|
||||
let parameters = new Array((<any>typeOrFunc.length));
|
||||
parameters.fill(undefined);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
annotations(typeOrFunc: Type): any[] {
|
||||
// Prefer the direct API.
|
||||
if (isPresent((<any>typeOrFunc).annotations)) {
|
||||
var annotations = (<any>typeOrFunc).annotations;
|
||||
if (isFunction(annotations) && annotations.annotations) {
|
||||
annotations = annotations.annotations;
|
||||
}
|
||||
return annotations;
|
||||
}
|
||||
if (isPresent(this._reflect) && isPresent(this._reflect.getMetadata)) {
|
||||
var annotations = this._reflect.getMetadata('annotations', typeOrFunc);
|
||||
if (isPresent(annotations)) return annotations;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
propMetadata(typeOrFunc: any): {[key: string]: any[]} {
|
||||
// Prefer the direct API.
|
||||
if (isPresent((<any>typeOrFunc).propMetadata)) {
|
||||
var propMetadata = (<any>typeOrFunc).propMetadata;
|
||||
if (isFunction(propMetadata) && propMetadata.propMetadata) {
|
||||
propMetadata = propMetadata.propMetadata;
|
||||
}
|
||||
return propMetadata;
|
||||
}
|
||||
if (isPresent(this._reflect) && isPresent(this._reflect.getMetadata)) {
|
||||
var propMetadata = this._reflect.getMetadata('propMetadata', typeOrFunc);
|
||||
if (isPresent(propMetadata)) return propMetadata;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
interfaces(type: Type): any[] {
|
||||
throw new BaseException("JavaScript does not support interfaces");
|
||||
}
|
||||
|
||||
getter(name: string): GetterFn { return <GetterFn>new Function('o', 'return o.' + name + ';'); }
|
||||
|
||||
setter(name: string): SetterFn {
|
||||
return <SetterFn>new Function('o', 'v', 'return o.' + name + ' = v;');
|
||||
}
|
||||
|
||||
method(name: string): MethodFn {
|
||||
let functionBody = `if (!o.${name}) throw new Error('"${name}" is undefined');
|
||||
return o.${name}.apply(o, args);`;
|
||||
return <MethodFn>new Function('o', 'args', functionBody);
|
||||
}
|
||||
|
||||
// There is not a concept of import uri in Js, but this is useful in developing Dart applications.
|
||||
importUri(type: Type): string { return `./${stringify(type)}`; }
|
||||
}
|
168
modules/@angular/core/src/reflection/reflector.ts
Normal file
168
modules/@angular/core/src/reflection/reflector.ts
Normal file
@ -0,0 +1,168 @@
|
||||
import {Type, isPresent, stringify} from 'angular2/src/facade/lang';
|
||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
import {
|
||||
ListWrapper,
|
||||
Map,
|
||||
MapWrapper,
|
||||
Set,
|
||||
SetWrapper,
|
||||
StringMapWrapper
|
||||
} from 'angular2/src/facade/collection';
|
||||
import {SetterFn, GetterFn, MethodFn} from './types';
|
||||
import {ReflectorReader} from './reflector_reader';
|
||||
import {PlatformReflectionCapabilities} from './platform_reflection_capabilities';
|
||||
export {SetterFn, GetterFn, MethodFn} from './types';
|
||||
export {PlatformReflectionCapabilities} from './platform_reflection_capabilities';
|
||||
|
||||
/**
|
||||
* Reflective information about a symbol, including annotations, interfaces, and other metadata.
|
||||
*/
|
||||
export class ReflectionInfo {
|
||||
constructor(public annotations?: any[], public parameters?: any[][], public factory?: Function,
|
||||
public interfaces?: any[], public propMetadata?: {[key: string]: any[]}) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides access to reflection data about symbols. Used internally by Angular
|
||||
* to power dependency injection and compilation.
|
||||
*/
|
||||
export class Reflector extends ReflectorReader {
|
||||
/** @internal */
|
||||
_injectableInfo = new Map<any, ReflectionInfo>();
|
||||
/** @internal */
|
||||
_getters = new Map<string, GetterFn>();
|
||||
/** @internal */
|
||||
_setters = new Map<string, SetterFn>();
|
||||
/** @internal */
|
||||
_methods = new Map<string, MethodFn>();
|
||||
/** @internal */
|
||||
_usedKeys: Set<any>;
|
||||
reflectionCapabilities: PlatformReflectionCapabilities;
|
||||
|
||||
constructor(reflectionCapabilities: PlatformReflectionCapabilities) {
|
||||
super();
|
||||
this._usedKeys = null;
|
||||
this.reflectionCapabilities = reflectionCapabilities;
|
||||
}
|
||||
|
||||
isReflectionEnabled(): boolean { return this.reflectionCapabilities.isReflectionEnabled(); }
|
||||
|
||||
/**
|
||||
* Causes `this` reflector to track keys used to access
|
||||
* {@link ReflectionInfo} objects.
|
||||
*/
|
||||
trackUsage(): void { this._usedKeys = new Set(); }
|
||||
|
||||
/**
|
||||
* Lists types for which reflection information was not requested since
|
||||
* {@link #trackUsage} was called. This list could later be audited as
|
||||
* potential dead code.
|
||||
*/
|
||||
listUnusedKeys(): any[] {
|
||||
if (this._usedKeys == null) {
|
||||
throw new BaseException('Usage tracking is disabled');
|
||||
}
|
||||
var allTypes = MapWrapper.keys(this._injectableInfo);
|
||||
return allTypes.filter(key => !SetWrapper.has(this._usedKeys, key));
|
||||
}
|
||||
|
||||
registerFunction(func: Function, funcInfo: ReflectionInfo): void {
|
||||
this._injectableInfo.set(func, funcInfo);
|
||||
}
|
||||
|
||||
registerType(type: Type, typeInfo: ReflectionInfo): void {
|
||||
this._injectableInfo.set(type, typeInfo);
|
||||
}
|
||||
|
||||
registerGetters(getters: {[key: string]: GetterFn}): void { _mergeMaps(this._getters, getters); }
|
||||
|
||||
registerSetters(setters: {[key: string]: SetterFn}): void { _mergeMaps(this._setters, setters); }
|
||||
|
||||
registerMethods(methods: {[key: string]: MethodFn}): void { _mergeMaps(this._methods, methods); }
|
||||
|
||||
factory(type: Type): Function {
|
||||
if (this._containsReflectionInfo(type)) {
|
||||
var res = this._getReflectionInfo(type).factory;
|
||||
return isPresent(res) ? res : null;
|
||||
} else {
|
||||
return this.reflectionCapabilities.factory(type);
|
||||
}
|
||||
}
|
||||
|
||||
parameters(typeOrFunc: /*Type*/ any): any[][] {
|
||||
if (this._injectableInfo.has(typeOrFunc)) {
|
||||
var res = this._getReflectionInfo(typeOrFunc).parameters;
|
||||
return isPresent(res) ? res : [];
|
||||
} else {
|
||||
return this.reflectionCapabilities.parameters(typeOrFunc);
|
||||
}
|
||||
}
|
||||
|
||||
annotations(typeOrFunc: /*Type*/ any): any[] {
|
||||
if (this._injectableInfo.has(typeOrFunc)) {
|
||||
var res = this._getReflectionInfo(typeOrFunc).annotations;
|
||||
return isPresent(res) ? res : [];
|
||||
} else {
|
||||
return this.reflectionCapabilities.annotations(typeOrFunc);
|
||||
}
|
||||
}
|
||||
|
||||
propMetadata(typeOrFunc: /*Type*/ any): {[key: string]: any[]} {
|
||||
if (this._injectableInfo.has(typeOrFunc)) {
|
||||
var res = this._getReflectionInfo(typeOrFunc).propMetadata;
|
||||
return isPresent(res) ? res : {};
|
||||
} else {
|
||||
return this.reflectionCapabilities.propMetadata(typeOrFunc);
|
||||
}
|
||||
}
|
||||
|
||||
interfaces(type: Type): any[] {
|
||||
if (this._injectableInfo.has(type)) {
|
||||
var res = this._getReflectionInfo(type).interfaces;
|
||||
return isPresent(res) ? res : [];
|
||||
} else {
|
||||
return this.reflectionCapabilities.interfaces(type);
|
||||
}
|
||||
}
|
||||
|
||||
getter(name: string): GetterFn {
|
||||
if (this._getters.has(name)) {
|
||||
return this._getters.get(name);
|
||||
} else {
|
||||
return this.reflectionCapabilities.getter(name);
|
||||
}
|
||||
}
|
||||
|
||||
setter(name: string): SetterFn {
|
||||
if (this._setters.has(name)) {
|
||||
return this._setters.get(name);
|
||||
} else {
|
||||
return this.reflectionCapabilities.setter(name);
|
||||
}
|
||||
}
|
||||
|
||||
method(name: string): MethodFn {
|
||||
if (this._methods.has(name)) {
|
||||
return this._methods.get(name);
|
||||
} else {
|
||||
return this.reflectionCapabilities.method(name);
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_getReflectionInfo(typeOrFunc: any): ReflectionInfo {
|
||||
if (isPresent(this._usedKeys)) {
|
||||
this._usedKeys.add(typeOrFunc);
|
||||
}
|
||||
return this._injectableInfo.get(typeOrFunc);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_containsReflectionInfo(typeOrFunc: any) { return this._injectableInfo.has(typeOrFunc); }
|
||||
|
||||
importUri(type: Type): string { return this.reflectionCapabilities.importUri(type); }
|
||||
}
|
||||
|
||||
function _mergeMaps(target: Map<string, Function>, config: {[key: string]: Function}): void {
|
||||
StringMapWrapper.forEach(config, (v: Function, k: string) => target.set(k, v));
|
||||
}
|
10
modules/@angular/core/src/reflection/reflector_reader.ts
Normal file
10
modules/@angular/core/src/reflection/reflector_reader.ts
Normal file
@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Provides read-only access to reflection data about symbols. Used internally by Angular
|
||||
* to power dependency injection and compilation.
|
||||
*/
|
||||
export abstract class ReflectorReader {
|
||||
abstract parameters(typeOrFunc: /*Type*/ any): any[][];
|
||||
abstract annotations(typeOrFunc: /*Type*/ any): any[];
|
||||
abstract propMetadata(typeOrFunc: /*Type*/ any): {[key: string]: any[]};
|
||||
abstract importUri(typeOrFunc: /*Type*/ any): string;
|
||||
}
|
5
modules/@angular/core/src/reflection/types.dart
Normal file
5
modules/@angular/core/src/reflection/types.dart
Normal file
@ -0,0 +1,5 @@
|
||||
library reflection.types;
|
||||
|
||||
typedef SetterFn(obj, value);
|
||||
typedef GetterFn(obj);
|
||||
typedef MethodFn(obj, List args);
|
5
modules/@angular/core/src/reflection/types.ts
Normal file
5
modules/@angular/core/src/reflection/types.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import {Type} from 'angular2/src/facade/lang';
|
||||
|
||||
export type SetterFn = (obj: any, value: any) => void;
|
||||
export type GetterFn = (obj: any) => any;
|
||||
export type MethodFn = (obj: any, args: any[]) => any;
|
2
modules/@angular/core/src/render.ts
Normal file
2
modules/@angular/core/src/render.ts
Normal file
@ -0,0 +1,2 @@
|
||||
// Public API for render
|
||||
export {RootRenderer, Renderer, RenderComponentType} from './render/api';
|
77
modules/@angular/core/src/render/api.ts
Normal file
77
modules/@angular/core/src/render/api.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||
import {Injector, Injectable} from 'angular2/src/core/di';
|
||||
|
||||
export class RenderComponentType {
|
||||
constructor(public id: string, public templateUrl: string, public slotCount: number,
|
||||
public encapsulation: ViewEncapsulation, public styles: Array<string | any[]>) {}
|
||||
}
|
||||
|
||||
export abstract class RenderDebugInfo {
|
||||
get injector(): Injector { return unimplemented(); }
|
||||
get component(): any { return unimplemented(); }
|
||||
get providerTokens(): any[] { return unimplemented(); }
|
||||
get references(): {[key: string]: any} { return unimplemented(); }
|
||||
get context(): any { return unimplemented(); }
|
||||
get source(): string { return unimplemented(); }
|
||||
}
|
||||
|
||||
export abstract class Renderer {
|
||||
abstract selectRootElement(selectorOrNode: string | any, debugInfo: RenderDebugInfo): any;
|
||||
|
||||
abstract createElement(parentElement: any, name: string, debugInfo: RenderDebugInfo): any;
|
||||
|
||||
abstract createViewRoot(hostElement: any): any;
|
||||
|
||||
abstract createTemplateAnchor(parentElement: any, debugInfo: RenderDebugInfo): any;
|
||||
|
||||
abstract createText(parentElement: any, value: string, debugInfo: RenderDebugInfo): any;
|
||||
|
||||
abstract projectNodes(parentElement: any, nodes: any[]): void;
|
||||
|
||||
abstract attachViewAfter(node: any, viewRootNodes: any[]): void;
|
||||
|
||||
abstract detachView(viewRootNodes: any[]): void;
|
||||
|
||||
abstract destroyView(hostElement: any, viewAllNodes: any[]): void;
|
||||
|
||||
abstract listen(renderElement: any, name: string, callback: Function): Function;
|
||||
|
||||
abstract listenGlobal(target: string, name: string, callback: Function): Function;
|
||||
|
||||
abstract setElementProperty(renderElement: any, propertyName: string, propertyValue: any): void;
|
||||
|
||||
abstract setElementAttribute(renderElement: any, attributeName: string,
|
||||
attributeValue: string): void;
|
||||
|
||||
/**
|
||||
* Used only in debug mode to serialize property changes to dom nodes as attributes.
|
||||
*/
|
||||
abstract setBindingDebugInfo(renderElement: any, propertyName: string,
|
||||
propertyValue: string): void;
|
||||
|
||||
abstract setElementClass(renderElement: any, className: string, isAdd: boolean);
|
||||
|
||||
abstract setElementStyle(renderElement: any, styleName: string, styleValue: string);
|
||||
|
||||
abstract invokeElementMethod(renderElement: any, methodName: string, args: any[]);
|
||||
|
||||
abstract setText(renderNode: any, text: string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injectable service that provides a low-level interface for modifying the UI.
|
||||
*
|
||||
* Use this service to bypass Angular's templating and make custom UI changes that can't be
|
||||
* expressed declaratively. For example if you need to set a property or an attribute whose name is
|
||||
* not statically known, use {@link #setElementProperty} or {@link #setElementAttribute}
|
||||
* respectively.
|
||||
*
|
||||
* If you are implementing a custom renderer, you must implement this interface.
|
||||
*
|
||||
* The default Renderer implementation is `DomRenderer`. Also available is `WebWorkerRenderer`.
|
||||
*/
|
||||
|
||||
export abstract class RootRenderer {
|
||||
abstract renderComponent(componentType: RenderComponentType): Renderer;
|
||||
}
|
153
modules/@angular/core/src/testability/testability.ts
Normal file
153
modules/@angular/core/src/testability/testability.ts
Normal file
@ -0,0 +1,153 @@
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import {Map, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {scheduleMicroTask} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {NgZone} from '../zone/ng_zone';
|
||||
import {ObservableWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
|
||||
/**
|
||||
* The Testability service provides testing hooks that can be accessed from
|
||||
* the browser and by services such as Protractor. Each bootstrapped Angular
|
||||
* application on the page will have an instance of Testability.
|
||||
*/
|
||||
@Injectable()
|
||||
export class Testability {
|
||||
/** @internal */
|
||||
_pendingCount: number = 0;
|
||||
/** @internal */
|
||||
_isZoneStable: boolean = true;
|
||||
/**
|
||||
* Whether any work was done since the last 'whenStable' callback. This is
|
||||
* useful to detect if this could have potentially destabilized another
|
||||
* component while it is stabilizing.
|
||||
* @internal
|
||||
*/
|
||||
_didWork: boolean = false;
|
||||
/** @internal */
|
||||
_callbacks: Function[] = [];
|
||||
constructor(private _ngZone: NgZone) { this._watchAngularEvents(); }
|
||||
|
||||
/** @internal */
|
||||
_watchAngularEvents(): void {
|
||||
ObservableWrapper.subscribe(this._ngZone.onUnstable, (_) => {
|
||||
this._didWork = true;
|
||||
this._isZoneStable = false;
|
||||
});
|
||||
|
||||
this._ngZone.runOutsideAngular(() => {
|
||||
ObservableWrapper.subscribe(this._ngZone.onStable, (_) => {
|
||||
NgZone.assertNotInAngularZone();
|
||||
scheduleMicroTask(() => {
|
||||
this._isZoneStable = true;
|
||||
this._runCallbacksIfReady();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
increasePendingRequestCount(): number {
|
||||
this._pendingCount += 1;
|
||||
this._didWork = true;
|
||||
return this._pendingCount;
|
||||
}
|
||||
|
||||
decreasePendingRequestCount(): number {
|
||||
this._pendingCount -= 1;
|
||||
if (this._pendingCount < 0) {
|
||||
throw new BaseException('pending async requests below zero');
|
||||
}
|
||||
this._runCallbacksIfReady();
|
||||
return this._pendingCount;
|
||||
}
|
||||
|
||||
isStable(): boolean {
|
||||
return this._isZoneStable && this._pendingCount == 0 && !this._ngZone.hasPendingMacrotasks;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_runCallbacksIfReady(): void {
|
||||
if (this.isStable()) {
|
||||
// Schedules the call backs in a new frame so that it is always async.
|
||||
scheduleMicroTask(() => {
|
||||
while (this._callbacks.length !== 0) {
|
||||
(this._callbacks.pop())(this._didWork);
|
||||
}
|
||||
this._didWork = false;
|
||||
});
|
||||
} else {
|
||||
// Not Ready
|
||||
this._didWork = true;
|
||||
}
|
||||
}
|
||||
|
||||
whenStable(callback: Function): void {
|
||||
this._callbacks.push(callback);
|
||||
this._runCallbacksIfReady();
|
||||
}
|
||||
|
||||
getPendingRequestCount(): number { return this._pendingCount; }
|
||||
|
||||
findBindings(using: any, provider: string, exactMatch: boolean): any[] {
|
||||
// TODO(juliemr): implement.
|
||||
return [];
|
||||
}
|
||||
|
||||
findProviders(using: any, provider: string, exactMatch: boolean): any[] {
|
||||
// TODO(juliemr): implement.
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A global registry of {@link Testability} instances for specific elements.
|
||||
*/
|
||||
@Injectable()
|
||||
export class TestabilityRegistry {
|
||||
/** @internal */
|
||||
_applications = new Map<any, Testability>();
|
||||
|
||||
constructor() { _testabilityGetter.addToWindow(this); }
|
||||
|
||||
registerApplication(token: any, testability: Testability) {
|
||||
this._applications.set(token, testability);
|
||||
}
|
||||
|
||||
getTestability(elem: any): Testability { return this._applications.get(elem); }
|
||||
|
||||
getAllTestabilities(): Testability[] { return MapWrapper.values(this._applications); }
|
||||
|
||||
getAllRootElements(): any[] { return MapWrapper.keys(this._applications); }
|
||||
|
||||
findTestabilityInTree(elem: Node, findInAncestors: boolean = true): Testability {
|
||||
return _testabilityGetter.findTestabilityInTree(this, elem, findInAncestors);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter interface for retrieving the `Testability` service associated for a
|
||||
* particular context.
|
||||
*/
|
||||
export interface GetTestability {
|
||||
addToWindow(registry: TestabilityRegistry): void;
|
||||
findTestabilityInTree(registry: TestabilityRegistry, elem: any,
|
||||
findInAncestors: boolean): Testability;
|
||||
}
|
||||
|
||||
/* @ts2dart_const */
|
||||
class _NoopGetTestability implements GetTestability {
|
||||
addToWindow(registry: TestabilityRegistry): void {}
|
||||
findTestabilityInTree(registry: TestabilityRegistry, elem: any,
|
||||
findInAncestors: boolean): Testability {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link GetTestability} implementation used by the Angular testing framework.
|
||||
*/
|
||||
export function setTestabilityGetter(getter: GetTestability): void {
|
||||
_testabilityGetter = getter;
|
||||
}
|
||||
|
||||
var _testabilityGetter: GetTestability = /*@ts2dart_const*/ new _NoopGetTestability();
|
4
modules/@angular/core/src/util.dart
Normal file
4
modules/@angular/core/src/util.dart
Normal file
@ -0,0 +1,4 @@
|
||||
library angular2.src.core.util;
|
||||
|
||||
// the ts version is needed only for TS, we don't need a Dart implementation
|
||||
// because there are no decorators in Dart.
|
2
modules/@angular/core/src/util.ts
Normal file
2
modules/@angular/core/src/util.ts
Normal file
@ -0,0 +1,2 @@
|
||||
// Public API for util
|
||||
export {Class, ClassDefinition, TypeDecorator} from './util/decorators';
|
1
modules/@angular/core/src/util/decorators.dart
Normal file
1
modules/@angular/core/src/util/decorators.dart
Normal file
@ -0,0 +1 @@
|
||||
library angular2.core.util.decorators;
|
329
modules/@angular/core/src/util/decorators.ts
Normal file
329
modules/@angular/core/src/util/decorators.ts
Normal file
@ -0,0 +1,329 @@
|
||||
import {ConcreteType, global, Type, isFunction, stringify} from 'angular2/src/facade/lang';
|
||||
|
||||
var _nextClassId = 0;
|
||||
|
||||
/**
|
||||
* Declares the interface to be used with {@link Class}.
|
||||
*/
|
||||
export interface ClassDefinition {
|
||||
/**
|
||||
* Optional argument for specifying the superclass.
|
||||
*/
|
||||
extends?: Type;
|
||||
|
||||
/**
|
||||
* Required constructor function for a class.
|
||||
*
|
||||
* The function may be optionally wrapped in an `Array`, in which case additional parameter
|
||||
* annotations may be specified.
|
||||
* The number of arguments and the number of parameter annotations must match.
|
||||
*
|
||||
* See {@link Class} for example of usage.
|
||||
*/
|
||||
constructor: Function | any[];
|
||||
|
||||
/**
|
||||
* Other methods on the class. Note that values should have type 'Function' but TS requires
|
||||
* all properties to have a narrower type than the index signature.
|
||||
*/
|
||||
[x: string]: Type | Function | any[];
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface implemented by all Angular type decorators, which allows them to be used as ES7
|
||||
* decorators as well as
|
||||
* Angular DSL syntax.
|
||||
*
|
||||
* DSL syntax:
|
||||
*
|
||||
* ```
|
||||
* var MyClass = ng
|
||||
* .Component({...})
|
||||
* .View({...})
|
||||
* .Class({...});
|
||||
* ```
|
||||
*
|
||||
* ES7 syntax:
|
||||
*
|
||||
* ```
|
||||
* @ng.Component({...})
|
||||
* @ng.View({...})
|
||||
* class MyClass {...}
|
||||
* ```
|
||||
*/
|
||||
export interface TypeDecorator {
|
||||
/**
|
||||
* Invoke as ES7 decorator.
|
||||
*/
|
||||
<T extends Type>(type: T): T;
|
||||
|
||||
// Make TypeDecorator assignable to built-in ParameterDecorator type.
|
||||
// ParameterDecorator is declared in lib.d.ts as a `declare type`
|
||||
// so we cannot declare this interface as a subtype.
|
||||
// see https://github.com/angular/angular/issues/3379#issuecomment-126169417
|
||||
(target: Object, propertyKey?: string | symbol, parameterIndex?: number): void;
|
||||
|
||||
/**
|
||||
* Storage for the accumulated annotations so far used by the DSL syntax.
|
||||
*
|
||||
* Used by {@link Class} to annotate the generated class.
|
||||
*/
|
||||
annotations: any[];
|
||||
|
||||
/**
|
||||
* Generate a class from the definition and annotate it with {@link TypeDecorator#annotations}.
|
||||
*/
|
||||
Class(obj: ClassDefinition): ConcreteType;
|
||||
}
|
||||
|
||||
function extractAnnotation(annotation: any): any {
|
||||
if (isFunction(annotation) && annotation.hasOwnProperty('annotation')) {
|
||||
// it is a decorator, extract annotation
|
||||
annotation = annotation.annotation;
|
||||
}
|
||||
return annotation;
|
||||
}
|
||||
|
||||
function applyParams(fnOrArray: (Function | any[]), key: string): Function {
|
||||
if (fnOrArray === Object || fnOrArray === String || fnOrArray === Function ||
|
||||
fnOrArray === Number || fnOrArray === Array) {
|
||||
throw new Error(`Can not use native ${stringify(fnOrArray)} as constructor`);
|
||||
}
|
||||
if (isFunction(fnOrArray)) {
|
||||
return <Function>fnOrArray;
|
||||
} else if (fnOrArray instanceof Array) {
|
||||
var annotations: any[] = fnOrArray;
|
||||
var fn: Function = fnOrArray[fnOrArray.length - 1];
|
||||
if (!isFunction(fn)) {
|
||||
throw new Error(
|
||||
`Last position of Class method array must be Function in key ${key} was '${stringify(fn)}'`);
|
||||
}
|
||||
var annoLength = annotations.length - 1;
|
||||
if (annoLength != fn.length) {
|
||||
throw new Error(
|
||||
`Number of annotations (${annoLength}) does not match number of arguments (${fn.length}) in the function: ${stringify(fn)}`);
|
||||
}
|
||||
var paramsAnnotations: any[][] = [];
|
||||
for (var i = 0, ii = annotations.length - 1; i < ii; i++) {
|
||||
var paramAnnotations: any[] = [];
|
||||
paramsAnnotations.push(paramAnnotations);
|
||||
var annotation = annotations[i];
|
||||
if (annotation instanceof Array) {
|
||||
for (var j = 0; j < annotation.length; j++) {
|
||||
paramAnnotations.push(extractAnnotation(annotation[j]));
|
||||
}
|
||||
} else if (isFunction(annotation)) {
|
||||
paramAnnotations.push(extractAnnotation(annotation));
|
||||
} else {
|
||||
paramAnnotations.push(annotation);
|
||||
}
|
||||
}
|
||||
Reflect.defineMetadata('parameters', paramsAnnotations, fn);
|
||||
return fn;
|
||||
} else {
|
||||
throw new Error(
|
||||
`Only Function or Array is supported in Class definition for key '${key}' is '${stringify(fnOrArray)}'`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a way for expressing ES6 classes with parameter annotations in ES5.
|
||||
*
|
||||
* ## Basic Example
|
||||
*
|
||||
* ```
|
||||
* var Greeter = ng.Class({
|
||||
* constructor: function(name) {
|
||||
* this.name = name;
|
||||
* },
|
||||
*
|
||||
* greet: function() {
|
||||
* alert('Hello ' + this.name + '!');
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* is equivalent to ES6:
|
||||
*
|
||||
* ```
|
||||
* class Greeter {
|
||||
* constructor(name) {
|
||||
* this.name = name;
|
||||
* }
|
||||
*
|
||||
* greet() {
|
||||
* alert('Hello ' + this.name + '!');
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* or equivalent to ES5:
|
||||
*
|
||||
* ```
|
||||
* var Greeter = function (name) {
|
||||
* this.name = name;
|
||||
* }
|
||||
*
|
||||
* Greeter.prototype.greet = function () {
|
||||
* alert('Hello ' + this.name + '!');
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ### Example with parameter annotations
|
||||
*
|
||||
* ```
|
||||
* var MyService = ng.Class({
|
||||
* constructor: [String, [new Query(), QueryList], function(name, queryList) {
|
||||
* ...
|
||||
* }]
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* is equivalent to ES6:
|
||||
*
|
||||
* ```
|
||||
* class MyService {
|
||||
* constructor(name: string, @Query() queryList: QueryList) {
|
||||
* ...
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ### Example with inheritance
|
||||
*
|
||||
* ```
|
||||
* var Shape = ng.Class({
|
||||
* constructor: (color) {
|
||||
* this.color = color;
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* var Square = ng.Class({
|
||||
* extends: Shape,
|
||||
* constructor: function(color, size) {
|
||||
* Shape.call(this, color);
|
||||
* this.size = size;
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function Class(clsDef: ClassDefinition): ConcreteType {
|
||||
var constructor = applyParams(
|
||||
clsDef.hasOwnProperty('constructor') ? clsDef.constructor : undefined, 'constructor');
|
||||
var proto = constructor.prototype;
|
||||
if (clsDef.hasOwnProperty('extends')) {
|
||||
if (isFunction(clsDef.extends)) {
|
||||
(<Function>constructor).prototype = proto =
|
||||
Object.create((<Function>clsDef.extends).prototype);
|
||||
} else {
|
||||
throw new Error(
|
||||
`Class definition 'extends' property must be a constructor function was: ${stringify(clsDef.extends)}`);
|
||||
}
|
||||
}
|
||||
for (var key in clsDef) {
|
||||
if (key != 'extends' && key != 'prototype' && clsDef.hasOwnProperty(key)) {
|
||||
proto[key] = applyParams(clsDef[key], key);
|
||||
}
|
||||
}
|
||||
|
||||
if (this && this.annotations instanceof Array) {
|
||||
Reflect.defineMetadata('annotations', this.annotations, constructor);
|
||||
}
|
||||
|
||||
if (!constructor['name']) {
|
||||
constructor['overriddenName'] = `class${_nextClassId++}`;
|
||||
}
|
||||
|
||||
return <ConcreteType>constructor;
|
||||
}
|
||||
|
||||
var Reflect = global.Reflect;
|
||||
// Throw statement at top-level is disallowed by closure compiler in ES6 input.
|
||||
// Wrap in an IIFE as a work-around.
|
||||
(function checkReflect() {
|
||||
if (!(Reflect && Reflect.getMetadata)) {
|
||||
throw 'reflect-metadata shim is required when using class decorators';
|
||||
}
|
||||
})();
|
||||
|
||||
export function makeDecorator(
|
||||
annotationCls, chainFn: (fn: Function) => void = null): (...args: any[]) => (cls: any) => any {
|
||||
function DecoratorFactory(objOrType): (cls: any) => any {
|
||||
var annotationInstance = new (<any>annotationCls)(objOrType);
|
||||
if (this instanceof annotationCls) {
|
||||
return annotationInstance;
|
||||
} else {
|
||||
var chainAnnotation =
|
||||
isFunction(this) && this.annotations instanceof Array ? this.annotations : [];
|
||||
chainAnnotation.push(annotationInstance);
|
||||
var TypeDecorator: TypeDecorator = <TypeDecorator>function TypeDecorator(cls) {
|
||||
var annotations = Reflect.getOwnMetadata('annotations', cls);
|
||||
annotations = annotations || [];
|
||||
annotations.push(annotationInstance);
|
||||
Reflect.defineMetadata('annotations', annotations, cls);
|
||||
return cls;
|
||||
};
|
||||
TypeDecorator.annotations = chainAnnotation;
|
||||
TypeDecorator.Class = Class;
|
||||
if (chainFn) chainFn(TypeDecorator);
|
||||
return TypeDecorator;
|
||||
}
|
||||
}
|
||||
DecoratorFactory.prototype = Object.create(annotationCls.prototype);
|
||||
return DecoratorFactory;
|
||||
}
|
||||
|
||||
export function makeParamDecorator(annotationCls): any {
|
||||
function ParamDecoratorFactory(...args): any {
|
||||
var annotationInstance = Object.create(annotationCls.prototype);
|
||||
annotationCls.apply(annotationInstance, args);
|
||||
if (this instanceof annotationCls) {
|
||||
return annotationInstance;
|
||||
} else {
|
||||
(<any>ParamDecorator).annotation = annotationInstance;
|
||||
return ParamDecorator;
|
||||
}
|
||||
|
||||
|
||||
function ParamDecorator(cls, unusedKey, index): any {
|
||||
var parameters: any[][] = Reflect.getMetadata('parameters', cls);
|
||||
parameters = parameters || [];
|
||||
|
||||
// there might be gaps if some in between parameters do not have annotations.
|
||||
// we pad with nulls.
|
||||
while (parameters.length <= index) {
|
||||
parameters.push(null);
|
||||
}
|
||||
|
||||
parameters[index] = parameters[index] || [];
|
||||
var annotationsForParam: any[] = parameters[index];
|
||||
annotationsForParam.push(annotationInstance);
|
||||
|
||||
Reflect.defineMetadata('parameters', parameters, cls);
|
||||
return cls;
|
||||
}
|
||||
}
|
||||
ParamDecoratorFactory.prototype = Object.create(annotationCls.prototype);
|
||||
return ParamDecoratorFactory;
|
||||
}
|
||||
|
||||
export function makePropDecorator(decoratorCls): any {
|
||||
function PropDecoratorFactory(...args): any {
|
||||
var decoratorInstance = Object.create(decoratorCls.prototype);
|
||||
decoratorCls.apply(decoratorInstance, args);
|
||||
|
||||
if (this instanceof decoratorCls) {
|
||||
return decoratorInstance;
|
||||
} else {
|
||||
return function PropDecorator(target: any, name: string) {
|
||||
var meta = Reflect.getOwnMetadata('propMetadata', target.constructor);
|
||||
meta = meta || {};
|
||||
meta[name] = meta[name] || [];
|
||||
meta[name].unshift(decoratorInstance);
|
||||
Reflect.defineMetadata('propMetadata', meta, target.constructor);
|
||||
};
|
||||
}
|
||||
}
|
||||
PropDecoratorFactory.prototype = Object.create(decoratorCls.prototype);
|
||||
return PropDecoratorFactory;
|
||||
}
|
2
modules/@angular/core/src/zone.ts
Normal file
2
modules/@angular/core/src/zone.ts
Normal file
@ -0,0 +1,2 @@
|
||||
// Public API for Zone
|
||||
export {NgZone, NgZoneError} from './zone/ng_zone';
|
227
modules/@angular/core/src/zone/ng_zone.ts
Normal file
227
modules/@angular/core/src/zone/ng_zone.ts
Normal file
@ -0,0 +1,227 @@
|
||||
import {EventEmitter} from 'angular2/src/facade/async';
|
||||
import {NgZoneImpl, NgZoneError} from './ng_zone_impl';
|
||||
import {BaseException} from '../../facade/exceptions';
|
||||
export {NgZoneError} from './ng_zone_impl';
|
||||
|
||||
|
||||
/**
|
||||
* An injectable service for executing work inside or outside of the Angular zone.
|
||||
*
|
||||
* The most common use of this service is to optimize performance when starting a work consisting of
|
||||
* one or more asynchronous tasks that don't require UI updates or error handling to be handled by
|
||||
* Angular. Such tasks can be kicked off via {@link #runOutsideAngular} and if needed, these tasks
|
||||
* can reenter the Angular zone via {@link #run}.
|
||||
*
|
||||
* <!-- TODO: add/fix links to:
|
||||
* - docs explaining zones and the use of zones in Angular and change-detection
|
||||
* - link to runOutsideAngular/run (throughout this file!)
|
||||
* -->
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/lY9m8HLy7z06vDoUaSN2?p=preview))
|
||||
* ```
|
||||
* import {Component, View, NgZone} from 'angular2/core';
|
||||
* import {NgIf} from 'angular2/common';
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'ng-zone-demo'.
|
||||
* template: `
|
||||
* <h2>Demo: NgZone</h2>
|
||||
*
|
||||
* <p>Progress: {{progress}}%</p>
|
||||
* <p *ngIf="progress >= 100">Done processing {{label}} of Angular zone!</p>
|
||||
*
|
||||
* <button (click)="processWithinAngularZone()">Process within Angular zone</button>
|
||||
* <button (click)="processOutsideOfAngularZone()">Process outside of Angular zone</button>
|
||||
* `,
|
||||
* directives: [NgIf]
|
||||
* })
|
||||
* export class NgZoneDemo {
|
||||
* progress: number = 0;
|
||||
* label: string;
|
||||
*
|
||||
* constructor(private _ngZone: NgZone) {}
|
||||
*
|
||||
* // Loop inside the Angular zone
|
||||
* // so the UI DOES refresh after each setTimeout cycle
|
||||
* processWithinAngularZone() {
|
||||
* this.label = 'inside';
|
||||
* this.progress = 0;
|
||||
* this._increaseProgress(() => console.log('Inside Done!'));
|
||||
* }
|
||||
*
|
||||
* // Loop outside of the Angular zone
|
||||
* // so the UI DOES NOT refresh after each setTimeout cycle
|
||||
* processOutsideOfAngularZone() {
|
||||
* this.label = 'outside';
|
||||
* this.progress = 0;
|
||||
* this._ngZone.runOutsideAngular(() => {
|
||||
* this._increaseProgress(() => {
|
||||
* // reenter the Angular zone and display done
|
||||
* this._ngZone.run(() => {console.log('Outside Done!') });
|
||||
* }}));
|
||||
* }
|
||||
*
|
||||
*
|
||||
* _increaseProgress(doneCallback: () => void) {
|
||||
* this.progress += 1;
|
||||
* console.log(`Current progress: ${this.progress}%`);
|
||||
*
|
||||
* if (this.progress < 100) {
|
||||
* window.setTimeout(() => this._increaseProgress(doneCallback)), 10)
|
||||
* } else {
|
||||
* doneCallback();
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export class NgZone {
|
||||
static isInAngularZone(): boolean { return NgZoneImpl.isInAngularZone(); }
|
||||
static assertInAngularZone(): void {
|
||||
if (!NgZoneImpl.isInAngularZone()) {
|
||||
throw new BaseException('Expected to be in Angular Zone, but it is not!');
|
||||
}
|
||||
}
|
||||
static assertNotInAngularZone(): void {
|
||||
if (NgZoneImpl.isInAngularZone()) {
|
||||
throw new BaseException('Expected to not be in Angular Zone, but it is!');
|
||||
}
|
||||
}
|
||||
|
||||
private _zoneImpl: NgZoneImpl;
|
||||
|
||||
private _hasPendingMicrotasks: boolean = false;
|
||||
private _hasPendingMacrotasks: boolean = false;
|
||||
|
||||
/** @internal */
|
||||
private _isStable = true;
|
||||
/** @internal */
|
||||
private _nesting = 0;
|
||||
/** @internal */
|
||||
private _onUnstable: EventEmitter<any> = new EventEmitter(false);
|
||||
/** @internal */
|
||||
private _onMicrotaskEmpty: EventEmitter<any> = new EventEmitter(false);
|
||||
/** @internal */
|
||||
private _onStable: EventEmitter<any> = new EventEmitter(false);
|
||||
/** @internal */
|
||||
private _onErrorEvents: EventEmitter<any> = new EventEmitter(false);
|
||||
|
||||
/**
|
||||
* @param {bool} enableLongStackTrace whether to enable long stack trace. They should only be
|
||||
* enabled in development mode as they significantly impact perf.
|
||||
*/
|
||||
constructor({enableLongStackTrace = false}) {
|
||||
this._zoneImpl = new NgZoneImpl({
|
||||
trace: enableLongStackTrace,
|
||||
onEnter: () => {
|
||||
// console.log('ZONE.enter', this._nesting, this._isStable);
|
||||
this._nesting++;
|
||||
if (this._isStable) {
|
||||
this._isStable = false;
|
||||
this._onUnstable.emit(null);
|
||||
}
|
||||
},
|
||||
onLeave: () => {
|
||||
this._nesting--;
|
||||
// console.log('ZONE.leave', this._nesting, this._isStable);
|
||||
this._checkStable();
|
||||
},
|
||||
setMicrotask: (hasMicrotasks: boolean) => {
|
||||
this._hasPendingMicrotasks = hasMicrotasks;
|
||||
this._checkStable();
|
||||
},
|
||||
setMacrotask: (hasMacrotasks: boolean) => { this._hasPendingMacrotasks = hasMacrotasks; },
|
||||
onError: (error: NgZoneError) => this._onErrorEvents.emit(error)
|
||||
});
|
||||
}
|
||||
|
||||
private _checkStable() {
|
||||
if (this._nesting == 0) {
|
||||
if (!this._hasPendingMicrotasks && !this._isStable) {
|
||||
try {
|
||||
// console.log('ZONE.microtaskEmpty');
|
||||
this._nesting++;
|
||||
this._onMicrotaskEmpty.emit(null);
|
||||
} finally {
|
||||
this._nesting--;
|
||||
if (!this._hasPendingMicrotasks) {
|
||||
try {
|
||||
// console.log('ZONE.stable', this._nesting, this._isStable);
|
||||
this.runOutsideAngular(() => this._onStable.emit(null));
|
||||
} finally {
|
||||
this._isStable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Notifies when code enters Angular Zone. This gets fired first on VM Turn.
|
||||
*/
|
||||
get onUnstable(): EventEmitter<any> { return this._onUnstable; }
|
||||
|
||||
/**
|
||||
* Notifies when there is no more microtasks enqueue in the current VM Turn.
|
||||
* This is a hint for Angular to do change detection, which may enqueue more microtasks.
|
||||
* For this reason this event can fire multiple times per VM Turn.
|
||||
*/
|
||||
get onMicrotaskEmpty(): EventEmitter<any> { return this._onMicrotaskEmpty; }
|
||||
|
||||
/**
|
||||
* Notifies when the last `onMicrotaskEmpty` has run and there are no more microtasks, which
|
||||
* implies we are about to relinquish VM turn.
|
||||
* This event gets called just once.
|
||||
*/
|
||||
get onStable(): EventEmitter<any> { return this._onStable; }
|
||||
|
||||
/**
|
||||
* Notify that an error has been delivered.
|
||||
*/
|
||||
get onError(): EventEmitter<any> { return this._onErrorEvents; }
|
||||
|
||||
/**
|
||||
* Whether there are any outstanding microtasks.
|
||||
*/
|
||||
get hasPendingMicrotasks(): boolean { return this._hasPendingMicrotasks; }
|
||||
|
||||
/**
|
||||
* Whether there are any outstanding microtasks.
|
||||
*/
|
||||
get hasPendingMacrotasks(): boolean { return this._hasPendingMacrotasks; }
|
||||
|
||||
/**
|
||||
* Executes the `fn` function synchronously within the Angular zone and returns value returned by
|
||||
* the function.
|
||||
*
|
||||
* Running functions via `run` allows you to reenter Angular zone from a task that was executed
|
||||
* outside of the Angular zone (typically started via {@link #runOutsideAngular}).
|
||||
*
|
||||
* Any future tasks or microtasks scheduled from within this function will continue executing from
|
||||
* within the Angular zone.
|
||||
*
|
||||
* If a synchronous error happens it will be rethrown and not reported via `onError`.
|
||||
*/
|
||||
run(fn: () => any): any { return this._zoneImpl.runInner(fn); }
|
||||
|
||||
/**
|
||||
* Same as #run, except that synchronous errors are caught and forwarded
|
||||
* via `onError` and not rethrown.
|
||||
*/
|
||||
runGuarded(fn: () => any): any { return this._zoneImpl.runInnerGuarded(fn); }
|
||||
|
||||
/**
|
||||
* Executes the `fn` function synchronously in Angular's parent zone and returns value returned by
|
||||
* the function.
|
||||
*
|
||||
* Running functions via `runOutsideAngular` allows you to escape Angular's zone and do work that
|
||||
* doesn't trigger Angular change-detection or is subject to Angular's error handling.
|
||||
*
|
||||
* Any future tasks or microtasks scheduled from within this function will continue executing from
|
||||
* outside of the Angular zone.
|
||||
*
|
||||
* Use {@link #run} to reenter the Angular zone and do work that updates the application model.
|
||||
*/
|
||||
runOutsideAngular(fn: () => any): any { return this._zoneImpl.runOuter(fn); }
|
||||
}
|
226
modules/@angular/core/src/zone/ng_zone_impl.dart
Normal file
226
modules/@angular/core/src/zone/ng_zone_impl.dart
Normal file
@ -0,0 +1,226 @@
|
||||
library angular.zone;
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:stack_trace/stack_trace.dart' show Chain;
|
||||
|
||||
typedef void ZeroArgFunction();
|
||||
typedef void ErrorHandlingFn(error, stackTrace);
|
||||
|
||||
/**
|
||||
* A `Timer` wrapper that lets you specify additional functions to call when it
|
||||
* is cancelled.
|
||||
*/
|
||||
class WrappedTimer implements Timer {
|
||||
Timer _timer;
|
||||
ZeroArgFunction _onCancelCb;
|
||||
|
||||
WrappedTimer(Timer timer) {
|
||||
_timer = timer;
|
||||
}
|
||||
|
||||
void addOnCancelCb(ZeroArgFunction onCancelCb) {
|
||||
if (this._onCancelCb != null) {
|
||||
throw "On cancel cb already registered";
|
||||
}
|
||||
this._onCancelCb = onCancelCb;
|
||||
}
|
||||
|
||||
void cancel() {
|
||||
if (this._onCancelCb != null) {
|
||||
this._onCancelCb();
|
||||
}
|
||||
_timer.cancel();
|
||||
}
|
||||
|
||||
bool get isActive => _timer.isActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores error information; delivered via [NgZone.onError] stream.
|
||||
*/
|
||||
class NgZoneError {
|
||||
/// Error object thrown.
|
||||
final error;
|
||||
/// Either long or short chain of stack traces.
|
||||
final List stackTrace;
|
||||
NgZoneError(this.error, this.stackTrace);
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Zone` wrapper that lets you schedule tasks after its private microtask queue is exhausted but
|
||||
* before the next "VM turn", i.e. event loop iteration.
|
||||
*
|
||||
* This lets you freely schedule microtasks that prepare data, and set an {@link onMicrotaskEmpty} handler that
|
||||
* will consume that data after it's ready but before the browser has a chance to re-render.
|
||||
*
|
||||
* A VM turn consist of a single macrotask followed 0 to many microtasks.
|
||||
*
|
||||
* The wrapper maintains an "inner" and "mount" `Zone`. The application code will executes
|
||||
* in the "inner" zone unless `runOutsideAngular` is explicitely called.
|
||||
*
|
||||
* A typical application will create a singleton `NgZone`. The mount zone is the `Zone` where the singleton has been
|
||||
* instantiated. The default `onMicrotaskEmpty` runs the Angular change detection.
|
||||
*/
|
||||
class NgZoneImpl {
|
||||
static bool isInAngularZone() {
|
||||
return Zone.current['isAngularZone'] == true;
|
||||
}
|
||||
|
||||
// Number of microtasks pending from _innerZone (& descendants)
|
||||
int _pendingMicrotasks = 0;
|
||||
List<Timer> _pendingTimers = [];
|
||||
Function onEnter;
|
||||
Function onLeave;
|
||||
Function setMicrotask;
|
||||
Function setMacrotask;
|
||||
Function onError;
|
||||
|
||||
Zone _outerZone;
|
||||
Zone _innerZone;
|
||||
/**
|
||||
* Associates with this
|
||||
*
|
||||
* - a "mount" [Zone], which is a the one that instantiated this.
|
||||
* - an "inner" [Zone], which is a child of the mount [Zone].
|
||||
*
|
||||
* @param {bool} trace whether to enable long stack trace. They should only be
|
||||
* enabled in development mode as they significantly impact perf.
|
||||
*/
|
||||
NgZoneImpl({
|
||||
bool trace,
|
||||
Function this.onEnter,
|
||||
Function this.onLeave,
|
||||
Function this.setMicrotask,
|
||||
Function this.setMacrotask,
|
||||
Function this.onError
|
||||
}) {
|
||||
_outerZone = Zone.current;
|
||||
|
||||
if (trace) {
|
||||
_innerZone = Chain.capture(
|
||||
() => _createInnerZone(Zone.current),
|
||||
onError: _onErrorWithLongStackTrace
|
||||
);
|
||||
} else {
|
||||
_innerZone = _createInnerZone(
|
||||
Zone.current,
|
||||
handleUncaughtError: _onErrorWithoutLongStackTrace
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Zone _createInnerZone(Zone zone, {handleUncaughtError}) {
|
||||
return zone.fork(
|
||||
specification: new ZoneSpecification(
|
||||
scheduleMicrotask: _scheduleMicrotask,
|
||||
run: _run,
|
||||
runUnary: _runUnary,
|
||||
runBinary: _runBinary,
|
||||
handleUncaughtError: handleUncaughtError,
|
||||
createTimer: _createTimer),
|
||||
zoneValues: {'isAngularZone': true}
|
||||
);
|
||||
}
|
||||
|
||||
dynamic runInnerGuarded(fn()) {
|
||||
return _innerZone.runGuarded(fn);
|
||||
}
|
||||
|
||||
dynamic runInner(fn()) {
|
||||
return _innerZone.run(fn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs `fn` in the mount zone and returns whatever it returns.
|
||||
*
|
||||
* In a typical app where the inner zone is the Angular zone, this allows one to escape Angular's
|
||||
* auto-digest mechanism.
|
||||
*
|
||||
* ```
|
||||
* void myFunction(NgZone zone, Element element) {
|
||||
* element.onClick.listen(() {
|
||||
* // auto-digest will run after element click.
|
||||
* });
|
||||
* zone.runOutsideAngular(() {
|
||||
* element.onMouseMove.listen(() {
|
||||
* // auto-digest will NOT run after mouse move
|
||||
* });
|
||||
* });
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
dynamic runOuter(fn()) {
|
||||
return _outerZone.run(fn);
|
||||
}
|
||||
|
||||
dynamic _run(Zone self, ZoneDelegate parent, Zone zone, fn()) {
|
||||
try {
|
||||
onEnter();
|
||||
return parent.run(zone, fn);
|
||||
} finally {
|
||||
onLeave();
|
||||
}
|
||||
}
|
||||
|
||||
dynamic _runUnary(Zone self, ZoneDelegate parent, Zone zone, fn(arg), arg) =>
|
||||
_run(self, parent, zone, () => fn(arg));
|
||||
|
||||
dynamic _runBinary(Zone self, ZoneDelegate parent, Zone zone, fn(arg1, arg2),
|
||||
arg1, arg2) =>
|
||||
_run(self, parent, zone, () => fn(arg1, arg2));
|
||||
|
||||
void _scheduleMicrotask(Zone self, ZoneDelegate parent, Zone zone, fn) {
|
||||
if (_pendingMicrotasks == 0) {
|
||||
setMicrotask(true);
|
||||
}
|
||||
_pendingMicrotasks++;
|
||||
var microtask = () {
|
||||
try {
|
||||
fn();
|
||||
} finally {
|
||||
_pendingMicrotasks--;
|
||||
if (_pendingMicrotasks == 0) {
|
||||
setMicrotask(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
parent.scheduleMicrotask(zone, microtask);
|
||||
}
|
||||
|
||||
// Called by Chain.capture() on errors when long stack traces are enabled
|
||||
void _onErrorWithLongStackTrace(error, Chain chain) {
|
||||
final traces = chain.terse.traces.map((t) => t.toString()).toList();
|
||||
onError(new NgZoneError(error, traces));
|
||||
}
|
||||
|
||||
// Outer zone handleUnchaughtError when long stack traces are not used
|
||||
void _onErrorWithoutLongStackTrace(Zone self, ZoneDelegate parent, Zone zone,
|
||||
error, StackTrace trace)
|
||||
{
|
||||
onError(new NgZoneError(error, [trace.toString()]));
|
||||
}
|
||||
|
||||
Timer _createTimer(
|
||||
Zone self, ZoneDelegate parent, Zone zone, Duration duration, fn()) {
|
||||
WrappedTimer wrappedTimer;
|
||||
var cb = () {
|
||||
try {
|
||||
fn();
|
||||
} finally {
|
||||
_pendingTimers.remove(wrappedTimer);
|
||||
setMacrotask(_pendingTimers.isNotEmpty);
|
||||
}
|
||||
};
|
||||
Timer timer = parent.createTimer(zone, duration, cb);
|
||||
wrappedTimer = new WrappedTimer(timer);
|
||||
wrappedTimer.addOnCancelCb(() {
|
||||
_pendingTimers.remove(wrappedTimer);
|
||||
setMacrotask(_pendingTimers.isNotEmpty);
|
||||
});
|
||||
|
||||
_pendingTimers.add(wrappedTimer);
|
||||
setMacrotask(true);
|
||||
return wrappedTimer;
|
||||
}
|
||||
|
||||
}
|
100
modules/@angular/core/src/zone/ng_zone_impl.ts
Normal file
100
modules/@angular/core/src/zone/ng_zone_impl.ts
Normal file
@ -0,0 +1,100 @@
|
||||
import {global} from 'angular2/src/facade/lang';
|
||||
|
||||
/**
|
||||
* Stores error information; delivered via [NgZone.onError] stream.
|
||||
*/
|
||||
export class NgZoneError {
|
||||
constructor(public error: any, public stackTrace: any) {}
|
||||
}
|
||||
|
||||
|
||||
export class NgZoneImpl {
|
||||
static isInAngularZone(): boolean { return Zone.current.get('isAngularZone') === true; }
|
||||
|
||||
/** @internal */
|
||||
private outer: Zone;
|
||||
/** @internal */
|
||||
private inner: Zone;
|
||||
|
||||
private onEnter: () => void;
|
||||
private onLeave: () => void;
|
||||
private setMicrotask: (hasMicrotasks: boolean) => void;
|
||||
private setMacrotask: (hasMacrotasks: boolean) => void;
|
||||
private onError: (error: NgZoneError) => void;
|
||||
|
||||
constructor({trace, onEnter, onLeave, setMicrotask, setMacrotask, onError}: {
|
||||
trace: boolean,
|
||||
onEnter: () => void,
|
||||
onLeave: () => void,
|
||||
setMicrotask: (hasMicrotasks: boolean) => void,
|
||||
setMacrotask: (hasMacrotasks: boolean) => void,
|
||||
onError: (error: NgZoneError) => void
|
||||
}) {
|
||||
this.onEnter = onEnter;
|
||||
this.onLeave = onLeave;
|
||||
this.setMicrotask = setMicrotask;
|
||||
this.setMacrotask = setMacrotask;
|
||||
this.onError = onError;
|
||||
|
||||
if (Zone) {
|
||||
this.outer = this.inner = Zone.current;
|
||||
if (Zone['wtfZoneSpec']) {
|
||||
this.inner = this.inner.fork(Zone['wtfZoneSpec']);
|
||||
}
|
||||
if (trace && Zone['longStackTraceZoneSpec']) {
|
||||
this.inner = this.inner.fork(Zone['longStackTraceZoneSpec']);
|
||||
}
|
||||
this.inner = this.inner.fork({
|
||||
name: 'angular',
|
||||
properties:<any>{'isAngularZone': true},
|
||||
onInvokeTask: (delegate: ZoneDelegate, current: Zone, target: Zone, task: Task,
|
||||
applyThis: any, applyArgs: any): any => {
|
||||
try {
|
||||
this.onEnter();
|
||||
return delegate.invokeTask(target, task, applyThis, applyArgs);
|
||||
} finally {
|
||||
this.onLeave();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
onInvoke: (delegate: ZoneDelegate, current: Zone, target: Zone, callback: Function,
|
||||
applyThis: any, applyArgs: any[], source: string): any => {
|
||||
try {
|
||||
this.onEnter();
|
||||
return delegate.invoke(target, callback, applyThis, applyArgs, source);
|
||||
} finally {
|
||||
this.onLeave();
|
||||
}
|
||||
},
|
||||
|
||||
onHasTask:
|
||||
(delegate: ZoneDelegate, current: Zone, target: Zone, hasTaskState: HasTaskState) => {
|
||||
delegate.hasTask(target, hasTaskState);
|
||||
if (current == target) {
|
||||
// We are only interested in hasTask events which originate from our zone
|
||||
// (A child hasTask event is not interesting to us)
|
||||
if (hasTaskState.change == 'microTask') {
|
||||
this.setMicrotask(hasTaskState.microTask);
|
||||
} else if (hasTaskState.change == 'macroTask') {
|
||||
this.setMacrotask(hasTaskState.macroTask);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onHandleError: (delegate: ZoneDelegate, current: Zone, target: Zone, error: any):
|
||||
boolean => {
|
||||
delegate.handleError(target, error);
|
||||
this.onError(new NgZoneError(error, error.stack));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw new Error('Angular2 needs to be run with Zone.js polyfill.');
|
||||
}
|
||||
}
|
||||
|
||||
runInner(fn: () => any): any { return this.inner.run(fn); };
|
||||
runInnerGuarded(fn: () => any): any { return this.inner.runGuarded(fn); };
|
||||
runOuter(fn: () => any): any { return this.outer.run(fn); };
|
||||
}
|
Reference in New Issue
Block a user