diff --git a/modules/angular2/platform/worker_app.dart b/modules/angular2/platform/worker_app.dart index e2ff19ea65..8a2bfee74f 100644 --- a/modules/angular2/platform/worker_app.dart +++ b/modules/angular2/platform/worker_app.dart @@ -12,3 +12,5 @@ export 'package:angular2/src/web_workers/shared/service_message_broker.dart' show ReceivedMessage, ServiceMessageBroker, ServiceMessageBrokerFactory; export 'package:angular2/src/web_workers/shared/serializer.dart' show PRIMITIVE; export 'package:angular2/src/web_workers/shared/message_bus.dart'; +export 'package:angular2/src/web_workers/worker/router_providers.dart' + show WORKER_APP_ROUTER; diff --git a/modules/angular2/platform/worker_app.ts b/modules/angular2/platform/worker_app.ts index 7e6957be96..d3b255c430 100644 --- a/modules/angular2/platform/worker_app.ts +++ b/modules/angular2/platform/worker_app.ts @@ -8,12 +8,13 @@ export { ClientMessageBrokerFactory, FnArg, UiArguments -} from '../src/web_workers/shared/client_message_broker'; +} from 'angular2/src/web_workers/shared/client_message_broker'; export { ReceivedMessage, ServiceMessageBroker, ServiceMessageBrokerFactory -} from '../src/web_workers/shared/service_message_broker'; -export {PRIMITIVE} from '../src/web_workers/shared/serializer'; -export * from '../src/web_workers/shared/message_bus'; +} from 'angular2/src/web_workers/shared/service_message_broker'; +export {PRIMITIVE} from 'angular2/src/web_workers/shared/serializer'; +export * from 'angular2/src/web_workers/shared/message_bus'; export {AngularEntrypoint} from 'angular2/src/core/angular_entrypoint'; +export {WORKER_APP_ROUTER} from 'angular2/src/web_workers/worker/router_providers'; diff --git a/modules/angular2/platform/worker_render.dart b/modules/angular2/platform/worker_render.dart index aafab6599d..f19e8e0a94 100644 --- a/modules/angular2/platform/worker_render.dart +++ b/modules/angular2/platform/worker_render.dart @@ -18,7 +18,9 @@ export '../src/web_workers/shared/service_message_broker.dart' export '../src/web_workers/shared/serializer.dart' show PRIMITIVE; export '../src/web_workers/shared/message_bus.dart'; +export '../src/web_workers/ui/router_providers.dart' show WORKER_RENDER_ROUTER; import 'package:angular2/src/platform/worker_render_common.dart'; const WORKER_RENDER_APP = WORKER_RENDER_APPLICATION_COMMON; + diff --git a/modules/angular2/platform/worker_render.ts b/modules/angular2/platform/worker_render.ts index a601dbf253..b6b7f3ffa5 100644 --- a/modules/angular2/platform/worker_render.ts +++ b/modules/angular2/platform/worker_render.ts @@ -24,3 +24,4 @@ import {WORKER_RENDER_APPLICATION} from 'angular2/src/platform/worker_render'; * @deprecated Use WORKER_RENDER_APPLICATION */ export const WORKER_RENDER_APP = WORKER_RENDER_APPLICATION; +export {WORKER_RENDER_ROUTER} from 'angular2/src/web_workers/ui/router_providers'; diff --git a/modules/angular2/router.ts b/modules/angular2/router.ts index 4cea18950a..7d028817f5 100644 --- a/modules/angular2/router.ts +++ b/modules/angular2/router.ts @@ -20,18 +20,12 @@ export {OnActivate, OnDeactivate, OnReuse, CanDeactivate, CanReuse} from './src/ export {CanActivate} from './src/router/lifecycle_annotations'; export {Instruction, ComponentInstruction} from './src/router/instruction'; export {OpaqueToken} from 'angular2/core'; +export {ROUTER_PROVIDERS_COMMON} from 'angular2/src/router/router_providers_common'; +export {ROUTER_PROVIDERS, ROUTER_BINDINGS} from 'angular2/src/router/router_providers'; -import {PlatformLocation} from './src/router/platform_location'; -import {LocationStrategy} from './src/router/location_strategy'; -import {PathLocationStrategy} from './src/router/path_location_strategy'; -import {Router, RootRouter} from './src/router/router'; import {RouterOutlet} from './src/router/router_outlet'; import {RouterLink} from './src/router/router_link'; -import {RouteRegistry, ROUTER_PRIMARY_COMPONENT} from './src/router/route_registry'; -import {Location} from './src/router/location'; -import {ApplicationRef, provide, OpaqueToken, Provider} from 'angular2/core'; import {CONST_EXPR} from './src/facade/lang'; -import {BaseException} from 'angular2/src/facade/exceptions'; /** * A list of directives. To use the router directives like {@link RouterOutlet} and @@ -56,63 +50,3 @@ import {BaseException} from 'angular2/src/facade/exceptions'; * ``` */ export const ROUTER_DIRECTIVES: any[] = CONST_EXPR([RouterOutlet, RouterLink]); - -/** - * A list of {@link Provider}s. To use the router, you must add this to your application. - * - * ### Example ([live demo](http://plnkr.co/edit/iRUP8B5OUbxCWQ3AcIDm)) - * - * ``` - * import {Component} from 'angular2/core'; - * import { - * ROUTER_DIRECTIVES, - * ROUTER_PROVIDERS, - * RouteConfig - * } from 'angular2/router'; - * - * @Component({directives: [ROUTER_DIRECTIVES]}) - * @RouteConfig([ - * {...}, - * ]) - * class AppCmp { - * // ... - * } - * - * bootstrap(AppCmp, [ROUTER_PROVIDERS]); - * ``` - */ -export const ROUTER_PROVIDERS: any[] = CONST_EXPR([ - RouteRegistry, - CONST_EXPR(new Provider(LocationStrategy, {useClass: PathLocationStrategy})), - PlatformLocation, - Location, - CONST_EXPR(new Provider( - Router, - { - useFactory: routerFactory, - deps: CONST_EXPR([RouteRegistry, Location, ROUTER_PRIMARY_COMPONENT, ApplicationRef]) - })), - CONST_EXPR(new Provider( - ROUTER_PRIMARY_COMPONENT, - {useFactory: routerPrimaryComponentFactory, deps: CONST_EXPR([ApplicationRef])})) -]); - -/** - * Use {@link ROUTER_PROVIDERS} instead. - * - * @deprecated - */ -export const ROUTER_BINDINGS = ROUTER_PROVIDERS; - -function routerFactory(registry, location, primaryComponent, appRef) { - var rootRouter = new RootRouter(registry, location, primaryComponent); - appRef.registerDisposeListener(() => rootRouter.dispose()); - return rootRouter; -} - -function routerPrimaryComponentFactory(app) { - if (app.componentTypes.length == 0) { - throw new BaseException("Bootstrap at least one component before injecting Router."); - } - return app.componentTypes[0]; -} diff --git a/modules/angular2/src/platform/worker_render_common.ts b/modules/angular2/src/platform/worker_render_common.ts index 635796638c..9298081d4f 100644 --- a/modules/angular2/src/platform/worker_render_common.ts +++ b/modules/angular2/src/platform/worker_render_common.ts @@ -36,6 +36,7 @@ import {BrowserDomAdapter} from './browser/browser_adapter'; import {wtfInit} from 'angular2/src/core/profile/wtf_init'; import {MessageBasedRenderer} from 'angular2/src/web_workers/ui/renderer'; import {MessageBasedXHRImpl} from 'angular2/src/web_workers/ui/xhr_impl'; +import {BrowserPlatformLocation} from 'angular2/src/router/browser_platform_location'; import { ServiceMessageBrokerFactory, ServiceMessageBrokerFactory_ @@ -59,6 +60,13 @@ export const WORKER_RENDER_PLATFORM: Array = CO new Provider(PLATFORM_INITIALIZER, {useValue: initWebWorkerRenderPlatform, multi: true}) ]); +/** + * A list of {@link Provider}s. To use the router in a Worker enabled application you must + * include these providers when setting up the render thread. + */ +export const WORKER_RENDER_ROUTER: Array = + CONST_EXPR([BrowserPlatformLocation]); + export const WORKER_RENDER_APPLICATION_COMMON: Array = CONST_EXPR([ APPLICATION_COMMON_PROVIDERS, WORKER_RENDER_MESSAGING_PROVIDERS, diff --git a/modules/angular2/src/router/browser_platform_location.ts b/modules/angular2/src/router/browser_platform_location.ts new file mode 100644 index 0000000000..88917717fc --- /dev/null +++ b/modules/angular2/src/router/browser_platform_location.ts @@ -0,0 +1,58 @@ +import {Injectable} from 'angular2/core'; +import {History, Location} from 'angular2/src/facade/browser'; +import {UrlChangeListener} from './platform_location'; +import {PlatformLocation} from './platform_location'; +import {DOM} from 'angular2/src/platform/dom/dom_adapter'; + +/** + * `PlatformLocation` encapsulates all of the direct calls to platform APIs. + * This class should not be used directly by an application developer. Instead, use + * {@link Location}. + */ +@Injectable() +export class BrowserPlatformLocation extends PlatformLocation { + private _location: Location; + private _history: History; + + constructor() { + super(); + this._init(); + } + + // This is moved to its own method so that `MockPlatformLocationStrategy` can overwrite it + /** @internal */ + _init() { + this._location = DOM.getLocation(); + this._history = DOM.getHistory(); + } + + /** @internal */ + get location(): Location { return this._location; } + + getBaseHrefFromDOM(): string { return DOM.getBaseHref(); } + + onPopState(fn: UrlChangeListener): void { + DOM.getGlobalEventTarget('window').addEventListener('popstate', fn, false); + } + + onHashChange(fn: UrlChangeListener): void { + DOM.getGlobalEventTarget('window').addEventListener('hashchange', fn, false); + } + + get pathname(): string { return this._location.pathname; } + get search(): string { return this._location.search; } + get hash(): string { return this._location.hash; } + set pathname(newPath: string) { this._location.pathname = newPath; } + + pushState(state: any, title: string, url: string): void { + this._history.pushState(state, title, url); + } + + replaceState(state: any, title: string, url: string): void { + this._history.replaceState(state, title, url); + } + + forward(): void { this._history.forward(); } + + back(): void { this._history.back(); } +} diff --git a/modules/angular2/src/router/hash_location_strategy.ts b/modules/angular2/src/router/hash_location_strategy.ts index 68d01c4142..937d195d6c 100644 --- a/modules/angular2/src/router/hash_location_strategy.ts +++ b/modules/angular2/src/router/hash_location_strategy.ts @@ -5,7 +5,7 @@ import { APP_BASE_HREF, normalizeQueryParams } from './location_strategy'; -import {EventListener} from 'angular2/src/facade/browser'; +import {UrlChangeListener} from './platform_location'; import {isPresent} from 'angular2/src/facade/lang'; import {PlatformLocation} from './platform_location'; @@ -58,7 +58,7 @@ export class HashLocationStrategy extends LocationStrategy { } } - onPopState(fn: EventListener): void { + onPopState(fn: UrlChangeListener): void { this._platformLocation.onPopState(fn); this._platformLocation.onHashChange(fn); } diff --git a/modules/angular2/src/router/location_strategy.ts b/modules/angular2/src/router/location_strategy.ts index 186dfa19bb..42724833a5 100644 --- a/modules/angular2/src/router/location_strategy.ts +++ b/modules/angular2/src/router/location_strategy.ts @@ -1,5 +1,6 @@ import {CONST_EXPR} from 'angular2/src/facade/lang'; import {OpaqueToken} from 'angular2/core'; +import {UrlChangeListener} from './platform_location'; /** * `LocationStrategy` is responsible for representing and reading route state @@ -24,7 +25,7 @@ export abstract class LocationStrategy { abstract replaceState(state: any, title: string, url: string, queryParams: string): void; abstract forward(): void; abstract back(): void; - abstract onPopState(fn: (_: any) => any): void; + abstract onPopState(fn: UrlChangeListener): void; abstract getBaseHref(): string; } diff --git a/modules/angular2/src/router/path_location_strategy.ts b/modules/angular2/src/router/path_location_strategy.ts index 7dc68b3820..7d114d1403 100644 --- a/modules/angular2/src/router/path_location_strategy.ts +++ b/modules/angular2/src/router/path_location_strategy.ts @@ -1,5 +1,4 @@ import {Injectable, Inject, Optional} from 'angular2/core'; -import {EventListener, History, Location} from 'angular2/src/facade/browser'; import {isBlank} from 'angular2/src/facade/lang'; import {BaseException} from 'angular2/src/facade/exceptions'; import { @@ -8,7 +7,7 @@ import { normalizeQueryParams, joinWithSlash } from './location_strategy'; -import {PlatformLocation} from './platform_location'; +import {PlatformLocation, UrlChangeListener} from './platform_location'; /** * `PathLocationStrategy` is a {@link LocationStrategy} used to configure the @@ -75,7 +74,7 @@ export class PathLocationStrategy extends LocationStrategy { this._baseHref = href; } - onPopState(fn: EventListener): void { + onPopState(fn: UrlChangeListener): void { this._platformLocation.onPopState(fn); this._platformLocation.onHashChange(fn); } diff --git a/modules/angular2/src/router/platform_location.ts b/modules/angular2/src/router/platform_location.ts index c58ffb4e6e..c6ed880024 100644 --- a/modules/angular2/src/router/platform_location.ts +++ b/modules/angular2/src/router/platform_location.ts @@ -1,50 +1,48 @@ -import {DOM} from 'angular2/src/platform/dom/dom_adapter'; -import {Injectable} from 'angular2/core'; -import {EventListener, History, Location} from 'angular2/src/facade/browser'; - /** - * `PlatformLocation` encapsulates all of the direct calls to platform APIs. * This class should not be used directly by an application developer. Instead, use * {@link Location}. + * + * `PlatformLocation` encapsulates all calls to DOM apis, which allows the Router to be platform + * agnostic. + * This means that we can have different implementation of `PlatformLocation` for the different + * platforms + * that angular supports. For example, the default `PlatformLocation` is {@link + * BrowserPlatformLocation}, + * however when you run your app in a WebWorker you use {@link WebWorkerPlatformLocation}. + * + * The `PlatformLocation` class is used directly by all implementations of {@link LocationStrategy} + * when + * they need to interact with the DOM apis like pushState, popState, etc... + * + * {@link LocationStrategy} in turn is used by the {@link Location} service which is used directly + * by + * the {@link Router} in order to navigate between routes. Since all interactions between {@link + * Router} / + * {@link Location} / {@link LocationStrategy} and DOM apis flow through the `PlatformLocation` + * class + * they are all platform independent. */ -@Injectable() -export class PlatformLocation { - private _location: Location; - private _history: History; +export abstract class PlatformLocation { + abstract getBaseHrefFromDOM(): string; + abstract onPopState(fn: UrlChangeListener): void; + abstract onHashChange(fn: UrlChangeListener): void; - constructor() { this._init(); } + pathname: string; + search: string; + hash: string; - // This is moved to its own method so that `MockPlatformLocationStrategy` can overwrite it - /** @internal */ - _init() { - this._location = DOM.getLocation(); - this._history = DOM.getHistory(); - } + abstract replaceState(state: any, title: string, url: string): void; - getBaseHrefFromDOM(): string { return DOM.getBaseHref(); } + abstract pushState(state: any, title: string, url: string): void; - onPopState(fn: EventListener): void { - DOM.getGlobalEventTarget('window').addEventListener('popstate', fn, false); - } + abstract forward(): void; - onHashChange(fn: EventListener): void { - DOM.getGlobalEventTarget('window').addEventListener('hashchange', fn, false); - } - - get pathname(): string { return this._location.pathname; } - get search(): string { return this._location.search; } - get hash(): string { return this._location.hash; } - set pathname(newPath: string) { this._location.pathname = newPath; } - - pushState(state: any, title: string, url: string): void { - this._history.pushState(state, title, url); - } - - replaceState(state: any, title: string, url: string): void { - this._history.replaceState(state, title, url); - } - - forward(): void { this._history.forward(); } - - back(): void { this._history.back(); } + abstract back(): void; } + +/** + * A serializable version of the event from onPopState or onHashChange + */ +export interface UrlChangeEvent { type: string; } + +export interface UrlChangeListener { (e: UrlChangeEvent): any; } diff --git a/modules/angular2/src/router/router_providers.ts b/modules/angular2/src/router/router_providers.ts new file mode 100644 index 0000000000..4b50f16079 --- /dev/null +++ b/modules/angular2/src/router/router_providers.ts @@ -0,0 +1,42 @@ +// import {ROUTER_PROVIDERS_COMMON} from './router_providers_common'; +import {ROUTER_PROVIDERS_COMMON} from 'angular2/router'; +import {Provider} from 'angular2/core'; +import {CONST_EXPR} from 'angular2/src/facade/lang'; +import {BrowserPlatformLocation} from './browser_platform_location'; +import {PlatformLocation} from './platform_location'; + +/** + * A list of {@link Provider}s. To use the router, you must add this to your application. + * + * ### Example ([live demo](http://plnkr.co/edit/iRUP8B5OUbxCWQ3AcIDm)) + * + * ``` + * import {Component} from 'angular2/core'; + * import { + * ROUTER_DIRECTIVES, + * ROUTER_PROVIDERS, + * RouteConfig + * } from 'angular2/router'; + * + * @Component({directives: [ROUTER_DIRECTIVES]}) + * @RouteConfig([ + * {...}, + * ]) + * class AppCmp { + * // ... + * } + * + * bootstrap(AppCmp, [ROUTER_PROVIDERS]); + * ``` + */ +export const ROUTER_PROVIDERS: any[] = CONST_EXPR([ + ROUTER_PROVIDERS_COMMON, + CONST_EXPR(new Provider(PlatformLocation, {useClass: BrowserPlatformLocation})), +]); + +/** + * Use {@link ROUTER_PROVIDERS} instead. + * + * @deprecated + */ +export const ROUTER_BINDINGS = ROUTER_PROVIDERS; diff --git a/modules/angular2/src/router/router_providers_common.ts b/modules/angular2/src/router/router_providers_common.ts new file mode 100644 index 0000000000..823634b133 --- /dev/null +++ b/modules/angular2/src/router/router_providers_common.ts @@ -0,0 +1,40 @@ +import {LocationStrategy} from 'angular2/src/router/location_strategy'; +import {PathLocationStrategy} from 'angular2/src/router/path_location_strategy'; +import {Router, RootRouter} from 'angular2/src/router/router'; +import {RouteRegistry, ROUTER_PRIMARY_COMPONENT} from 'angular2/src/router/route_registry'; +import {Location} from 'angular2/src/router/location'; +import {CONST_EXPR, Type} from 'angular2/src/facade/lang'; +import {ApplicationRef, OpaqueToken, Provider} from 'angular2/core'; +import {BaseException} from 'angular2/src/facade/exceptions'; + +/** + * The Platform agnostic ROUTER PROVIDERS + */ +export const ROUTER_PROVIDERS_COMMON: any[] = CONST_EXPR([ + RouteRegistry, + CONST_EXPR(new Provider(LocationStrategy, {useClass: PathLocationStrategy})), + Location, + CONST_EXPR(new Provider( + Router, + { + useFactory: routerFactory, + deps: CONST_EXPR([RouteRegistry, Location, ROUTER_PRIMARY_COMPONENT, ApplicationRef]) + })), + CONST_EXPR(new Provider( + ROUTER_PRIMARY_COMPONENT, + {useFactory: routerPrimaryComponentFactory, deps: CONST_EXPR([ApplicationRef])})) +]); + +function routerFactory(registry: RouteRegistry, location: Location, primaryComponent: Type, + appRef: ApplicationRef): RootRouter { + var rootRouter = new RootRouter(registry, location, primaryComponent); + appRef.registerDisposeListener(() => rootRouter.dispose()); + return rootRouter; +} + +function routerPrimaryComponentFactory(app: ApplicationRef): Type { + if (app.componentTypes.length == 0) { + throw new BaseException("Bootstrap at least one component before injecting Router."); + } + return app.componentTypes[0]; +} diff --git a/modules/angular2/src/web_workers/shared/messaging_api.ts b/modules/angular2/src/web_workers/shared/messaging_api.ts index e84a6b03e1..b0207dddf0 100644 --- a/modules/angular2/src/web_workers/shared/messaging_api.ts +++ b/modules/angular2/src/web_workers/shared/messaging_api.ts @@ -4,4 +4,5 @@ */ export const RENDERER_CHANNEL = "ng-Renderer"; export const XHR_CHANNEL = "ng-XHR"; -export const EVENT_CHANNEL = "ng-events"; +export const EVENT_CHANNEL = "ng-Events"; +export const ROUTER_CHANNEL = "ng-Router"; diff --git a/modules/angular2/src/web_workers/shared/serialized_types.ts b/modules/angular2/src/web_workers/shared/serialized_types.ts new file mode 100644 index 0000000000..446537391f --- /dev/null +++ b/modules/angular2/src/web_workers/shared/serialized_types.ts @@ -0,0 +1,7 @@ +// This file contains interface versions of browser types that can be serialized to Plain Old +// JavaScript Objects +export class LocationType { + constructor(public href: string, public protocol: string, public host: string, + public hostname: string, public port: string, public pathname: string, + public search: string, public hash: string, public origin: string) {} +} diff --git a/modules/angular2/src/web_workers/shared/serializer.ts b/modules/angular2/src/web_workers/shared/serializer.ts index 7de8f39f9d..87c88c0c46 100644 --- a/modules/angular2/src/web_workers/shared/serializer.ts +++ b/modules/angular2/src/web_workers/shared/serializer.ts @@ -6,6 +6,7 @@ import {RenderComponentType} from "angular2/src/core/render/api"; import {Injectable} from "angular2/src/core/di"; import {RenderStore} from 'angular2/src/web_workers/shared/render_store'; import {ViewEncapsulation, VIEW_ENCAPSULATION_VALUES} from 'angular2/src/core/metadata/view'; +import {LocationType} from './serialized_types'; // PRIMITIVE is any type that does not need to be serialized (string, number, boolean) // We set it to String so that it is considered a Type. @@ -31,6 +32,8 @@ export class Serializer { return this._serializeRenderComponentType(obj); } else if (type === ViewEncapsulation) { return serializeEnum(obj); + } else if (type === LocationType) { + return this._serializeLocation(obj); } else { throw new BaseException("No serializer for " + type.toString()); } @@ -55,6 +58,8 @@ export class Serializer { return this._deserializeRenderComponentType(map); } else if (type === ViewEncapsulation) { return VIEW_ENCAPSULATION_VALUES[map]; + } else if (type === LocationType) { + return this._deserializeLocation(map); } else { throw new BaseException("No deserializer for " + type.toString()); } @@ -90,6 +95,25 @@ export class Serializer { } } + private _serializeLocation(loc: LocationType): Object { + return { + 'href': loc.href, + 'protocol': loc.protocol, + 'host': loc.host, + 'hostname': loc.hostname, + 'port': loc.port, + 'pathname': loc.pathname, + 'search': loc.search, + 'hash': loc.hash, + 'origin': loc.origin + }; + } + + private _deserializeLocation(loc: {[key: string]: any}): LocationType { + return new LocationType(loc['href'], loc['protocol'], loc['host'], loc['hostname'], loc['port'], + loc['pathname'], loc['search'], loc['hash'], loc['origin']); + } + private _serializeRenderComponentType(obj: RenderComponentType): Object { return { 'id': obj.id, @@ -106,4 +130,4 @@ export class Serializer { } -export class RenderStoreObject {} \ No newline at end of file +export class RenderStoreObject {} diff --git a/modules/angular2/src/web_workers/shared/service_message_broker.ts b/modules/angular2/src/web_workers/shared/service_message_broker.ts index 06976fb1b2..16b8501abc 100644 --- a/modules/angular2/src/web_workers/shared/service_message_broker.ts +++ b/modules/angular2/src/web_workers/shared/service_message_broker.ts @@ -50,11 +50,13 @@ export class ServiceMessageBroker_ extends ServiceMessageBroker { ObservableWrapper.subscribe(source, (message) => this._handleMessage(message)); } - registerMethod(methodName: string, signature: Type[], method: Function, returnType?: Type): void { + registerMethod(methodName: string, signature: Type[], method: (..._: any[]) => Promise| void, + returnType?: Type): void { this._methods.set(methodName, (message: ReceivedMessage) => { var serializedArgs = message.args; - var deserializedArgs: any[] = ListWrapper.createFixedSize(signature.length); - for (var i = 0; i < signature.length; i++) { + let numArgs = signature === null ? 0 : signature.length; + var deserializedArgs: any[] = ListWrapper.createFixedSize(numArgs); + for (var i = 0; i < numArgs; i++) { var serializedArg = serializedArgs[i]; deserializedArgs[i] = this._serializer.deserialize(serializedArg, signature[i]); } diff --git a/modules/angular2/src/web_workers/ui/platform_location.ts b/modules/angular2/src/web_workers/ui/platform_location.ts new file mode 100644 index 0000000000..21ceccd112 --- /dev/null +++ b/modules/angular2/src/web_workers/ui/platform_location.ts @@ -0,0 +1,54 @@ +import {BrowserPlatformLocation} from 'angular2/src/router/browser_platform_location'; +import {Injectable} from 'angular2/src/core/di'; +import {ROUTER_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api'; +import { + ServiceMessageBrokerFactory, + ServiceMessageBroker +} from 'angular2/src/web_workers/shared/service_message_broker'; +import {PRIMITIVE, Serializer} from 'angular2/src/web_workers/shared/serializer'; +import {bind} from './bind'; +import {LocationType} from 'angular2/src/web_workers/shared/serialized_types'; +import {MessageBus} from 'angular2/src/web_workers/shared/message_bus'; +import {Promise, EventEmitter, ObservableWrapper, PromiseWrapper} from 'angular2/src/facade/async'; +import {UrlChangeListener} from 'angular2/src/router/platform_location'; + +@Injectable() +export class MessageBasedPlatformLocation { + private _channelSink: EventEmitter; + private _broker: ServiceMessageBroker; + + constructor(private _brokerFactory: ServiceMessageBrokerFactory, + private _platformLocation: BrowserPlatformLocation, bus: MessageBus, + private _serializer: Serializer) { + this._platformLocation.onPopState(bind(this._sendUrlChangeEvent, this)); + this._platformLocation.onHashChange(bind(this._sendUrlChangeEvent, this)); + this._broker = this._brokerFactory.createMessageBroker(ROUTER_CHANNEL); + this._channelSink = bus.to(ROUTER_CHANNEL); + } + + start(): void { + this._broker.registerMethod("getLocation", null, bind(this._getLocation, this), LocationType); + this._broker.registerMethod("setPathname", [PRIMITIVE], bind(this._setPathname, this)); + this._broker.registerMethod("pushState", [PRIMITIVE, PRIMITIVE, PRIMITIVE], + bind(this._platformLocation.pushState, this._platformLocation)); + this._broker.registerMethod("replaceState", [PRIMITIVE, PRIMITIVE, PRIMITIVE], + bind(this._platformLocation.replaceState, this._platformLocation)); + this._broker.registerMethod("forward", null, + bind(this._platformLocation.forward, this._platformLocation)); + this._broker.registerMethod("back", null, + bind(this._platformLocation.back, this._platformLocation)); + } + + private _getLocation(): Promise { + return PromiseWrapper.resolve(this._platformLocation.location); + } + + + private _sendUrlChangeEvent(e: Event): void { + let loc = this._serializer.serialize(this._platformLocation.location, LocationType); + let serializedEvent = {'type': e.type}; + ObservableWrapper.callEmit(this._channelSink, {'event': serializedEvent, 'location': loc}); + } + + private _setPathname(pathname: string): void { this._platformLocation.pathname = pathname; } +} diff --git a/modules/angular2/src/web_workers/ui/router_providers.ts b/modules/angular2/src/web_workers/ui/router_providers.ts new file mode 100644 index 0000000000..21805ee2e0 --- /dev/null +++ b/modules/angular2/src/web_workers/ui/router_providers.ts @@ -0,0 +1,20 @@ +import {MessageBasedPlatformLocation} from './platform_location'; +import {CONST_EXPR} from 'angular2/src/facade/lang'; +import {BrowserPlatformLocation} from 'angular2/src/router/browser_platform_location'; +import {APP_INITIALIZER, Provider, Injector, NgZone} from 'angular2/core'; + +export const WORKER_RENDER_ROUTER = CONST_EXPR([ + MessageBasedPlatformLocation, + BrowserPlatformLocation, + CONST_EXPR( + new Provider(APP_INITIALIZER, + {useFactory: initRouterListeners, multi: true, deps: CONST_EXPR([Injector])})) +]); + +function initRouterListeners(injector: Injector): () => void { + return () => { + let zone = injector.get(NgZone); + + zone.run(() => injector.get(MessageBasedPlatformLocation).start()); + }; +} diff --git a/modules/angular2/src/web_workers/worker/platform_location.ts b/modules/angular2/src/web_workers/worker/platform_location.ts new file mode 100644 index 0000000000..6ada0ffc10 --- /dev/null +++ b/modules/angular2/src/web_workers/worker/platform_location.ts @@ -0,0 +1,136 @@ +import {Injectable} from 'angular2/src/core/di'; +import { + PlatformLocation, + UrlChangeEvent, + UrlChangeListener +} from 'angular2/src/router/platform_location'; +import { + FnArg, + UiArguments, + ClientMessageBroker, + ClientMessageBrokerFactory +} from 'angular2/src/web_workers/shared/client_message_broker'; +import {ROUTER_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api'; +import {LocationType} from 'angular2/src/web_workers/shared/serialized_types'; +import {Promise, PromiseWrapper, EventEmitter, ObservableWrapper} from 'angular2/src/facade/async'; +import {BaseException} from 'angular2/src/facade/exceptions'; +import {PRIMITIVE, Serializer} from 'angular2/src/web_workers/shared/serializer'; +import {MessageBus} from 'angular2/src/web_workers/shared/message_bus'; +import {StringMapWrapper} from 'angular2/src/facade/collection'; +import {StringWrapper} from 'angular2/src/facade/lang'; +import {deserializeGenericEvent} from './event_deserializer'; + +@Injectable() +export class WebWorkerPlatformLocation extends PlatformLocation { + private _broker: ClientMessageBroker; + private _popStateListeners: Array = []; + private _hashChangeListeners: Array = []; + private _location: LocationType = null; + private _channelSource: EventEmitter; + + constructor(brokerFactory: ClientMessageBrokerFactory, bus: MessageBus, + private _serializer: Serializer) { + super(); + this._broker = brokerFactory.createMessageBroker(ROUTER_CHANNEL); + + this._channelSource = bus.from(ROUTER_CHANNEL); + ObservableWrapper.subscribe(this._channelSource, (msg: {[key: string]: any}) => { + var listeners: Array = null; + if (StringMapWrapper.contains(msg, 'event')) { + let type: string = msg['event']['type']; + if (StringWrapper.equals(type, "popstate")) { + listeners = this._popStateListeners; + } else if (StringWrapper.equals(type, "hashchange")) { + listeners = this._hashChangeListeners; + } + + if (listeners !== null) { + let e = deserializeGenericEvent(msg['event']); + // There was a popState or hashChange event, so the location object thas been updated + this._location = this._serializer.deserialize(msg['location'], LocationType); + listeners.forEach((fn: Function) => fn(e)); + } + } + }); + } + + /** @internal **/ + init(): Promise { + var args: UiArguments = new UiArguments("getLocation"); + + var locationPromise: Promise = this._broker.runOnService(args, LocationType); + return PromiseWrapper.then(locationPromise, (val: LocationType): boolean => { + this._location = val; + return true; + }, (err): boolean => { throw new BaseException(err); }); + } + + getBaseHrefFromDOM(): string { + throw new BaseException( + "Attempt to get base href from DOM from WebWorker. You must either provide a value for the APP_BASE_HREF token through DI or use the hash location strategy."); + } + + onPopState(fn: UrlChangeListener): void { this._popStateListeners.push(fn); } + + onHashChange(fn: UrlChangeListener): void { this._hashChangeListeners.push(fn); } + + get pathname(): string { + if (this._location === null) { + return null; + } + + return this._location.pathname; + } + + get search(): string { + if (this._location === null) { + return null; + } + + return this._location.search; + } + + get hash(): string { + if (this._location === null) { + return null; + } + + return this._location.hash; + } + + set pathname(newPath: string) { + if (this._location === null) { + throw new BaseException("Attempt to set pathname before value is obtained from UI"); + } + + this._location.pathname = newPath; + + var fnArgs = [new FnArg(newPath, PRIMITIVE)]; + var args = new UiArguments("setPathname", fnArgs); + this._broker.runOnService(args, null); + } + + pushState(state: any, title: string, url: string): void { + var fnArgs = + [new FnArg(state, PRIMITIVE), new FnArg(title, PRIMITIVE), new FnArg(url, PRIMITIVE)]; + var args = new UiArguments("pushState", fnArgs); + this._broker.runOnService(args, null); + } + + replaceState(state: any, title: string, url: string): void { + var fnArgs = + [new FnArg(state, PRIMITIVE), new FnArg(title, PRIMITIVE), new FnArg(url, PRIMITIVE)]; + var args = new UiArguments("replaceState", fnArgs); + this._broker.runOnService(args, null); + } + + forward(): void { + var args = new UiArguments("forward"); + this._broker.runOnService(args, null); + } + + back(): void { + var args = new UiArguments("back"); + this._broker.runOnService(args, null); + } +} diff --git a/modules/angular2/src/web_workers/worker/router_providers.ts b/modules/angular2/src/web_workers/worker/router_providers.ts new file mode 100644 index 0000000000..c3cce5ea2e --- /dev/null +++ b/modules/angular2/src/web_workers/worker/router_providers.ts @@ -0,0 +1,21 @@ +import {ApplicationRef, Provider, NgZone, APP_INITIALIZER} from 'angular2/core'; +import {PlatformLocation} from 'angular2/src/router/platform_location'; +import {WebWorkerPlatformLocation} from './platform_location'; +import {ROUTER_PROVIDERS_COMMON} from 'angular2/src/router/router_providers_common'; +import {Promise} from 'angular2/src/facade/async'; + +export var WORKER_APP_ROUTER = [ + ROUTER_PROVIDERS_COMMON, + new Provider(PlatformLocation, {useClass: WebWorkerPlatformLocation}), + new Provider(APP_INITIALIZER, + { + useFactory: (platformLocation: WebWorkerPlatformLocation, zone: NgZone) => () => + initRouter(platformLocation, zone), + multi: true, + deps: [PlatformLocation, NgZone] + }) +]; + +function initRouter(platformLocation: WebWorkerPlatformLocation, zone: NgZone): Promise { + return zone.run(() => { return platformLocation.init(); }); +} diff --git a/modules/angular2/test/web_workers/debug_tools/bootstrap.server.spec.dart b/modules/angular2/test/web_workers/debug_tools/bootstrap.server.spec.dart index ec48b9b721..2d7138aa4f 100644 --- a/modules/angular2/test/web_workers/debug_tools/bootstrap.server.spec.dart +++ b/modules/angular2/test/web_workers/debug_tools/bootstrap.server.spec.dart @@ -4,7 +4,8 @@ import 'package:angular2/src/platform/server/html_adapter.dart'; import "package:angular2/testing_internal.dart"; import "package:angular2/src/core/reflection/reflection_capabilities.dart"; import "package:angular2/src/core/reflection/reflection.dart"; -import "package:angular2/src/platform/worker_app_common.dart" show WORKER_APP_APPLICATION_COMMON; +import "package:angular2/src/platform/worker_app_common.dart" + show WORKER_APP_APPLICATION_COMMON; import "package:angular2/platform/worker_app.dart" show WORKER_APP_PLATFORM; import "package:angular2/core.dart"; import "../shared/web_worker_test_util.dart"; @@ -19,7 +20,7 @@ main() { reflector.reflectionCapabilities = new ReflectionCapabilities(); var buses = createPairedMessageBuses(); platform([WORKER_APP_PLATFORM]) - .application([WORKER_APP_APPLICATION_COMMON]); + .application([WORKER_APP_APPLICATION_COMMON]); }); }); } diff --git a/modules/angular2/test/web_workers/debug_tools/multi_client_server_message_bus.server.spec.dart b/modules/angular2/test/web_workers/debug_tools/multi_client_server_message_bus.server.spec.dart index 5997d2cade..a20e0789c3 100644 --- a/modules/angular2/test/web_workers/debug_tools/multi_client_server_message_bus.server.spec.dart +++ b/modules/angular2/test/web_workers/debug_tools/multi_client_server_message_bus.server.spec.dart @@ -217,7 +217,9 @@ SpySocketWrapper createSocket({Function messageHandler}) { var socket = new SpyWebSocket(); if (messageHandler != null) { socket.spy("add").andCallFake(messageHandler); - socket.spy("addStream").andCallFake((Stream stream) => stream.listen(messageHandler)); + socket + .spy("addStream") + .andCallFake((Stream stream) => stream.listen(messageHandler)); } var controller = new StreamController.broadcast(); diff --git a/modules/angular2/test/web_workers/shared/web_worker_test_util.ts b/modules/angular2/test/web_workers/shared/web_worker_test_util.ts index 55d68c5b3e..eb598d01d9 100644 --- a/modules/angular2/test/web_workers/shared/web_worker_test_util.ts +++ b/modules/angular2/test/web_workers/shared/web_worker_test_util.ts @@ -1,9 +1,18 @@ import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection'; +import {PromiseWrapper} from 'angular2/src/facade/async'; +import {UiArguments} from 'angular2/src/web_workers/shared/client_message_broker'; +import {Type, isPresent} from 'angular2/src/facade/lang'; +import {SpyMessageBroker} from '../worker/spies'; +import {expect} from 'angular2/testing_internal'; import { MessageBusSink, MessageBusSource, MessageBus } from 'angular2/src/web_workers/shared/message_bus'; +import { + ClientMessageBroker, + ClientMessageBrokerFactory_ +} from 'angular2/src/web_workers/shared/client_message_broker'; import {MockEventEmitter} from './mock_event_emitter'; import {BaseException, WrappedException} from 'angular2/src/facade/exceptions'; import {NgZone} from 'angular2/src/core/zone/ng_zone'; @@ -25,6 +34,37 @@ export function createPairedMessageBuses(): PairedMessageBuses { new MockMessageBus(workerMessageBusSink, workerMessageBusSource)); } +/** + * Spies on the given {@link SpyMessageBroker} and expects a call with the given methodName + * andvalues. + * If a handler is provided it will be called to handle the request. + * Only intended to be called on a given broker instance once. + */ +export function expectBrokerCall(broker: SpyMessageBroker, methodName: string, vals?: Array, + handler?: (..._: any[]) => Promise| void): void { + broker.spy("runOnService") + .andCallFake((args: UiArguments, returnType: Type) => { + expect(args.method).toEqual(methodName); + if (isPresent(vals)) { + expect(args.args.length).toEqual(vals.length); + ListWrapper.forEachWithIndex(vals, (v, i) => {expect(v).toEqual(args.args[i].value)}); + } + var promise = null; + if (isPresent(handler)) { + let givenValues = args.args.map((arg) => {arg.value}); + if (givenValues.length > 0) { + promise = handler(givenValues); + } else { + promise = handler(); + } + } + if (promise == null) { + promise = PromiseWrapper.wrap(() => {}); + } + return promise; + }); +} + export class PairedMessageBuses { constructor(public ui: MessageBus, public worker: MessageBus) {} } @@ -85,3 +125,8 @@ export class MockMessageBus extends MessageBus { attachToZone(zone: NgZone) {} } + +export class MockMessageBrokerFactory extends ClientMessageBrokerFactory_ { + constructor(private _messageBroker: ClientMessageBroker) { super(null, null); } + createMessageBroker(channel: string, runInZone = true) { return this._messageBroker; } +} diff --git a/modules/angular2/test/web_workers/worker/platform_location_spec.ts b/modules/angular2/test/web_workers/worker/platform_location_spec.ts new file mode 100644 index 0000000000..62e06d9569 --- /dev/null +++ b/modules/angular2/test/web_workers/worker/platform_location_spec.ts @@ -0,0 +1,97 @@ +import { + AsyncTestCompleter, + inject, + describe, + it, + expect, + beforeEach, + beforeEachProviders +} from 'angular2/testing_internal'; +import {SpyMessageBroker} from './spies'; +import {WebWorkerPlatformLocation} from 'angular2/src/web_workers/worker/platform_location'; +import {LocationType} from 'angular2/src/web_workers/shared/serialized_types'; +import {MessageBus} from 'angular2/src/web_workers/shared/message_bus'; +import { + createPairedMessageBuses, + MockMessageBrokerFactory, + expectBrokerCall +} from '../shared/web_worker_test_util'; +import {UiArguments} from 'angular2/src/web_workers/shared/client_message_broker'; +import {Type} from 'angular2/src/facade/lang'; +import {PromiseWrapper} from "angular2/src/facade/async"; +import {CONST_EXPR} from 'angular2/src/facade/lang'; + +export function main() { + describe("WebWorkerPlatformLocation", () => { + var uiBus: MessageBus = null; + var workerBus: MessageBus = null; + var broker: any = null; + var TEST_LOCATION = + new LocationType("http://www.example.com", "http", "example.com", "example.com", "80", "/", + "", "", "http://www.example.com"); + + + function createWebWorkerPlatformLocation(loc: LocationType): WebWorkerPlatformLocation { + broker.spy("runOnService") + .andCallFake((args: UiArguments, returnType: Type) => { + if (args.method === 'getLocation') { + return PromiseWrapper.resolve(loc); + } + }); + var factory = new MockMessageBrokerFactory(broker); + return new WebWorkerPlatformLocation(factory, workerBus, null); + } + + function testPushOrReplaceState(pushState: boolean) { + let platformLocation = createWebWorkerPlatformLocation(null); + const TITLE = "foo"; + const URL = "http://www.example.com/foo"; + expectBrokerCall(broker, pushState ? "pushState" : "replaceState", [null, TITLE, URL]); + if (pushState) { + platformLocation.pushState(null, TITLE, URL); + } else { + platformLocation.replaceState(null, TITLE, URL); + } + } + + beforeEach(() => { + var buses = createPairedMessageBuses(); + uiBus = buses.ui; + workerBus = buses.worker; + workerBus.initChannel("ng-Router"); + uiBus.initChannel("ng-Router"); + broker = new SpyMessageBroker(); + }); + + it("should throw if getBaseHrefFromDOM is called", () => { + let platformLocation = createWebWorkerPlatformLocation(null); + expect(() => platformLocation.getBaseHrefFromDOM()).toThrowError(); + }); + + it("should get location on init", () => { + let platformLocation = createWebWorkerPlatformLocation(null); + expectBrokerCall(broker, "getLocation"); + platformLocation.init(); + }); + + it("should throw if set pathname is called before init finishes", () => { + let platformLocation = createWebWorkerPlatformLocation(null); + platformLocation.init(); + expect(() => platformLocation.pathname = "TEST").toThrowError(); + }); + + it("should send pathname to render thread", inject([AsyncTestCompleter], (async) => { + let platformLocation = createWebWorkerPlatformLocation(TEST_LOCATION); + platformLocation.init().then((_) => { + let PATHNAME = "/test"; + expectBrokerCall(broker, "setPathname", [PATHNAME]); + platformLocation.pathname = PATHNAME; + async.done(); + }); + })); + + it("should send pushState to render thread", () => { testPushOrReplaceState(true); }); + + it("should send replaceState to render thread", () => { testPushOrReplaceState(false); }); + }); +} diff --git a/modules/angular2/test/web_workers/worker/xhr_impl_spec.ts b/modules/angular2/test/web_workers/worker/xhr_impl_spec.ts index dfb68279af..9b6ba77c2e 100644 --- a/modules/angular2/test/web_workers/worker/xhr_impl_spec.ts +++ b/modules/angular2/test/web_workers/worker/xhr_impl_spec.ts @@ -8,15 +8,9 @@ import { beforeEachProviders } from 'angular2/testing_internal'; import {SpyMessageBroker} from './spies'; -import {Type} from 'angular2/src/facade/lang'; -import { - ClientMessageBroker, - UiArguments, - ClientMessageBrokerFactory, - ClientMessageBrokerFactory_ -} from 'angular2/src/web_workers/shared/client_message_broker'; import {WebWorkerXHRImpl} from "angular2/src/web_workers/worker/xhr_impl"; import {PromiseWrapper} from "angular2/src/facade/async"; +import {MockMessageBrokerFactory, expectBrokerCall} from "../shared/web_worker_test_util"; export function main() { describe("WebWorkerXHRImpl", () => { @@ -25,16 +19,10 @@ export function main() { const URL = "http://www.example.com/test"; const RESPONSE = "Example response text"; - var messageBroker: any = new SpyMessageBroker(); - messageBroker.spy("runOnService") - .andCallFake((args: UiArguments, returnType: Type) => { - expect(args.method).toEqual("get"); - expect(args.args.length).toEqual(1); - expect(args.args[0].value).toEqual(URL); - return PromiseWrapper.wrap(() => { return RESPONSE; }); - }); - - var xhrImpl = new WebWorkerXHRImpl(new MockMessageBrokerFactory(messageBroker)); + var messageBroker = new SpyMessageBroker(); + expectBrokerCall(messageBroker, "get", [URL], + (_) => { return PromiseWrapper.wrap(() => { return RESPONSE; }); }); + var xhrImpl = new WebWorkerXHRImpl(new MockMessageBrokerFactory(messageBroker)); xhrImpl.get(URL).then((response) => { expect(response).toEqual(RESPONSE); async.done(); @@ -42,8 +30,3 @@ export function main() { })); }); } - -class MockMessageBrokerFactory extends ClientMessageBrokerFactory_ { - constructor(private _messageBroker: ClientMessageBroker) { super(null, null); } - createMessageBroker(channel: string, runInZone = true) { return this._messageBroker; } -} diff --git a/modules/playground/e2e_test/web_workers/router/router_spec.dart b/modules/playground/e2e_test/web_workers/router/router_spec.dart new file mode 100644 index 0000000000..87b2b59cb0 --- /dev/null +++ b/modules/playground/e2e_test/web_workers/router/router_spec.dart @@ -0,0 +1,3 @@ +library playground.e2e_test.web_workers.router.router_spec; + +main() {} diff --git a/modules/playground/e2e_test/web_workers/router/router_spec.ts b/modules/playground/e2e_test/web_workers/router/router_spec.ts new file mode 100644 index 0000000000..eaea238211 --- /dev/null +++ b/modules/playground/e2e_test/web_workers/router/router_spec.ts @@ -0,0 +1,72 @@ +import {verifyNoBrowserErrors} from 'angular2/src/testing/e2e_util'; + +describe("WebWorker Router", () => { + afterEach(() => { + verifyNoBrowserErrors(); + browser.ignoreSynchronization = false; + }); + + let contentSelector = "app main h1"; + let navSelector = "app nav ul"; + var baseUrl = "playground/src/web_workers/router/index.html"; + + it("should route on click", () => { + // This test can't wait for Angular 2 as Testability is not available when using WebWorker + browser.ignoreSynchronization = true; + browser.get(baseUrl); + + waitForElement(contentSelector); + var content = element(by.css(contentSelector)); + expect(content.getText()).toEqual("Start"); + + let aboutBtn = element(by.css(navSelector + " .about")); + aboutBtn.click(); + waitForUrl(/\/about/); + waitForElement(contentSelector); + content = element(by.css(contentSelector)); + waitForElementText(content, "About"); + expect(content.getText()).toEqual("About"); + expect(browser.getCurrentUrl()).toMatch(/\/about/); + + let contactBtn = element(by.css(navSelector + " .contact")); + contactBtn.click(); + waitForUrl(/\/contact/); + waitForElement(contentSelector); + content = element(by.css(contentSelector)); + waitForElementText(content, "Contact"); + expect(content.getText()).toEqual("Contact"); + expect(browser.getCurrentUrl()).toMatch(/\/contact/); + }); + + it("should load the correct route from the URL", () => { + // This test can't wait for Angular 2 as Testability is not available when using WebWorker + browser.ignoreSynchronization = true; + browser.get(baseUrl + "#/about"); + + waitForElement(contentSelector); + let content = element(by.css(contentSelector)); + waitForElementText(content, "About"); + expect(content.getText()).toEqual("About"); + }); + + function waitForElement(selector: string): void { + browser.wait(protractor.until.elementLocated(by.css(selector)), 15000); + } + + function waitForElementText(elem: protractor.ElementFinder, expected: string): void { + browser.wait(() => { + let deferred = protractor.promise.defer(); + elem.getText().then((text) => { return deferred.fulfill(text === expected); }); + return deferred.promise; + }, 5000); + } + + function waitForUrl(regex): void { + browser.wait(() => { + let deferred = protractor.promise.defer(); + browser.getCurrentUrl().then( + (url) => { return deferred.fulfill(url.match(regex) !== null); }); + return deferred.promise; + }, 5000); + } +}); diff --git a/modules/playground/pubspec.yaml b/modules/playground/pubspec.yaml index a75ad05ac0..c34cfb9017 100644 --- a/modules/playground/pubspec.yaml +++ b/modules/playground/pubspec.yaml @@ -50,6 +50,8 @@ transformers: - web/src/web_workers/todo/background_index.dart - web/src/web_workers/todo/index.dart - web/src/web_workers/todo/server_index.dart + - web/src/web_workers/router/index.dart + - web/src/web_workers/router/background_index.dart - web/src/zippy_component/index.dart - $dart2js: diff --git a/modules/playground/src/web_workers/images/background_index.dart b/modules/playground/src/web_workers/images/background_index.dart index a27476af7c..a911d188ac 100644 --- a/modules/playground/src/web_workers/images/background_index.dart +++ b/modules/playground/src/web_workers/images/background_index.dart @@ -9,7 +9,8 @@ import "package:angular2/src/core/reflection/reflection.dart"; main(List args, SendPort replyTo) { reflector.reflectionCapabilities = new ReflectionCapabilities(); - platform([WORKER_APP_PLATFORM, new Provider(RENDER_SEND_PORT, useValue: replyTo)]) - .application([WORKER_APP_APPLICATION]) - .bootstrap(ImageDemo); + platform([ + WORKER_APP_PLATFORM, + new Provider(RENDER_SEND_PORT, useValue: replyTo) + ]).application([WORKER_APP_APPLICATION]).bootstrap(ImageDemo); } diff --git a/modules/playground/src/web_workers/router/app.html b/modules/playground/src/web_workers/router/app.html new file mode 100644 index 0000000000..a96a63606f --- /dev/null +++ b/modules/playground/src/web_workers/router/app.html @@ -0,0 +1,11 @@ + +
+ +
+ diff --git a/modules/playground/src/web_workers/router/background_index.dart b/modules/playground/src/web_workers/router/background_index.dart new file mode 100644 index 0000000000..ce4530dfa9 --- /dev/null +++ b/modules/playground/src/web_workers/router/background_index.dart @@ -0,0 +1,20 @@ +library playground.src.web_workers.router.background_index; + +import "index_common.dart" show App; +import "dart:isolate"; +import "package:angular2/platform/worker_app.dart"; +import "package:angular2/core.dart"; +import "package:angular2/src/web_workers/worker/router_providers.dart"; +import "package:angular2/router.dart"; + +@AngularEntrypoint() +main(List args, SendPort replyTo) { + platform([ + WORKER_APP_PLATFORM, + new Provider(RENDER_SEND_PORT, useValue: replyTo) + ]).asyncApplication(null, [ + WORKER_APP_APPLICATION, + WORKER_APP_ROUTER, + new Provider(LocationStrategy, useClass: HashLocationStrategy) + ]).then((ref) => ref.bootstrap(App)); +} diff --git a/modules/playground/src/web_workers/router/background_index.ts b/modules/playground/src/web_workers/router/background_index.ts new file mode 100644 index 0000000000..e9fe6157be --- /dev/null +++ b/modules/playground/src/web_workers/router/background_index.ts @@ -0,0 +1,18 @@ +import {platform, Provider, NgZone} from "angular2/core"; +import { + WORKER_APP_PLATFORM, + WORKER_APP_APPLICATION, + WORKER_APP_ROUTER +} from "angular2/platform/worker_app"; +import {App} from "./index_common"; +import {HashLocationStrategy, LocationStrategy} from "angular2/router"; + +export function main() { + let refPromise = platform([WORKER_APP_PLATFORM]) + .asyncApplication(null, [ + WORKER_APP_APPLICATION, + WORKER_APP_ROUTER, + new Provider(LocationStrategy, {useClass: HashLocationStrategy}) + ]); + refPromise.then((ref) => ref.bootstrap(App)); +} diff --git a/modules/playground/src/web_workers/router/components/about.ts b/modules/playground/src/web_workers/router/components/about.ts new file mode 100644 index 0000000000..021ab8b5c0 --- /dev/null +++ b/modules/playground/src/web_workers/router/components/about.ts @@ -0,0 +1,4 @@ +import {Component} from 'angular2/core'; +@Component({selector: 'about', template: '

About

'}) +export class About { +} \ No newline at end of file diff --git a/modules/playground/src/web_workers/router/components/contact.ts b/modules/playground/src/web_workers/router/components/contact.ts new file mode 100644 index 0000000000..f37b1937a3 --- /dev/null +++ b/modules/playground/src/web_workers/router/components/contact.ts @@ -0,0 +1,4 @@ +import {Component} from 'angular2/core'; +@Component({selector: 'contact', template: '

Contact

'}) +export class Contact { +} \ No newline at end of file diff --git a/modules/playground/src/web_workers/router/components/start.ts b/modules/playground/src/web_workers/router/components/start.ts new file mode 100644 index 0000000000..1703fcc7f7 --- /dev/null +++ b/modules/playground/src/web_workers/router/components/start.ts @@ -0,0 +1,4 @@ +import {Component} from 'angular2/core'; +@Component({selector: 'start', template: '

Start

'}) +export class Start { +} \ No newline at end of file diff --git a/modules/playground/src/web_workers/router/index.dart b/modules/playground/src/web_workers/router/index.dart new file mode 100644 index 0000000000..84b34c9819 --- /dev/null +++ b/modules/playground/src/web_workers/router/index.dart @@ -0,0 +1,13 @@ +library angular2.examples.web_workers.router.index; + +import "package:angular2/platform/worker_render.dart"; +import "package:angular2/core.dart"; +import "package:angular2/src/core/reflection/reflection_capabilities.dart"; +import "package:angular2/src/core/reflection/reflection.dart"; + +@AngularEntrypoint() +main() { + reflector.reflectionCapabilities = new ReflectionCapabilities(); + platform([WORKER_RENDER_PLATFORM]).asyncApplication( + initIsolate("background_index.dart"), [WORKER_RENDER_ROUTER]); +} diff --git a/modules/playground/src/web_workers/router/index.html b/modules/playground/src/web_workers/router/index.html new file mode 100644 index 0000000000..df9f7a9a3c --- /dev/null +++ b/modules/playground/src/web_workers/router/index.html @@ -0,0 +1,8 @@ + + + Web Worker Router Example + + + $SCRIPTS$ + + diff --git a/modules/playground/src/web_workers/router/index.ts b/modules/playground/src/web_workers/router/index.ts new file mode 100644 index 0000000000..b9af0a442c --- /dev/null +++ b/modules/playground/src/web_workers/router/index.ts @@ -0,0 +1,16 @@ +import {platform, Provider} from 'angular2/core'; +import { + WORKER_RENDER_APP, + WORKER_RENDER_PLATFORM, + WORKER_SCRIPT, + WORKER_RENDER_ROUTER +} from 'angular2/platform/worker_render'; +import {BrowserPlatformLocation} from "angular2/src/router/browser_platform_location"; +import {MessageBasedPlatformLocation} from "angular2/src/web_workers/ui/platform_location"; + +let ref = platform([WORKER_RENDER_PLATFORM]) + .application([ + WORKER_RENDER_APP, + new Provider(WORKER_SCRIPT, {useValue: "loader.js"}), + WORKER_RENDER_ROUTER + ]); \ No newline at end of file diff --git a/modules/playground/src/web_workers/router/index_common.ts b/modules/playground/src/web_workers/router/index_common.ts new file mode 100644 index 0000000000..c19ff3b2cd --- /dev/null +++ b/modules/playground/src/web_workers/router/index_common.ts @@ -0,0 +1,14 @@ +import {Component, View} from 'angular2/core'; +import {Start} from './components/start'; +import {About} from './components/about'; +import {Contact} from './components/contact'; +import {ROUTER_DIRECTIVES, RouteConfig, Route} from 'angular2/router'; + +@Component({selector: 'app', directives: [ROUTER_DIRECTIVES], templateUrl: 'app.html'}) +@RouteConfig([ + new Route({path: '/', component: Start, name: "Start"}), + new Route({path: '/contact', component: Contact, name: "Contact"}), + new Route({path: '/about', component: About, name: "About"}) +]) +export class App { +} diff --git a/modules/playground/src/web_workers/router/loader.js b/modules/playground/src/web_workers/router/loader.js new file mode 100644 index 0000000000..d4a24d0dd7 --- /dev/null +++ b/modules/playground/src/web_workers/router/loader.js @@ -0,0 +1,17 @@ +$SCRIPTS$ + +System.config({ + baseURL: '/', + defaultJSExtensions: true +}); + +System.import("playground/src/web_workers/router/background_index") + .then( + function(m) { + try { + m.main(); + } catch (e) { + console.error(e); + } + }, + function(error) { console.error("error loading background", error); }); diff --git a/modules/playground/src/web_workers/todo/index_web_socket.dart b/modules/playground/src/web_workers/todo/index_web_socket.dart index ea1418e523..882af0d3f0 100644 --- a/modules/playground/src/web_workers/todo/index_web_socket.dart +++ b/modules/playground/src/web_workers/todo/index_web_socket.dart @@ -10,7 +10,6 @@ main() { var webSocket = new WebSocket("ws://127.0.0.1:1337/ws"); webSocket.onOpen.listen((e) { var bus = new WebSocketMessageBus.fromWebSocket(webSocket); - platform([WORKER_RENDER_PLATFORM]) .application([WORKER_RENDER_APPLICATION_COMMON, new Provider(MessageBus, useValue: bus), new Provider(APP_INITIALIZER, diff --git a/tools/broccoli/trees/browser_tree.ts b/tools/broccoli/trees/browser_tree.ts index ea17be1854..46e588f6b0 100644 --- a/tools/broccoli/trees/browser_tree.ts +++ b/tools/broccoli/trees/browser_tree.ts @@ -68,7 +68,8 @@ const kServedPaths = [ 'playground/src/web_workers/kitchen_sink', 'playground/src/web_workers/todo', 'playground/src/web_workers/images', - 'playground/src/web_workers/message_broker' + 'playground/src/web_workers/message_broker', + 'playground/src/web_workers/router' ];