feat(WebWorkers): Add WebSocket MessageBuses for debugging apps
Closes #3858
This commit is contained in:
parent
9f576b0233
commit
4ba4427510
|
@ -47,7 +47,7 @@ import {
|
|||
import {QueryList} from './query_list';
|
||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||
import {RenderDirectiveMetadata} from 'angular2/src/core/render/api';
|
||||
import {EventConfig} from 'angular2/src/core/render/dom/util';
|
||||
import {EventConfig} from 'angular2/src/core/render/event_config';
|
||||
import {PipeBinding} from '../pipes/pipe_binding';
|
||||
|
||||
var _staticKeys;
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
import {Injectable} from 'angular2/di';
|
||||
|
||||
import {List, ListWrapper, MapWrapper} from 'angular2/src/core/facade/collection';
|
||||
import {isPresent, isBlank, BaseException, assertionsEnabled} from 'angular2/src/core/facade/lang';
|
||||
import {
|
||||
StringWrapper,
|
||||
isPresent,
|
||||
isBlank,
|
||||
BaseException,
|
||||
assertionsEnabled
|
||||
} from 'angular2/src/core/facade/lang';
|
||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||
|
||||
import {
|
||||
|
@ -505,7 +511,7 @@ function _findDirectiveIndexByExportAs(renderElementBinder, directiveBindings, e
|
|||
}
|
||||
}
|
||||
|
||||
if (isBlank(matchedDirective) && exportAs !== "$implicit") {
|
||||
if (isBlank(matchedDirective) && !StringWrapper.equals(exportAs, "$implicit")) {
|
||||
throw new BaseException(`Cannot find directive with exportAs = '${exportAs}'`);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,8 @@ import {CompileElement} from './compile_element';
|
|||
import {CompileControl} from './compile_control';
|
||||
|
||||
import {RenderDirectiveMetadata} from '../../api';
|
||||
import {EventConfig, dashCaseToCamelCase, camelCaseToDashCase} from '../util';
|
||||
import {dashCaseToCamelCase, camelCaseToDashCase} from '../util';
|
||||
import {EventConfig} from '../../event_config';
|
||||
import {DirectiveBuilder, ElementBinderBuilder} from '../view/proto_view_builder';
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,8 +8,6 @@ import {TemplateCloner} from './template_cloner';
|
|||
export const NG_BINDING_CLASS_SELECTOR = '.ng-binding';
|
||||
export const NG_BINDING_CLASS = 'ng-binding';
|
||||
|
||||
export const EVENT_TARGET_SEPARATOR = ':';
|
||||
|
||||
export const NG_CONTENT_ELEMENT_NAME = 'ng-content';
|
||||
export const NG_SHADOW_ROOT_ELEMENT_NAME = 'shadow-root';
|
||||
|
||||
|
@ -29,27 +27,6 @@ export function dashCaseToCamelCase(input: string): string {
|
|||
(m) => { return m[1].toUpperCase(); });
|
||||
}
|
||||
|
||||
export class EventConfig {
|
||||
constructor(public fieldName: string, public eventName: string, public isLongForm: boolean) {}
|
||||
|
||||
static parse(eventConfig: string): EventConfig {
|
||||
var fieldName = eventConfig, eventName = eventConfig, isLongForm = false;
|
||||
var separatorIdx = eventConfig.indexOf(EVENT_TARGET_SEPARATOR);
|
||||
if (separatorIdx > -1) {
|
||||
// long format: 'fieldName: eventName'
|
||||
fieldName = StringWrapper.substring(eventConfig, 0, separatorIdx).trim();
|
||||
eventName = StringWrapper.substring(eventConfig, separatorIdx + 1).trim();
|
||||
isLongForm = true;
|
||||
}
|
||||
return new EventConfig(fieldName, eventName, isLongForm);
|
||||
}
|
||||
|
||||
getFullName(): string {
|
||||
return this.isLongForm ? `${this.fieldName}${EVENT_TARGET_SEPARATOR}${this.eventName}` :
|
||||
this.eventName;
|
||||
}
|
||||
}
|
||||
|
||||
// Attention: This is on the hot path, so don't use closures or default values!
|
||||
export function queryBoundElements(templateContent: Node, isSingleElementChild: boolean):
|
||||
Element[] {
|
||||
|
|
|
@ -34,12 +34,8 @@ import {
|
|||
PropertyBindingType
|
||||
} from '../../api';
|
||||
|
||||
import {
|
||||
NG_BINDING_CLASS,
|
||||
EVENT_TARGET_SEPARATOR,
|
||||
queryBoundTextNodeIndices,
|
||||
camelCaseToDashCase
|
||||
} from '../util';
|
||||
import {NG_BINDING_CLASS, queryBoundTextNodeIndices, camelCaseToDashCase} from '../util';
|
||||
import {EVENT_TARGET_SEPARATOR} from "../../event_config";
|
||||
|
||||
export class ProtoViewBuilder {
|
||||
variableBindings: Map<string, string> = new Map();
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
import {StringWrapper} from 'angular2/src/core/facade/lang';
|
||||
export const EVENT_TARGET_SEPARATOR = ':';
|
||||
|
||||
export class EventConfig {
|
||||
constructor(public fieldName: string, public eventName: string, public isLongForm: boolean) {}
|
||||
|
||||
static parse(eventConfig: string): EventConfig {
|
||||
var fieldName = eventConfig, eventName = eventConfig, isLongForm = false;
|
||||
var separatorIdx = eventConfig.indexOf(EVENT_TARGET_SEPARATOR);
|
||||
if (separatorIdx > -1) {
|
||||
// long format: 'fieldName: eventName'
|
||||
fieldName = StringWrapper.substring(eventConfig, 0, separatorIdx).trim();
|
||||
eventName = StringWrapper.substring(eventConfig, separatorIdx + 1).trim();
|
||||
isLongForm = true;
|
||||
}
|
||||
return new EventConfig(fieldName, eventName, isLongForm);
|
||||
}
|
||||
|
||||
getFullName(): string {
|
||||
return this.isLongForm ? `${this.fieldName}${EVENT_TARGET_SEPARATOR}${this.eventName}` :
|
||||
this.eventName;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,216 @@
|
|||
library angular2.src.web_workers.debug_tools.multi_client_server_message_bus;
|
||||
|
||||
import "package:angular2/src/web_workers/shared/message_bus.dart"
|
||||
show MessageBus, MessageBusSink, MessageBusSource;
|
||||
import 'dart:io';
|
||||
import 'dart:convert' show JSON;
|
||||
import 'dart:async';
|
||||
import 'package:angular2/src/core/facade/async.dart' show EventEmitter;
|
||||
import 'package:angular2/src/web_workers/shared/messaging_api.dart';
|
||||
|
||||
// TODO(jteplitz602): Remove hard coded result type and
|
||||
// clear messageHistory once app is done with it #3859
|
||||
class MultiClientServerMessageBus implements MessageBus {
|
||||
final MultiClientServerMessageBusSink sink;
|
||||
MultiClientServerMessageBusSource source;
|
||||
bool hasPrimary = false;
|
||||
|
||||
MultiClientServerMessageBus(this.sink, this.source);
|
||||
|
||||
MultiClientServerMessageBus.fromHttpServer(HttpServer server)
|
||||
: sink = new MultiClientServerMessageBusSink() {
|
||||
source = new MultiClientServerMessageBusSource(resultReceived);
|
||||
server.listen((HttpRequest request) {
|
||||
if (request.uri.path == "/ws") {
|
||||
WebSocketTransformer.upgrade(request).then((WebSocket socket) {
|
||||
var wrapper = new WebSocketWrapper(
|
||||
sink.messageHistory, sink.resultMarkers, socket);
|
||||
if (!hasPrimary) {
|
||||
wrapper.setPrimary(true);
|
||||
hasPrimary = true;
|
||||
}
|
||||
sink.addConnection(wrapper);
|
||||
source.addConnection(wrapper);
|
||||
|
||||
wrapper.stream.listen(null, onDone: _handleDisconnect(wrapper));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void resultReceived() {
|
||||
sink.resultReceived();
|
||||
}
|
||||
|
||||
EventEmitter from(String channel) {
|
||||
return source.from(channel);
|
||||
}
|
||||
|
||||
EventEmitter to(String channel) {
|
||||
return sink.to(channel);
|
||||
}
|
||||
|
||||
Function _handleDisconnect(WebSocketWrapper wrapper) {
|
||||
return () {
|
||||
sink.removeConnection(wrapper);
|
||||
if (wrapper.isPrimary) {
|
||||
hasPrimary = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class WebSocketWrapper {
|
||||
WebSocket socket;
|
||||
Stream stream;
|
||||
int _numResultsReceived = 0;
|
||||
bool _isPrimary = false;
|
||||
bool caughtUp = false;
|
||||
List<String> _messageHistory;
|
||||
List<int> _resultMarkers;
|
||||
|
||||
WebSocketWrapper(this._messageHistory, this._resultMarkers, this.socket) {
|
||||
stream = socket.asBroadcastStream();
|
||||
stream.listen((encodedMessage) {
|
||||
var message = JSON.decode(encodedMessage)['message'];
|
||||
if (message is Map && message.containsKey("type")) {
|
||||
if (message['type'] == 'result') {
|
||||
resultReceived();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool get isPrimary => _isPrimary;
|
||||
|
||||
void resultReceived() {
|
||||
if (!isPrimary && !caughtUp) {
|
||||
_numResultsReceived++;
|
||||
sendToMarker(_numResultsReceived);
|
||||
}
|
||||
}
|
||||
|
||||
void setPrimary(bool primary) {
|
||||
_isPrimary = primary;
|
||||
if (primary) {
|
||||
caughtUp = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Sends up to the given result marker
|
||||
void sendToMarker(int markerIndex) {
|
||||
int numMessages;
|
||||
int curr;
|
||||
if (markerIndex >= _resultMarkers.length) {
|
||||
// we're past the final result marker so send all messages in history
|
||||
curr = (_resultMarkers.length > 0)
|
||||
? _resultMarkers[_resultMarkers.length - 1]
|
||||
: 0;
|
||||
numMessages = _messageHistory.length - curr;
|
||||
caughtUp = true;
|
||||
} else {
|
||||
curr = (markerIndex == 0) ? 0 : _resultMarkers[markerIndex - 1];
|
||||
var end = _resultMarkers[markerIndex];
|
||||
numMessages = end - curr;
|
||||
}
|
||||
while (numMessages > 0) {
|
||||
socket.add(_messageHistory[curr]);
|
||||
curr++;
|
||||
numMessages--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MultiClientServerMessageBusSink implements MessageBusSink {
|
||||
final List<String> messageHistory = new List<String>();
|
||||
final Set<WebSocketWrapper> openConnections = new Set<WebSocketWrapper>();
|
||||
final Map<String, EventEmitter> _channels = new Map<String, EventEmitter>();
|
||||
final List<int> resultMarkers = new List<int>();
|
||||
|
||||
void resultReceived() {
|
||||
resultMarkers.add(messageHistory.length);
|
||||
}
|
||||
|
||||
void addConnection(WebSocketWrapper webSocket) {
|
||||
openConnections.add(webSocket);
|
||||
// send messages up to the first result marker to this socket
|
||||
webSocket.sendToMarker(0);
|
||||
}
|
||||
|
||||
void removeConnection(WebSocketWrapper webSocket) {
|
||||
openConnections.remove(webSocket);
|
||||
}
|
||||
|
||||
EventEmitter to(String channel) {
|
||||
if (_channels.containsKey(channel)) {
|
||||
return _channels[channel];
|
||||
} else {
|
||||
var emitter = new EventEmitter();
|
||||
emitter.listen((message) {
|
||||
_send({'channel': channel, 'message': message});
|
||||
});
|
||||
return emitter;
|
||||
}
|
||||
}
|
||||
|
||||
void _send(dynamic message) {
|
||||
String encodedMessage = JSON.encode(message);
|
||||
openConnections.forEach((WebSocketWrapper webSocket) {
|
||||
if (webSocket.caughtUp) {
|
||||
webSocket.socket.add(encodedMessage);
|
||||
}
|
||||
});
|
||||
messageHistory.add(encodedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
class MultiClientServerMessageBusSource implements MessageBusSource {
|
||||
final Map<String, EventEmitter> _channels = new Map<String, EventEmitter>();
|
||||
Function onResultReceived;
|
||||
|
||||
MultiClientServerMessageBusSource(this.onResultReceived);
|
||||
|
||||
EventEmitter from(String channel) {
|
||||
if (_channels.containsKey(channel)) {
|
||||
return _channels[channel];
|
||||
} else {
|
||||
var emitter = new EventEmitter();
|
||||
_channels[channel] = emitter;
|
||||
return emitter;
|
||||
}
|
||||
}
|
||||
|
||||
void addConnection(WebSocketWrapper webSocket) {
|
||||
if (webSocket.isPrimary) {
|
||||
webSocket.stream.listen((encodedMessage) {
|
||||
var decodedMessage = decodeMessage(encodedMessage);
|
||||
var channel = decodedMessage['channel'];
|
||||
var message = decodedMessage['message'];
|
||||
if (message is Map && message.containsKey("type")) {
|
||||
if (message['type'] == 'result') {
|
||||
// tell the bus that a result was received on the primary
|
||||
onResultReceived();
|
||||
}
|
||||
}
|
||||
|
||||
if (_channels.containsKey(channel)) {
|
||||
_channels[channel].add(message);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
webSocket.stream.listen((encodedMessage) {
|
||||
// handle events from non-primary browser
|
||||
var decodedMessage = decodeMessage(encodedMessage);
|
||||
var channel = decodedMessage['channel'];
|
||||
var message = decodedMessage['message'];
|
||||
if (_channels.containsKey(EVENT_CHANNEL) && channel == EVENT_CHANNEL) {
|
||||
_channels[channel].add(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> decodeMessage(dynamic message) {
|
||||
return JSON.decode(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
library angular2.src.web_workers.debug_tools.single_client_server_message_bus;
|
||||
|
||||
import "package:angular2/src/web_workers/shared/message_bus.dart"
|
||||
show MessageBus, MessageBusSink, MessageBusSource;
|
||||
import 'dart:io';
|
||||
import 'dart:convert' show JSON;
|
||||
import 'dart:async';
|
||||
import "package:angular2/src/core/facade/async.dart" show EventEmitter;
|
||||
|
||||
class SingleClientServerMessageBus implements MessageBus {
|
||||
final SingleClientServerMessageBusSink sink;
|
||||
SingleClientServerMessageBusSource source;
|
||||
bool connected = false;
|
||||
|
||||
SingleClientServerMessageBus(this.sink, this.source);
|
||||
|
||||
SingleClientServerMessageBus.fromHttpServer(HttpServer server)
|
||||
: sink = new SingleClientServerMessageBusSink() {
|
||||
source = new SingleClientServerMessageBusSource();
|
||||
server.listen((HttpRequest request) {
|
||||
if (request.uri.path == "/ws") {
|
||||
if (!connected) {
|
||||
WebSocketTransformer.upgrade(request).then((WebSocket socket) {
|
||||
sink.setConnection(socket);
|
||||
|
||||
var stream = socket.asBroadcastStream();
|
||||
source.setConnectionFromStream(stream);
|
||||
stream.listen(null, onDone: _handleDisconnect);
|
||||
}).catchError((error) {
|
||||
throw error;
|
||||
connected = false;
|
||||
});
|
||||
connected = true;
|
||||
} else {
|
||||
// refuse additional clients
|
||||
request.response.statusCode = HttpStatus.SERVICE_UNAVAILABLE;
|
||||
request.response.write("Maximum number of clients connected.");
|
||||
request.response.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _handleDisconnect() {
|
||||
sink.removeConnection();
|
||||
source.removeConnection();
|
||||
connected = false;
|
||||
}
|
||||
|
||||
EventEmitter from(String channel) {
|
||||
return source.from(channel);
|
||||
}
|
||||
|
||||
EventEmitter to(String channel) {
|
||||
return sink.to(channel);
|
||||
}
|
||||
}
|
||||
|
||||
class SingleClientServerMessageBusSink implements MessageBusSink {
|
||||
final List<String> _messageBuffer = new List<String>();
|
||||
WebSocket _socket = null;
|
||||
final Map<String, EventEmitter> _channels = new Map<String, EventEmitter>();
|
||||
|
||||
void setConnection(WebSocket webSocket) {
|
||||
_socket = webSocket;
|
||||
_sendBufferedMessages();
|
||||
}
|
||||
|
||||
EventEmitter to(String channel) {
|
||||
if (_channels.containsKey(channel)) {
|
||||
return _channels[channel];
|
||||
} else {
|
||||
var emitter = new EventEmitter();
|
||||
emitter.listen((message) {
|
||||
_send({'channel': channel, 'message': message});
|
||||
});
|
||||
return emitter;
|
||||
}
|
||||
}
|
||||
|
||||
void removeConnection() {
|
||||
_socket = null;
|
||||
}
|
||||
|
||||
void _send(dynamic message) {
|
||||
String encodedMessage = JSON.encode(message);
|
||||
if (_socket != null) {
|
||||
_socket.add(encodedMessage);
|
||||
} else {
|
||||
_messageBuffer.add(encodedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
void _sendBufferedMessages() {
|
||||
_messageBuffer.forEach((message) => _socket.add(message));
|
||||
_messageBuffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
class SingleClientServerMessageBusSource implements MessageBusSource {
|
||||
final Map<String, EventEmitter> _channels = new Map<String, EventEmitter>();
|
||||
Stream _stream;
|
||||
|
||||
SingleClientServerMessageBusSource();
|
||||
|
||||
EventEmitter from(String channel) {
|
||||
if (_channels.containsKey(channel)) {
|
||||
return _channels[channel];
|
||||
} else {
|
||||
var emitter = new EventEmitter();
|
||||
_channels[channel] = emitter;
|
||||
return emitter;
|
||||
}
|
||||
}
|
||||
|
||||
void setConnectionFromWebSocket(WebSocket socket) {
|
||||
setConnectionFromStream(socket.asBroadcastStream());
|
||||
}
|
||||
|
||||
void setConnectionFromStream(Stream stream) {
|
||||
_stream = stream;
|
||||
_stream.listen((encodedMessage) {
|
||||
var decodedMessage = decodeMessage(encodedMessage);
|
||||
var channel = decodedMessage['channel'];
|
||||
var message = decodedMessage['message'];
|
||||
|
||||
if (_channels.containsKey(channel)) {
|
||||
_channels[channel].add(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void removeConnection() {
|
||||
_stream = null;
|
||||
}
|
||||
|
||||
Map<String, dynamic> decodeMessage(dynamic message) {
|
||||
return JSON.decode(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
library angular2.src.web_workers.worker.web_socket_message_bus;
|
||||
|
||||
import 'dart:html';
|
||||
import 'dart:convert' show JSON;
|
||||
import "package:angular2/src/web_workers/shared/message_bus.dart"
|
||||
show MessageBus, MessageBusSink, MessageBusSource;
|
||||
import 'package:angular2/src/core/facade/async.dart' show EventEmitter;
|
||||
|
||||
class WebSocketMessageBus implements MessageBus {
|
||||
final WebSocketMessageBusSink sink;
|
||||
final WebSocketMessageBusSource source;
|
||||
|
||||
WebSocketMessageBus(this.sink, this.source);
|
||||
|
||||
WebSocketMessageBus.fromWebSocket(WebSocket webSocket)
|
||||
: sink = new WebSocketMessageBusSink(webSocket),
|
||||
source = new WebSocketMessageBusSource(webSocket);
|
||||
|
||||
EventEmitter from(String channel) {
|
||||
return source.from(channel);
|
||||
}
|
||||
|
||||
EventEmitter to(String channel) {
|
||||
return sink.to(channel);
|
||||
}
|
||||
}
|
||||
|
||||
class WebSocketMessageBusSink implements MessageBusSink {
|
||||
final WebSocket _webSocket;
|
||||
final Map<String, EventEmitter> _channels = new Map<String, EventEmitter>();
|
||||
|
||||
WebSocketMessageBusSink(this._webSocket);
|
||||
|
||||
EventEmitter to(String channel) {
|
||||
if (_channels.containsKey(channel)) {
|
||||
return _channels[channel];
|
||||
} else {
|
||||
var emitter = new EventEmitter();
|
||||
emitter.listen((message) {
|
||||
_send({'channel': channel, 'message': message});
|
||||
});
|
||||
_channels[channel] = emitter;
|
||||
return emitter;
|
||||
}
|
||||
}
|
||||
|
||||
void _send(message) {
|
||||
_webSocket.send(JSON.encode(message));
|
||||
}
|
||||
}
|
||||
|
||||
class WebSocketMessageBusSource implements MessageBusSource {
|
||||
final Map<String, EventEmitter> _channels = new Map<String, EventEmitter>();
|
||||
|
||||
WebSocketMessageBusSource(WebSocket webSocket) {
|
||||
webSocket.onMessage.listen((MessageEvent encodedMessage) {
|
||||
var message = decodeMessage(encodedMessage.data);
|
||||
var channel = message['channel'];
|
||||
if (_channels.containsKey(channel)) {
|
||||
_channels[channel].add(message['message']);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
EventEmitter from(String channel) {
|
||||
if (_channels.containsKey(channel)) {
|
||||
return _channels[channel];
|
||||
} else {
|
||||
var emitter = new EventEmitter();
|
||||
_channels[channel] = emitter;
|
||||
return emitter;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> decodeMessage(dynamic message) {
|
||||
return JSON.decode(message);
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@ import {
|
|||
import {ListWrapper, StringMapWrapper, MapWrapper} from "angular2/src/core/facade/collection";
|
||||
import {Serializer} from "angular2/src/web_workers/shared/serializer";
|
||||
import {Injectable} from "angular2/di";
|
||||
import {Type} from "angular2/src/core/facade/lang";
|
||||
import {Type, StringWrapper} from "angular2/src/core/facade/lang";
|
||||
|
||||
@Injectable()
|
||||
export class ClientMessageBrokerFactory {
|
||||
|
@ -90,10 +90,10 @@ export class ClientMessageBroker {
|
|||
private _handleMessage(message: StringMap<string, any>): void {
|
||||
var data = new MessageData(message);
|
||||
// TODO(jteplitz602): replace these strings with messaging constants #3685
|
||||
if (data.type === "result" || data.type === "error") {
|
||||
if (StringWrapper.equals(data.type, "result") || StringWrapper.equals(data.type, "error")) {
|
||||
var id = data.id;
|
||||
if (this._pending.has(id)) {
|
||||
if (data.type === "result") {
|
||||
if (StringWrapper.equals(data.type, "result")) {
|
||||
this._pending.get(id).resolve(data.value);
|
||||
} else {
|
||||
this._pending.get(id).reject(data.value, null);
|
||||
|
|
|
@ -3,6 +3,7 @@ import {EventEmitter, ObservableWrapper} from 'angular2/src/core/facade/async';
|
|||
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
|
||||
import {AnchorBasedAppRootUrl} from 'angular2/src/core/services/anchor_based_app_root_url';
|
||||
import {Injectable} from 'angular2/di';
|
||||
import {StringWrapper} from 'angular2/src/core/facade/lang';
|
||||
|
||||
@Injectable()
|
||||
export class WebWorkerSetup {
|
||||
|
@ -12,7 +13,7 @@ export class WebWorkerSetup {
|
|||
var source = bus.from(SETUP_CHANNEL);
|
||||
|
||||
ObservableWrapper.subscribe(source, (message: string) => {
|
||||
if (message === "ready") {
|
||||
if (StringWrapper.equals(message, "ready")) {
|
||||
ObservableWrapper.callNext(sink, {"rootUrl": rootUrl});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -24,9 +24,9 @@ import {
|
|||
defaultKeyValueDiffers
|
||||
} from 'angular2/src/core/change_detection/change_detection';
|
||||
import {DEFAULT_PIPES} from 'angular2/pipes';
|
||||
import {StyleUrlResolver} from 'angular2/src/core/render/dom/compiler/style_url_resolver';
|
||||
import {ExceptionHandler} from 'angular2/src/core/exception_handler';
|
||||
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||
import {StyleUrlResolver} from 'angular2/src/core/render/dom/compiler/style_url_resolver';
|
||||
import {PipeResolver} from 'angular2/src/core/compiler/pipe_resolver';
|
||||
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
|
||||
import {List, ListWrapper} from 'angular2/src/core/facade/collection';
|
||||
|
@ -42,7 +42,6 @@ import {
|
|||
ComponentRef,
|
||||
DynamicComponentLoader
|
||||
} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||
import {Testability} from 'angular2/src/core/testability/testability';
|
||||
import {AppViewPool, APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_pool';
|
||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
||||
|
@ -51,7 +50,6 @@ import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
|
|||
import {WebWorkerRenderer, WebWorkerCompiler} from './renderer';
|
||||
import {Renderer, RenderCompiler} from 'angular2/src/core/render/api';
|
||||
import {internalView} from 'angular2/src/core/compiler/view_ref';
|
||||
|
||||
import {ClientMessageBrokerFactory} from 'angular2/src/web_workers/shared/client_message_broker';
|
||||
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
|
||||
import {APP_COMPONENT_REF_PROMISE, APP_COMPONENT} from 'angular2/src/core/application_tokens';
|
||||
|
@ -125,6 +123,8 @@ function _injectorBindings(appComponentType, bus: MessageBus, initData: StringMa
|
|||
bind(KeyValueDiffers).toValue(defaultKeyValueDiffers),
|
||||
bind(ChangeDetection).toValue(bestChangeDetection),
|
||||
DirectiveResolver,
|
||||
UrlResolver,
|
||||
StyleUrlResolver,
|
||||
PipeResolver,
|
||||
Parser,
|
||||
Lexer,
|
||||
|
@ -132,10 +132,7 @@ function _injectorBindings(appComponentType, bus: MessageBus, initData: StringMa
|
|||
WebWorkerXHRImpl,
|
||||
bind(XHR).toAlias(WebWorkerXHRImpl),
|
||||
ComponentUrlMapper,
|
||||
UrlResolver,
|
||||
StyleUrlResolver,
|
||||
DynamicComponentLoader,
|
||||
Testability,
|
||||
bind(AppRootUrl).toValue(new AppRootUrl(initData['rootUrl'])),
|
||||
WebWorkerEventDispatcher
|
||||
];
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {EventConfig} from 'angular2/src/core/render/dom/util';
|
||||
import {EventConfig} from 'angular2/src/core/render/event_config';
|
||||
import {ddescribe, describe, expect, it} from 'angular2/test_lib';
|
||||
|
||||
export function main() {
|
|
@ -0,0 +1,23 @@
|
|||
library angular2.test.web_workers.debug_tools.message_bus_common;
|
||||
|
||||
import "dart:convert" show JSON;
|
||||
import "package:angular2/src/web_workers/shared/message_bus.dart";
|
||||
import "package:angular2/test_lib.dart"
|
||||
show AsyncTestCompleter, expect, SpyObject;
|
||||
|
||||
var MESSAGE = const {'test': 10};
|
||||
const CHANNEL = "TEST_CHANNEL";
|
||||
|
||||
void expectSinkSendsEncodedJson(SpyObject socket, MessageBusSink sink,
|
||||
String sendMethodName, AsyncTestCompleter async) {
|
||||
socket.spy(sendMethodName).andCallFake((message) {
|
||||
expectMessageEquality(message, MESSAGE, CHANNEL);
|
||||
async.done();
|
||||
});
|
||||
sink.to(CHANNEL).add(MESSAGE);
|
||||
}
|
||||
|
||||
void expectMessageEquality(String message, Map expectedData, String channel) {
|
||||
expect(JSON.decode(message))
|
||||
.toEqual({'channel': channel, 'message': expectedData});
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
library angular2.test.web_workers.debug_tools.multi_client_server_message_bus;
|
||||
|
||||
import "dart:io";
|
||||
import "dart:async";
|
||||
import "package:angular2/test_lib.dart"
|
||||
show
|
||||
AsyncTestCompleter,
|
||||
inject,
|
||||
describe,
|
||||
it,
|
||||
expect,
|
||||
beforeEach,
|
||||
createTestInjector,
|
||||
beforeEachBindings,
|
||||
SpyObject,
|
||||
proxy;
|
||||
import "package:angular2/src/web_workers/debug_tools/multi_client_server_message_bus.dart";
|
||||
import "package:angular2/src/web_workers/shared/messaging_api.dart";
|
||||
import "./message_bus_common.dart";
|
||||
import "./spy_web_socket.dart";
|
||||
import "dart:convert" show JSON;
|
||||
import 'dart:math';
|
||||
|
||||
main() {
|
||||
List<String> messageHistory = new List<String>();
|
||||
List<int> resultMarkers = new List<int>();
|
||||
describe("MultiClientServerMessageBusSink", () {
|
||||
const CHANNEL = "TEST_CHANNEL";
|
||||
var MESSAGE = const {'test': 10};
|
||||
|
||||
beforeEach(() {
|
||||
messageHistory.clear();
|
||||
resultMarkers.clear();
|
||||
});
|
||||
|
||||
it(
|
||||
"should send messages to all connected clients",
|
||||
inject([AsyncTestCompleter], (async) {
|
||||
const NUM_CLIENTS = 5;
|
||||
var sink = new MultiClientServerMessageBusSink();
|
||||
int numMessagesSent = 0;
|
||||
// initialize all the sockets
|
||||
var sockets = new List<WebSocketWrapper>(NUM_CLIENTS);
|
||||
for (var i = 0; i < sockets.length; i++) {
|
||||
var messageSent =
|
||||
false; // ensure this socket only receives one message
|
||||
var socketWrapper = createSocket(messageHandler: (message) {
|
||||
expect(messageSent).toEqual(false);
|
||||
messageSent = true;
|
||||
expectMessageEquality(message, MESSAGE, CHANNEL);
|
||||
numMessagesSent++;
|
||||
if (numMessagesSent == NUM_CLIENTS) {
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
var socket = socketWrapper.socket;
|
||||
sockets[i] =
|
||||
new WebSocketWrapper(messageHistory, resultMarkers, socket);
|
||||
sink.addConnection(sockets[i]);
|
||||
}
|
||||
sink.to(CHANNEL).add(MESSAGE);
|
||||
}));
|
||||
});
|
||||
|
||||
describe("WebSocketWrapper", () {
|
||||
beforeEach(() {
|
||||
messageHistory.clear();
|
||||
resultMarkers.clear();
|
||||
});
|
||||
|
||||
/**
|
||||
* Generates the given number of random messages, inserts them into messageHistory,
|
||||
* and then returns a cloned version of messageHistory
|
||||
*/
|
||||
List<String> generateRandomMessages(int numMessages) {
|
||||
const MAX = 1 << 31;
|
||||
var random = new Random();
|
||||
for (var i = 0; i < numMessages; i++) {
|
||||
var message = {'value': random.nextInt(MAX)};
|
||||
messageHistory
|
||||
.add(JSON.encode({'channel': CHANNEL, 'message': message}));
|
||||
}
|
||||
// copy the message history to ensure the test fails if the wrapper modifies the list
|
||||
return new List.from(messageHistory);
|
||||
}
|
||||
|
||||
it(
|
||||
"should send all messages when there are no markers",
|
||||
inject([AsyncTestCompleter], (async) {
|
||||
const NUM_MESSAGES = 10;
|
||||
var messageHistoryClone = generateRandomMessages(NUM_MESSAGES);
|
||||
|
||||
int numMessagesSent = 0;
|
||||
var socketWrapper = createSocket(messageHandler: (message) {
|
||||
expect(message).toEqual(messageHistoryClone[numMessagesSent]);
|
||||
//expectMessageEquality(message, expected, CHANNEL);
|
||||
numMessagesSent++;
|
||||
if (numMessagesSent == messageHistoryClone.length) {
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
var wrapper = new WebSocketWrapper(
|
||||
messageHistory, resultMarkers, socketWrapper.socket);
|
||||
wrapper.sendToMarker(0);
|
||||
}));
|
||||
|
||||
it(
|
||||
"should send between two markers",
|
||||
inject([AsyncTestCompleter], (async) {
|
||||
const NUM_MESSAGES = 50;
|
||||
const FIRST_MARKER = 5;
|
||||
const SECOND_MARKER = 15;
|
||||
var messageHistoryClone = generateRandomMessages(NUM_MESSAGES);
|
||||
|
||||
int numMessagesSent = 0;
|
||||
resultMarkers.add(FIRST_MARKER);
|
||||
resultMarkers.add(SECOND_MARKER);
|
||||
var socketWrapper = createSocket(messageHandler: (message) {
|
||||
expect(message)
|
||||
.toEqual(messageHistoryClone[FIRST_MARKER + numMessagesSent]);
|
||||
numMessagesSent++;
|
||||
if (numMessagesSent == SECOND_MARKER - FIRST_MARKER) {
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
var wrapper = new WebSocketWrapper(
|
||||
messageHistory, resultMarkers, socketWrapper.socket);
|
||||
wrapper.sendToMarker(1);
|
||||
}));
|
||||
});
|
||||
|
||||
describe("MultiClientServerMessageBusSource", () {
|
||||
beforeEach(() {
|
||||
messageHistory.clear();
|
||||
resultMarkers.clear();
|
||||
});
|
||||
|
||||
void sendMessage(StreamController controller, dynamic message) {
|
||||
controller.add(JSON.encode(message));
|
||||
}
|
||||
|
||||
void testForwardingMessages(bool primary, bool events, Function done) {
|
||||
var result = createSocket();
|
||||
var controller = result.controller;
|
||||
var socket =
|
||||
new WebSocketWrapper(messageHistory, resultMarkers, result.socket);
|
||||
socket.setPrimary(primary);
|
||||
|
||||
var source = new MultiClientServerMessageBusSource(null);
|
||||
source.addConnection(socket);
|
||||
|
||||
var channel = events ? EVENT_CHANNEL : CHANNEL;
|
||||
source.from(channel).listen((message) {
|
||||
expect(message).toEqual(MESSAGE);
|
||||
done();
|
||||
});
|
||||
|
||||
var message = {'channel': channel, 'message': MESSAGE};
|
||||
sendMessage(controller, message);
|
||||
}
|
||||
|
||||
it(
|
||||
"should forward messages from the primary",
|
||||
inject([AsyncTestCompleter], (async) {
|
||||
testForwardingMessages(true, false, async.done);
|
||||
}));
|
||||
|
||||
it(
|
||||
"should forward event channel messages from non primaries",
|
||||
inject([AsyncTestCompleter], (async) {
|
||||
testForwardingMessages(false, true, async.done);
|
||||
}));
|
||||
|
||||
it(
|
||||
"should forward event channel messages from the primary",
|
||||
inject([AsyncTestCompleter], (async) {
|
||||
testForwardingMessages(true, true, async.done);
|
||||
}));
|
||||
|
||||
it(
|
||||
"should mark results from the primary",
|
||||
inject([AsyncTestCompleter], (async) {
|
||||
var result = createSocket();
|
||||
var controller = result.controller;
|
||||
var socket = new WebSocketWrapper(
|
||||
messageHistory, resultMarkers, result.socket);
|
||||
socket.setPrimary(true);
|
||||
|
||||
var source =
|
||||
new MultiClientServerMessageBusSource(() => async.done());
|
||||
source.addConnection(socket);
|
||||
|
||||
var message = {
|
||||
'channel': CHANNEL,
|
||||
'message': {'type': 'result'}
|
||||
};
|
||||
sendMessage(controller, message);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new SpyWebSocket that calls messageHandler when a new message is sent.
|
||||
* Also returns a StreamController instance that you can use to send messages to anything
|
||||
* that is subscribed to this socket.
|
||||
*/
|
||||
SpySocketWrapper createSocket({Function messageHandler}) {
|
||||
var socket = new SpyWebSocket();
|
||||
if (messageHandler != null) {
|
||||
socket.spy("add").andCallFake(messageHandler);
|
||||
}
|
||||
|
||||
var controller = new StreamController<String>.broadcast();
|
||||
socket.spy("asBroadcastStream").andCallFake(() => controller.stream);
|
||||
return new SpySocketWrapper(socket, controller);
|
||||
}
|
||||
|
||||
class SpySocketWrapper {
|
||||
SpyWebSocket socket;
|
||||
StreamController controller;
|
||||
|
||||
SpySocketWrapper(this.socket, this.controller);
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
library angular2.test.web_workers.debug_tools.single_client_server_message_bus;
|
||||
|
||||
import "dart:io";
|
||||
import "dart:async";
|
||||
import "package:angular2/test_lib.dart"
|
||||
show
|
||||
AsyncTestCompleter,
|
||||
inject,
|
||||
describe,
|
||||
it,
|
||||
expect,
|
||||
beforeEach,
|
||||
createTestInjector,
|
||||
beforeEachBindings,
|
||||
SpyObject,
|
||||
proxy;
|
||||
import "package:angular2/src/web_workers/debug_tools/single_client_server_message_bus.dart";
|
||||
import "./message_bus_common.dart";
|
||||
import "./spy_web_socket.dart";
|
||||
import "dart:convert" show JSON;
|
||||
|
||||
main() {
|
||||
var MESSAGE = const {'test': 10};
|
||||
const CHANNEL = "TEST_CHANNEL";
|
||||
describe("SingleClientServerMessageBusSink", () {
|
||||
it(
|
||||
"should send JSON encoded data over the WebSocket",
|
||||
inject([AsyncTestCompleter], (async) {
|
||||
var socket = new SpyWebSocket();
|
||||
var sink = new SingleClientServerMessageBusSink();
|
||||
sink.setConnection(socket);
|
||||
expectSinkSendsEncodedJson(socket, sink, "add", async);
|
||||
}));
|
||||
|
||||
it(
|
||||
"should buffer messages before connect",
|
||||
inject([AsyncTestCompleter], (async) {
|
||||
var sink = new SingleClientServerMessageBusSink();
|
||||
sink.to(CHANNEL).add(MESSAGE);
|
||||
|
||||
var socket = new SpyWebSocket();
|
||||
socket.spy("add").andCallFake((message) {
|
||||
expectMessageEquality(message, MESSAGE, CHANNEL);
|
||||
async.done();
|
||||
});
|
||||
sink.setConnection(socket);
|
||||
}));
|
||||
|
||||
it(
|
||||
"should buffer messages in between disconnect and connect",
|
||||
inject([AsyncTestCompleter], (async) {
|
||||
var SECOND_MESSAGE = const {'test': 12, 'second': 'hi'};
|
||||
var sink = new SingleClientServerMessageBusSink();
|
||||
sink.to(CHANNEL).add(MESSAGE);
|
||||
|
||||
var socket = new SpyWebSocket();
|
||||
sink.setConnection(socket);
|
||||
|
||||
int numMessages = 0;
|
||||
|
||||
socket.spy("add").andCallFake((message) {
|
||||
numMessages++;
|
||||
if (numMessages == 1) {
|
||||
expectMessageEquality(message, MESSAGE, CHANNEL);
|
||||
} else {
|
||||
expectMessageEquality(message, SECOND_MESSAGE, CHANNEL);
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
|
||||
sink.removeConnection();
|
||||
sink.to(CHANNEL).add(SECOND_MESSAGE);
|
||||
sink.setConnection(socket);
|
||||
}));
|
||||
});
|
||||
|
||||
describe("SingleClientServerMessageBusSource", () {
|
||||
it(
|
||||
"should decode JSON messages and emit them",
|
||||
inject([AsyncTestCompleter], (async) {
|
||||
var socket = new SpyWebSocket();
|
||||
StreamController<String> controller =
|
||||
new StreamController.broadcast();
|
||||
socket.spy("asBroadcastStream").andCallFake(() => controller.stream);
|
||||
|
||||
var source = new SingleClientServerMessageBusSource();
|
||||
source.setConnectionFromWebSocket(socket);
|
||||
source.from(CHANNEL).listen((message) {
|
||||
expect(message).toEqual(MESSAGE);
|
||||
async.done();
|
||||
});
|
||||
|
||||
controller
|
||||
.add(JSON.encode({'channel': CHANNEL, 'message': MESSAGE}));
|
||||
}));
|
||||
});
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* Contains code shared between all server message bus tests
|
||||
*/
|
||||
library angular2.test.web_workers.debug_tools.server_message_bus_common;
|
||||
|
||||
import "package:angular2/test_lib.dart";
|
||||
import "dart:io";
|
||||
|
||||
@proxy
|
||||
class SpyWebSocket extends SpyObject implements WebSocket {
|
||||
SpyWebSocket() : super(SpyWebSocket);
|
||||
noSuchMethod(m) {
|
||||
return super.noSuchMethod(m);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
library angular2.test.web_workers.debug_tools.web_socket_server_message_bus;
|
||||
|
||||
import "package:angular2/test_lib.dart"
|
||||
show
|
||||
AsyncTestCompleter,
|
||||
inject,
|
||||
describe,
|
||||
it,
|
||||
expect,
|
||||
beforeEach,
|
||||
createTestInjector,
|
||||
beforeEachBindings,
|
||||
SpyObject,
|
||||
proxy;
|
||||
import "package:angular2/src/web_workers/debug_tools/web_socket_message_bus.dart";
|
||||
import "dart:html" show WebSocket, MessageEvent;
|
||||
import "./message_bus_common.dart";
|
||||
import "dart:async";
|
||||
import "dart:convert" show JSON;
|
||||
|
||||
main() {
|
||||
var MESSAGE = const {'test': 10};
|
||||
const CHANNEL = "TEST_CHANNEL";
|
||||
|
||||
describe("WebSocketMessageBusSink", () {
|
||||
it(
|
||||
"should send JSON encoded data over the WebSocket",
|
||||
inject([AsyncTestCompleter], (async) {
|
||||
var socket = new SpyWebSocket();
|
||||
var sink = new WebSocketMessageBusSink(socket);
|
||||
expectSinkSendsEncodedJson(socket, sink, "send", async);
|
||||
}));
|
||||
});
|
||||
|
||||
describe("WebSocketMessageBusSource", () {
|
||||
it(
|
||||
"should decode JSON messages and emit them",
|
||||
inject([AsyncTestCompleter], (async) {
|
||||
var socket = new SpyWebSocket();
|
||||
StreamController<MessageEvent> controller =
|
||||
new StreamController.broadcast();
|
||||
socket.spy("get:onMessage").andCallFake(() => controller.stream);
|
||||
var source = new WebSocketMessageBusSource(socket);
|
||||
|
||||
source.from(CHANNEL).listen((message) {
|
||||
expect(message).toEqual(MESSAGE);
|
||||
async.done();
|
||||
});
|
||||
|
||||
var event = new SpyMessageEvent();
|
||||
event.spy("get:data").andCallFake(
|
||||
() => JSON.encode({'channel': CHANNEL, 'message': MESSAGE}));
|
||||
controller.add(event);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
@proxy
|
||||
class SpyMessageEvent extends SpyObject implements MessageEvent {
|
||||
SpyMessageEvent() : super(SpyMessageEvent);
|
||||
noSuchMethod(m) {
|
||||
return super.noSuchMethod(m);
|
||||
}
|
||||
}
|
||||
|
||||
@proxy
|
||||
class SpyWebSocket extends SpyObject implements WebSocket {
|
||||
SpyWebSocket() : super(SpyWebSocket);
|
||||
noSuchMethod(m) {
|
||||
return super.noSuchMethod(m);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
import {NgFor, Component, View, FORM_DIRECTIVES} from 'angular2/angular2';
|
||||
import {NgFor} from 'angular2/src/core/directives/ng_for';
|
||||
import {View, Component} from 'angular2/src/core/metadata';
|
||||
import {FORM_DIRECTIVES} from 'angular2/src/forms/directives';
|
||||
import {Store, Todo, TodoFactory} from './services/TodoStore';
|
||||
|
||||
@Component({selector: 'todo-app', viewBindings: [Store, TodoFactory]})
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
library angular2.examples.web_workers.todo.index_web_socket;
|
||||
|
||||
import "package:angular2/src/core/reflection/reflection_capabilities.dart";
|
||||
import "package:angular2/src/core/reflection/reflection.dart";
|
||||
import "package:angular2/src/web_workers/ui/impl.dart" show bootstrapUICommon;
|
||||
import "package:angular2/src/web_workers/debug_tools/web_socket_message_bus.dart";
|
||||
import 'dart:html'
|
||||
show WebSocket;
|
||||
|
||||
main() {
|
||||
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
||||
var webSocket = new WebSocket("ws://127.0.0.1:1337/ws");
|
||||
var bus = new WebSocketMessageBus.fromWebSocket(webSocket);
|
||||
|
||||
bootstrapUICommon(bus);
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<title>Todo Angular 2 - WebWorker</title>
|
||||
<link rel="stylesheet" href="css/main.css" media="screen" title="no title" charset="utf-8">
|
||||
<body>
|
||||
<todo-app>
|
||||
Loading...
|
||||
</todo-app>
|
||||
|
||||
|
||||
$SCRIPTS$
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,16 @@
|
|||
library angular2.examples.web_workers.todo.server_index;
|
||||
import "index_common.dart" show TodoApp;
|
||||
import "package:angular2/src/web_workers/debug_tools/multi_client_server_message_bus.dart";
|
||||
import "package:angular2/src/web_workers/worker/application_common.dart"
|
||||
show bootstrapWebWorkerCommon;
|
||||
import 'dart:io';
|
||||
import "package:angular2/src/core/reflection/reflection_capabilities.dart";
|
||||
import "package:angular2/src/core/reflection/reflection.dart";
|
||||
|
||||
void main() {
|
||||
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
||||
HttpServer.bind('127.0.0.1', 1337).then((HttpServer server) {
|
||||
var bus = new MultiClientServerMessageBus.fromHttpServer(server);
|
||||
bootstrapWebWorkerCommon(TodoApp, bus).catchError((error) => throw error);
|
||||
});
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
library angular2.transform.template_compiler.reflection.model;
|
||||
|
||||
import 'package:angular2/src/core/render/dom/util.dart';
|
||||
import 'package:angular2/src/core/render/event_config.dart';
|
||||
|
||||
/// Defines the names of getters, setters, and methods which need to be
|
||||
/// available to Angular 2 via the `reflector` at runtime.
|
||||
|
|
Loading…
Reference in New Issue