feat(view): changed event emitters to be observables
This commit is contained in:
@ -360,6 +360,30 @@ export class Directive extends Injectable {
|
||||
*/
|
||||
properties:any; // StringMap
|
||||
|
||||
/**
|
||||
* Enumerates the set of emitted events.
|
||||
*
|
||||
* ## Syntax
|
||||
*
|
||||
* ```
|
||||
* @Component({
|
||||
* events: ['status-change']
|
||||
* })
|
||||
* class TaskComponent {
|
||||
* statusChange:EventEmitter;
|
||||
*
|
||||
* constructor() {
|
||||
* this.complete = new EventEmitter();
|
||||
* }
|
||||
*
|
||||
* onComplete() {
|
||||
* this.statusChange.next("completed");
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
events:List<string>;
|
||||
|
||||
/**
|
||||
* Specifies which DOM hostListeners a directive listens to.
|
||||
*
|
||||
@ -426,11 +450,13 @@ export class Directive extends Injectable {
|
||||
constructor({
|
||||
selector,
|
||||
properties,
|
||||
events,
|
||||
hostListeners,
|
||||
lifecycle
|
||||
}:{
|
||||
selector:string,
|
||||
properties:any,
|
||||
events:List,
|
||||
hostListeners: any,
|
||||
lifecycle:List
|
||||
}={})
|
||||
@ -438,6 +464,7 @@ export class Directive extends Injectable {
|
||||
super();
|
||||
this.selector = selector;
|
||||
this.properties = properties;
|
||||
this.events = events;
|
||||
this.hostListeners = hostListeners;
|
||||
this.lifecycle = lifecycle;
|
||||
}
|
||||
@ -551,6 +578,7 @@ export class Component extends Directive {
|
||||
constructor({
|
||||
selector,
|
||||
properties,
|
||||
events,
|
||||
hostListeners,
|
||||
injectables,
|
||||
lifecycle,
|
||||
@ -558,6 +586,7 @@ export class Component extends Directive {
|
||||
}:{
|
||||
selector:string,
|
||||
properties:Object,
|
||||
events:List,
|
||||
hostListeners:Object,
|
||||
injectables:List,
|
||||
lifecycle:List,
|
||||
@ -567,6 +596,7 @@ export class Component extends Directive {
|
||||
super({
|
||||
selector: selector,
|
||||
properties: properties,
|
||||
events: events,
|
||||
hostListeners: hostListeners,
|
||||
lifecycle: lifecycle
|
||||
});
|
||||
@ -634,12 +664,14 @@ export class DynamicComponent extends Directive {
|
||||
constructor({
|
||||
selector,
|
||||
properties,
|
||||
events,
|
||||
hostListeners,
|
||||
injectables,
|
||||
lifecycle
|
||||
}:{
|
||||
selector:string,
|
||||
properties:Object,
|
||||
events:List,
|
||||
hostListeners:Object,
|
||||
injectables:List,
|
||||
lifecycle:List
|
||||
@ -647,6 +679,7 @@ export class DynamicComponent extends Directive {
|
||||
super({
|
||||
selector: selector,
|
||||
properties: properties,
|
||||
events: events,
|
||||
hostListeners: hostListeners,
|
||||
lifecycle: lifecycle
|
||||
});
|
||||
@ -727,12 +760,14 @@ export class Decorator extends Directive {
|
||||
constructor({
|
||||
selector,
|
||||
properties,
|
||||
events,
|
||||
hostListeners,
|
||||
lifecycle,
|
||||
compileChildren = true,
|
||||
}:{
|
||||
selector:string,
|
||||
properties:any,
|
||||
events:List,
|
||||
hostListeners:any,
|
||||
lifecycle:List,
|
||||
compileChildren:boolean
|
||||
@ -741,6 +776,7 @@ export class Decorator extends Directive {
|
||||
super({
|
||||
selector: selector,
|
||||
properties: properties,
|
||||
events: events,
|
||||
hostListeners: hostListeners,
|
||||
lifecycle: lifecycle
|
||||
});
|
||||
@ -846,17 +882,20 @@ export class Viewport extends Directive {
|
||||
constructor({
|
||||
selector,
|
||||
properties,
|
||||
events,
|
||||
hostListeners,
|
||||
lifecycle
|
||||
}:{
|
||||
selector:string,
|
||||
properties:any,
|
||||
events:List,
|
||||
lifecycle:List
|
||||
}={})
|
||||
{
|
||||
super({
|
||||
selector: selector,
|
||||
properties: properties,
|
||||
events: events,
|
||||
hostListeners: hostListeners,
|
||||
lifecycle: lifecycle
|
||||
});
|
||||
|
25
modules/angular2/src/core/annotations/di.js
vendored
25
modules/angular2/src/core/annotations/di.js
vendored
@ -1,34 +1,11 @@
|
||||
import {CONST} from 'angular2/src/facade/lang';
|
||||
import {DependencyAnnotation} from 'angular2/di';
|
||||
|
||||
/**
|
||||
* Specifies that a function for emitting events should be injected.
|
||||
*
|
||||
* NOTE: This is changing pre 1.0.
|
||||
*
|
||||
* The directive can inject an emitter function that would emit events onto the directive host element.
|
||||
*
|
||||
* @exportedAs angular2/annotations
|
||||
*/
|
||||
export class EventEmitter extends DependencyAnnotation {
|
||||
eventName: string;
|
||||
|
||||
@CONST()
|
||||
constructor(eventName) {
|
||||
super();
|
||||
this.eventName = eventName;
|
||||
}
|
||||
|
||||
get token() {
|
||||
return Function;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that a function for setting host properties should be injected.
|
||||
*
|
||||
* NOTE: This is changing pre 1.0.
|
||||
*
|
||||
*
|
||||
* The directive can inject a property setter that would allow setting this property on the host element.
|
||||
*
|
||||
* @exportedAs angular2/annotations
|
||||
|
@ -3,13 +3,14 @@ import {Math} from 'angular2/src/facade/math';
|
||||
import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {Injector, Key, Dependency, bind, Binding, ResolvedBinding, NoProviderError, ProviderError, CyclicDependencyError} from 'angular2/di';
|
||||
import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility';
|
||||
import {EventEmitter, PropertySetter, Attribute, Query} from 'angular2/src/core/annotations/di';
|
||||
import {PropertySetter, Attribute, Query} from 'angular2/src/core/annotations/di';
|
||||
import * as viewModule from 'angular2/src/core/compiler/view';
|
||||
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
||||
import {NgElement} from 'angular2/src/core/compiler/ng_element';
|
||||
import {Directive, Component, onChange, onDestroy, onAllChangesDone} from 'angular2/src/core/annotations/annotations';
|
||||
import {ChangeDetector, ChangeDetectorRef} from 'angular2/change_detection';
|
||||
import {QueryList} from './query_list';
|
||||
import {reflector} from 'angular2/src/reflection/reflection';
|
||||
|
||||
var _MAX_DIRECTIVE_CONSTRUCTION_COUNTER = 10;
|
||||
|
||||
@ -194,17 +195,14 @@ export class TreeNode {
|
||||
|
||||
export class DirectiveDependency extends Dependency {
|
||||
depth:int;
|
||||
eventEmitterName:string;
|
||||
propSetterName:string;
|
||||
attributeName:string;
|
||||
queryDirective;
|
||||
|
||||
constructor(key:Key, asPromise:boolean, lazy:boolean, optional:boolean,
|
||||
properties:List, depth:int, eventEmitterName: string,
|
||||
propSetterName: string, attributeName:string, queryDirective) {
|
||||
constructor(key:Key, asPromise:boolean, lazy:boolean, optional:boolean, properties:List,
|
||||
depth:int, propSetterName: string, attributeName:string, queryDirective) {
|
||||
super(key, asPromise, lazy, optional, properties);
|
||||
this.depth = depth;
|
||||
this.eventEmitterName = eventEmitterName;
|
||||
this.propSetterName = propSetterName;
|
||||
this.attributeName = attributeName;
|
||||
this.queryDirective = queryDirective;
|
||||
@ -213,18 +211,16 @@ export class DirectiveDependency extends Dependency {
|
||||
|
||||
_verify() {
|
||||
var count = 0;
|
||||
if (isPresent(this.eventEmitterName)) count++;
|
||||
if (isPresent(this.propSetterName)) count++;
|
||||
if (isPresent(this.queryDirective)) count++;
|
||||
if (isPresent(this.attributeName)) count++;
|
||||
if (count > 1) throw new BaseException(
|
||||
'A directive injectable can contain only one of the following @EventEmitter, @PropertySetter, @Attribute or @Query.');
|
||||
'A directive injectable can contain only one of the following @PropertySetter, @Attribute or @Query.');
|
||||
}
|
||||
|
||||
static createFrom(d:Dependency):Dependency {
|
||||
return new DirectiveDependency(d.key, d.asPromise, d.lazy, d.optional,
|
||||
d.properties, DirectiveDependency._depth(d.properties),
|
||||
DirectiveDependency._eventEmitterName(d.properties),
|
||||
DirectiveDependency._propSetterName(d.properties),
|
||||
DirectiveDependency._attributeName(d.properties),
|
||||
DirectiveDependency._query(d.properties)
|
||||
@ -238,11 +234,6 @@ export class DirectiveDependency extends Dependency {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static _eventEmitterName(properties):string {
|
||||
var p = ListWrapper.find(properties, (p) => p instanceof EventEmitter);
|
||||
return isPresent(p) ? p.eventName : null;
|
||||
}
|
||||
|
||||
static _propSetterName(properties):string {
|
||||
var p = ListWrapper.find(properties, (p) => p instanceof PropertySetter);
|
||||
return isPresent(p) ? p.propName : null;
|
||||
@ -277,6 +268,10 @@ export class DirectiveBinding extends ResolvedBinding {
|
||||
}
|
||||
}
|
||||
|
||||
get eventEmitters():List<string> {
|
||||
return isPresent(this.annotation) && isPresent(this.annotation.events) ? this.annotation.events : [];
|
||||
}
|
||||
|
||||
get changeDetection() {
|
||||
if (this.annotation instanceof Component) {
|
||||
var c:Component = this.annotation;
|
||||
@ -296,10 +291,6 @@ export class DirectiveBinding extends ResolvedBinding {
|
||||
var binding = new Binding(type, {toClass: type});
|
||||
return DirectiveBinding.createFromBinding(binding, annotation);
|
||||
}
|
||||
|
||||
static _hasEventEmitter(eventName: string, binding: DirectiveBinding) {
|
||||
return ListWrapper.any(binding.dependencies, (d) => (d.eventEmitterName == eventName));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(rado): benchmark and consider rolling in as ElementInjector fields.
|
||||
@ -317,6 +308,16 @@ export class PreBuiltObjects {
|
||||
}
|
||||
}
|
||||
|
||||
class EventEmitterAccessor {
|
||||
eventName:string;
|
||||
getter:Function;
|
||||
|
||||
constructor(eventName:string, getter:Function) {
|
||||
this.eventName = eventName;
|
||||
this.getter = getter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Difference between di.Injector and ElementInjector
|
||||
@ -337,17 +338,18 @@ ElementInjector:
|
||||
PERF BENCHMARK: http://www.williambrownstreet.net/blog/2014/04/faster-angularjs-rendering-angularjs-and-reactjs/
|
||||
*/
|
||||
|
||||
|
||||
export class ProtoElementInjector {
|
||||
_binding0:ResolvedBinding;
|
||||
_binding1:ResolvedBinding;
|
||||
_binding2:ResolvedBinding;
|
||||
_binding3:ResolvedBinding;
|
||||
_binding4:ResolvedBinding;
|
||||
_binding5:ResolvedBinding;
|
||||
_binding6:ResolvedBinding;
|
||||
_binding7:ResolvedBinding;
|
||||
_binding8:ResolvedBinding;
|
||||
_binding9:ResolvedBinding;
|
||||
_binding0:DirectiveBinding;
|
||||
_binding1:DirectiveBinding;
|
||||
_binding2:DirectiveBinding;
|
||||
_binding3:DirectiveBinding;
|
||||
_binding4:DirectiveBinding;
|
||||
_binding5:DirectiveBinding;
|
||||
_binding6:DirectiveBinding;
|
||||
_binding7:DirectiveBinding;
|
||||
_binding8:DirectiveBinding;
|
||||
_binding9:DirectiveBinding;
|
||||
_binding0IsComponent:boolean;
|
||||
_keyId0:int;
|
||||
_keyId1:int;
|
||||
@ -364,6 +366,7 @@ export class ProtoElementInjector {
|
||||
view:viewModule.AppView;
|
||||
distanceToParent:number;
|
||||
attributes:Map;
|
||||
eventEmitterAccessors:List<List<EventEmitterAccessor>>;
|
||||
|
||||
numberOfDirectives:number;
|
||||
|
||||
@ -397,22 +400,69 @@ export class ProtoElementInjector {
|
||||
|
||||
this.numberOfDirectives = bindings.length;
|
||||
var length = bindings.length;
|
||||
this.eventEmitterAccessors = ListWrapper.createFixedSize(length);
|
||||
|
||||
if (length > 0) {this._binding0 = this._createBinding(bindings[0]); this._keyId0 = this._binding0.key.id;}
|
||||
if (length > 1) {this._binding1 = this._createBinding(bindings[1]); this._keyId1 = this._binding1.key.id;}
|
||||
if (length > 2) {this._binding2 = this._createBinding(bindings[2]); this._keyId2 = this._binding2.key.id;}
|
||||
if (length > 3) {this._binding3 = this._createBinding(bindings[3]); this._keyId3 = this._binding3.key.id;}
|
||||
if (length > 4) {this._binding4 = this._createBinding(bindings[4]); this._keyId4 = this._binding4.key.id;}
|
||||
if (length > 5) {this._binding5 = this._createBinding(bindings[5]); this._keyId5 = this._binding5.key.id;}
|
||||
if (length > 6) {this._binding6 = this._createBinding(bindings[6]); this._keyId6 = this._binding6.key.id;}
|
||||
if (length > 7) {this._binding7 = this._createBinding(bindings[7]); this._keyId7 = this._binding7.key.id;}
|
||||
if (length > 8) {this._binding8 = this._createBinding(bindings[8]); this._keyId8 = this._binding8.key.id;}
|
||||
if (length > 9) {this._binding9 = this._createBinding(bindings[9]); this._keyId9 = this._binding9.key.id;}
|
||||
if (length > 0) {
|
||||
this._binding0 = this._createBinding(bindings[0]);
|
||||
this._keyId0 = this._binding0.key.id;
|
||||
this.eventEmitterAccessors[0] = this._createEventEmitterAccessors(this._binding0);
|
||||
}
|
||||
if (length > 1) {
|
||||
this._binding1 = this._createBinding(bindings[1]);
|
||||
this._keyId1 = this._binding1.key.id;
|
||||
this.eventEmitterAccessors[1] = this._createEventEmitterAccessors(this._binding1);
|
||||
}
|
||||
if (length > 2) {
|
||||
this._binding2 = this._createBinding(bindings[2]);
|
||||
this._keyId2 = this._binding2.key.id;
|
||||
this.eventEmitterAccessors[2] = this._createEventEmitterAccessors(this._binding2);
|
||||
}
|
||||
if (length > 3) {
|
||||
this._binding3 = this._createBinding(bindings[3]);
|
||||
this._keyId3 = this._binding3.key.id;
|
||||
this.eventEmitterAccessors[3] = this._createEventEmitterAccessors(this._binding3);
|
||||
}
|
||||
if (length > 4) {
|
||||
this._binding4 = this._createBinding(bindings[4]);
|
||||
this._keyId4 = this._binding4.key.id;
|
||||
this.eventEmitterAccessors[4] = this._createEventEmitterAccessors(this._binding4);
|
||||
}
|
||||
if (length > 5) {
|
||||
this._binding5 = this._createBinding(bindings[5]);
|
||||
this._keyId5 = this._binding5.key.id;
|
||||
this.eventEmitterAccessors[5] = this._createEventEmitterAccessors(this._binding5);
|
||||
}
|
||||
if (length > 6) {
|
||||
this._binding6 = this._createBinding(bindings[6]);
|
||||
this._keyId6 = this._binding6.key.id;
|
||||
this.eventEmitterAccessors[6] = this._createEventEmitterAccessors(this._binding6);
|
||||
}
|
||||
if (length > 7) {
|
||||
this._binding7 = this._createBinding(bindings[7]);
|
||||
this._keyId7 = this._binding7.key.id;
|
||||
this.eventEmitterAccessors[7] = this._createEventEmitterAccessors(this._binding7);
|
||||
}
|
||||
if (length > 8) {
|
||||
this._binding8 = this._createBinding(bindings[8]);
|
||||
this._keyId8 = this._binding8.key.id;
|
||||
this.eventEmitterAccessors[8] = this._createEventEmitterAccessors(this._binding8);
|
||||
}
|
||||
if (length > 9) {
|
||||
this._binding9 = this._createBinding(bindings[9]);
|
||||
this._keyId9 = this._binding9.key.id;
|
||||
this.eventEmitterAccessors[9] = this._createEventEmitterAccessors(this._binding9);
|
||||
}
|
||||
if (length > 10) {
|
||||
throw 'Maximum number of directives per element has been reached.';
|
||||
}
|
||||
}
|
||||
|
||||
_createEventEmitterAccessors(b:DirectiveBinding) {
|
||||
return ListWrapper.map(b.eventEmitters, eventName =>
|
||||
new EventEmitterAccessor(eventName, reflector.getter(eventName))
|
||||
);
|
||||
}
|
||||
|
||||
instantiate(parent:ElementInjector):ElementInjector {
|
||||
return new ElementInjector(this, parent);
|
||||
}
|
||||
@ -447,21 +497,6 @@ export class ProtoElementInjector {
|
||||
if (index == 9) return this._binding9;
|
||||
throw new OutOfBoundsAccess(index);
|
||||
}
|
||||
|
||||
hasEventEmitter(eventName: string) {
|
||||
var p = this;
|
||||
if (isPresent(p._binding0) && DirectiveBinding._hasEventEmitter(eventName, p._binding0)) return true;
|
||||
if (isPresent(p._binding1) && DirectiveBinding._hasEventEmitter(eventName, p._binding1)) return true;
|
||||
if (isPresent(p._binding2) && DirectiveBinding._hasEventEmitter(eventName, p._binding2)) return true;
|
||||
if (isPresent(p._binding3) && DirectiveBinding._hasEventEmitter(eventName, p._binding3)) return true;
|
||||
if (isPresent(p._binding4) && DirectiveBinding._hasEventEmitter(eventName, p._binding4)) return true;
|
||||
if (isPresent(p._binding5) && DirectiveBinding._hasEventEmitter(eventName, p._binding5)) return true;
|
||||
if (isPresent(p._binding6) && DirectiveBinding._hasEventEmitter(eventName, p._binding6)) return true;
|
||||
if (isPresent(p._binding7) && DirectiveBinding._hasEventEmitter(eventName, p._binding7)) return true;
|
||||
if (isPresent(p._binding8) && DirectiveBinding._hasEventEmitter(eventName, p._binding8)) return true;
|
||||
if (isPresent(p._binding9) && DirectiveBinding._hasEventEmitter(eventName, p._binding9)) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class ElementInjector extends TreeNode {
|
||||
@ -607,6 +642,10 @@ export class ElementInjector extends TreeNode {
|
||||
return this._getDirectiveByKeyId(Key.get(type).id) !== _undefined;
|
||||
}
|
||||
|
||||
getEventEmitterAccessors() {
|
||||
return this._proto.eventEmitterAccessors;
|
||||
}
|
||||
|
||||
/** Gets the NgElement associated with this ElementInjector */
|
||||
getNgElement() {
|
||||
return this._preBuiltObjects.element;
|
||||
@ -689,7 +728,6 @@ export class ElementInjector extends TreeNode {
|
||||
}
|
||||
|
||||
_getByDependency(dep:DirectiveDependency, requestor:Key) {
|
||||
if (isPresent(dep.eventEmitterName)) return this._buildEventEmitter(dep);
|
||||
if (isPresent(dep.propSetterName)) return this._buildPropSetter(dep);
|
||||
if (isPresent(dep.attributeName)) return this._buildAttribute(dep);
|
||||
if (isPresent(dep.queryDirective)) return this._findQuery(dep.queryDirective).list;
|
||||
@ -699,13 +737,6 @@ export class ElementInjector extends TreeNode {
|
||||
return this._getByKey(dep.key, dep.depth, dep.optional, requestor);
|
||||
}
|
||||
|
||||
_buildEventEmitter(dep: DirectiveDependency) {
|
||||
var view = this._getPreBuiltObjectByKeyId(StaticKeys.instance().viewId);
|
||||
return (event) => {
|
||||
view.triggerEventHandlers(dep.eventEmitterName, event, this._proto.index);
|
||||
};
|
||||
}
|
||||
|
||||
_buildPropSetter(dep) {
|
||||
var view = this._getPreBuiltObjectByKeyId(StaticKeys.instance().viewId);
|
||||
var renderer = view.renderer;
|
||||
@ -934,18 +965,10 @@ export class ElementInjector extends TreeNode {
|
||||
throw new OutOfBoundsAccess(index);
|
||||
}
|
||||
|
||||
getDirectiveBindingAtIndex(index:int) {
|
||||
return this._proto.getDirectiveBindingAtIndex(index);
|
||||
}
|
||||
|
||||
hasInstances() {
|
||||
return this._constructionCounter > 0;
|
||||
}
|
||||
|
||||
hasEventEmitter(eventName: string) {
|
||||
return this._proto.hasEventEmitter(eventName);
|
||||
}
|
||||
|
||||
/** Gets whether this element is exporting a component instance as $implicit. */
|
||||
isExportingComponent() {
|
||||
return this._proto.exportComponent;
|
||||
|
@ -2,6 +2,7 @@ import {Injectable, Inject, OpaqueToken, Injector} from 'angular2/di';
|
||||
import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection';
|
||||
import * as eli from './element_injector';
|
||||
import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||
import {ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import * as vcModule from './view_container';
|
||||
import * as viewModule from './view';
|
||||
import {BindingPropagationConfig, Locals} from 'angular2/change_detection';
|
||||
@ -165,6 +166,7 @@ export class AppViewHydrator {
|
||||
var elementInjector = view.elementInjectors[i];
|
||||
if (isPresent(elementInjector)) {
|
||||
elementInjector.instantiateDirectives(appInjector, hostElementInjector, shadowDomAppInjector, view.preBuiltObjects[i]);
|
||||
this._setUpEventEmitters(view, elementInjector);
|
||||
|
||||
// The exporting of $implicit is a special case. Since multiple elements will all export
|
||||
// the different values as $implicit, directly assign $implicit bindings to the variable
|
||||
@ -194,6 +196,25 @@ export class AppViewHydrator {
|
||||
return renderComponentIndex;
|
||||
}
|
||||
|
||||
_setUpEventEmitters(view:viewModule.AppView, elementInjector:eli.ElementInjector) {
|
||||
var emitters = elementInjector.getEventEmitterAccessors();
|
||||
for(var directiveIndex = 0; directiveIndex < emitters.length; ++directiveIndex) {
|
||||
var directiveEmitters = emitters[directiveIndex];
|
||||
var directive = elementInjector.getDirectiveAtIndex(directiveIndex);
|
||||
|
||||
for (var eventIndex = 0; eventIndex < directiveEmitters.length; ++eventIndex) {
|
||||
var eventEmitterAccessor = directiveEmitters[eventIndex];
|
||||
this._setUpSubscription(view, directive, directiveIndex, eventEmitterAccessor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_setUpSubscription(view:viewModule.AppView, directive:Object, directiveIndex:number, eventEmitterAccessor) {
|
||||
var eventEmitter = eventEmitterAccessor.getter(directive);
|
||||
ObservableWrapper.subscribe(eventEmitter,
|
||||
eventObj => view.triggerEventHandlers(eventEmitterAccessor.eventName, eventObj, directiveIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* This should only be called by View or ViewContainer.
|
||||
*/
|
||||
|
@ -37,27 +37,50 @@ class ObservableWrapper {
|
||||
return s.listen(onNext, onError: onError, onDone: onComplete, cancelOnError: true);
|
||||
}
|
||||
|
||||
static StreamController createController() {
|
||||
return new StreamController.broadcast();
|
||||
static void callNext(EventEmitter emitter, value) {
|
||||
emitter.add(value);
|
||||
}
|
||||
|
||||
static Stream createObservable(StreamController controller) {
|
||||
return controller.stream;
|
||||
static void callThrow(EventEmitter emitter, error) {
|
||||
emitter.addError(error);
|
||||
}
|
||||
|
||||
static void callNext(StreamController controller, value) {
|
||||
controller.add(value);
|
||||
}
|
||||
|
||||
static void callThrow(StreamController controller, error) {
|
||||
controller.addError(error);
|
||||
}
|
||||
|
||||
static void callReturn(StreamController controller) {
|
||||
controller.close();
|
||||
static void callReturn(EventEmitter emitter) {
|
||||
emitter.close();
|
||||
}
|
||||
}
|
||||
|
||||
class EventEmitter extends Stream {
|
||||
StreamController<String> _controller;
|
||||
|
||||
EventEmitter() {
|
||||
_controller = new StreamController.broadcast();
|
||||
}
|
||||
|
||||
StreamSubscription listen(void onData(String line), {
|
||||
void onError(Error error),
|
||||
void onDone(),
|
||||
bool cancelOnError }) {
|
||||
return _controller.stream.listen(onData,
|
||||
onError: onError,
|
||||
onDone: onDone,
|
||||
cancelOnError: cancelOnError);
|
||||
}
|
||||
|
||||
void add(value) {
|
||||
_controller.add(value);
|
||||
}
|
||||
|
||||
void addError(error) {
|
||||
_controller.addError(error);
|
||||
}
|
||||
|
||||
void close() {
|
||||
_controller.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class _Completer {
|
||||
final Completer c;
|
||||
|
||||
|
@ -53,6 +53,28 @@ export class PromiseWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
export class ObservableWrapper {
|
||||
static subscribe(emitter:EventEmitter, onNext, onThrow = null, onReturn = null) {
|
||||
return emitter.observer({next: onNext, throw: onThrow, return: onReturn});
|
||||
}
|
||||
|
||||
static callNext(emitter:EventEmitter, value:any) {
|
||||
emitter.next(value);
|
||||
}
|
||||
|
||||
static callThrow(emitter:EventEmitter, error:any) {
|
||||
emitter.throw(error);
|
||||
}
|
||||
|
||||
static callReturn(emitter:EventEmitter) {
|
||||
emitter.return();
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: vsavkin change to interface
|
||||
export class Observable {
|
||||
observer(generator:Function){}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Rx.Observable but provides an adapter to make it work as specified here:
|
||||
@ -60,39 +82,37 @@ export class PromiseWrapper {
|
||||
*
|
||||
* Once a reference implementation of the spec is available, switch to it.
|
||||
*/
|
||||
export var Observable = Rx.Observable;
|
||||
export var ObservableController = Rx.Subject;
|
||||
export class EventEmitter extends Observable {
|
||||
_subject:Rx.Subject;
|
||||
|
||||
export class ObservableWrapper {
|
||||
static createController():Rx.Subject {
|
||||
return new Rx.Subject();
|
||||
constructor() {
|
||||
super();
|
||||
this._subject = new Rx.Subject();
|
||||
}
|
||||
|
||||
static createObservable(subject:Rx.Subject):Observable {
|
||||
return subject;
|
||||
observer(generator) {
|
||||
// Rx.Scheduler.immediate and setTimeout is a workaround, so Rx works with zones.js.
|
||||
// Once https://github.com/angular/zone.js/issues/51 is fixed, the hack should be removed.
|
||||
return this._subject.observeOn(Rx.Scheduler.immediate).subscribe(
|
||||
(value) => {setTimeout(() => generator.next(value));},
|
||||
(error) => generator.throw ? generator.throw(error) : null,
|
||||
() => generator.return ? generator.return() : null
|
||||
);
|
||||
}
|
||||
|
||||
static subscribe(observable:Observable, generatorOrOnNext, onThrow = null, onReturn = null) {
|
||||
if (isPresent(generatorOrOnNext.next)) {
|
||||
return observable.observeOn(Rx.Scheduler.timeout).subscribe(
|
||||
(value) => generatorOrOnNext.next(value),
|
||||
(error) => generatorOrOnNext.throw(error),
|
||||
() => generatorOrOnNext.return()
|
||||
);
|
||||
} else {
|
||||
return observable.observeOn(Rx.Scheduler.timeout).subscribe(generatorOrOnNext, onThrow, onReturn);
|
||||
}
|
||||
toRx():Rx.Observable {
|
||||
return this._subject;
|
||||
}
|
||||
|
||||
static callNext(subject:Rx.Subject, value:any) {
|
||||
subject.onNext(value);
|
||||
next(value) {
|
||||
this._subject.onNext(value);
|
||||
}
|
||||
|
||||
static callThrow(subject:Rx.Subject, error:any) {
|
||||
subject.onError(error);
|
||||
throw(error) {
|
||||
this._subject.onError(error);
|
||||
}
|
||||
|
||||
static callReturn(subject:Rx.Subject) {
|
||||
subject.onCompleted();
|
||||
return(value) {
|
||||
this._subject.onCompleted();
|
||||
}
|
||||
}
|
@ -49,35 +49,50 @@ export class PromiseWrapper {
|
||||
}
|
||||
|
||||
|
||||
export class ObservableWrapper {
|
||||
static subscribe(emitter: EventEmitter, onNext, onThrow = null, onReturn = null) {
|
||||
return emitter.observer({next: onNext, throw: onThrow, return: onReturn});
|
||||
}
|
||||
|
||||
static callNext(emitter: EventEmitter, value: any) { emitter.next(value); }
|
||||
|
||||
static callThrow(emitter: EventEmitter, error: any) { emitter.throw(error); }
|
||||
|
||||
static callReturn(emitter: EventEmitter) { emitter.return (null); }
|
||||
}
|
||||
|
||||
// TODO: vsavkin change to interface
|
||||
export class Observable {
|
||||
observer(generator: any) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Rx.Observable but provides an adapter to make it work as specified here:
|
||||
* https://github.com/jhusain/observable-spec
|
||||
*
|
||||
* Once a reference implementation of the spec is available, switch to it.
|
||||
*/
|
||||
type Observable = Rx.Observable<any>;
|
||||
type ObservableController = Rx.Subject<any>;
|
||||
export class EventEmitter extends Observable {
|
||||
_subject: Rx.Subject<any>;
|
||||
|
||||
export class ObservableWrapper {
|
||||
static createController(): Rx.Subject<any> { return new Rx.Subject(); }
|
||||
|
||||
static createObservable<T>(subject: Rx.Subject<T>): Rx.Observable<T> { return subject; }
|
||||
|
||||
static subscribe(observable: Rx.Observable<any>, generatorOrOnNext, onThrow = null,
|
||||
onReturn = null) {
|
||||
if (isPresent(generatorOrOnNext.next)) {
|
||||
return observable.observeOn(Rx.Scheduler.timeout)
|
||||
.subscribe((value) => generatorOrOnNext.next(value),
|
||||
(error) => generatorOrOnNext.throw(error), () => generatorOrOnNext.return ());
|
||||
} else {
|
||||
return observable.observeOn(Rx.Scheduler.timeout)
|
||||
.subscribe(generatorOrOnNext, onThrow, onReturn);
|
||||
}
|
||||
constructor() {
|
||||
super();
|
||||
this._subject = new Rx.Subject<any>();
|
||||
}
|
||||
|
||||
static callNext(subject: Rx.Subject<any>, value: any) { subject.onNext(value); }
|
||||
observer(generator) {
|
||||
var immediateScheduler = (<any>Rx.Scheduler).immediate;
|
||||
return this._subject.observeOn(immediateScheduler)
|
||||
.subscribe((value) => { setTimeout(() => generator.next(value)); },
|
||||
(error) => generator.throw ? generator.throw(error) : null,
|
||||
() => generator.return ? generator.return () : null);
|
||||
}
|
||||
|
||||
static callThrow(subject: Rx.Subject<any>, error: any) { subject.onError(error); }
|
||||
toRx(): Rx.Observable<any> { return this._subject; }
|
||||
|
||||
static callReturn(subject: Rx.Subject<any>) { subject.onCompleted(); }
|
||||
}
|
||||
next(value) { this._subject.onNext(value); }
|
||||
|
||||
throw(error) { this._subject.onError(error); }
|
||||
|
||||
return (value) { this._subject.onCompleted(); }
|
||||
}
|
25
modules/angular2/src/forms/model.js
vendored
25
modules/angular2/src/forms/model.js
vendored
@ -1,5 +1,5 @@
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {Observable, ObservableController, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {Observable, EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {StringMap, StringMapWrapper, ListWrapper, List} from 'angular2/src/facade/collection';
|
||||
import {Validators} from './validators';
|
||||
|
||||
@ -40,8 +40,7 @@ export class AbstractControl {
|
||||
_parent:any; /* ControlGroup | ControlArray */
|
||||
validator:Function;
|
||||
|
||||
valueChanges:Observable;
|
||||
_valueChangesController:ObservableController;
|
||||
_valueChanges:EventEmitter;
|
||||
|
||||
constructor(validator:Function) {
|
||||
this.validator = validator;
|
||||
@ -72,6 +71,10 @@ export class AbstractControl {
|
||||
return ! this.pristine;
|
||||
}
|
||||
|
||||
get valueChanges():Observable {
|
||||
return this._valueChanges;
|
||||
}
|
||||
|
||||
setParent(parent){
|
||||
this._parent = parent;
|
||||
}
|
||||
@ -95,16 +98,14 @@ export class Control extends AbstractControl {
|
||||
constructor(value:any, validator:Function = Validators.nullValidator) {
|
||||
super(validator);
|
||||
this._setValueErrorsStatus(value);
|
||||
|
||||
this._valueChangesController = ObservableWrapper.createController();
|
||||
this.valueChanges = ObservableWrapper.createObservable(this._valueChangesController);
|
||||
this._valueChanges = new EventEmitter();
|
||||
}
|
||||
|
||||
updateValue(value:any):void {
|
||||
this._setValueErrorsStatus(value);
|
||||
this._pristine = false;
|
||||
|
||||
ObservableWrapper.callNext(this._valueChangesController, this._value);
|
||||
ObservableWrapper.callNext(this._valueChanges, this._value);
|
||||
|
||||
this._updateParent();
|
||||
}
|
||||
@ -137,8 +138,7 @@ export class ControlGroup extends AbstractControl {
|
||||
this.controls = controls;
|
||||
this._optionals = isPresent(optionals) ? optionals : {};
|
||||
|
||||
this._valueChangesController = ObservableWrapper.createController();
|
||||
this.valueChanges = ObservableWrapper.createObservable(this._valueChangesController);
|
||||
this._valueChanges = new EventEmitter();
|
||||
|
||||
this._setParentForControls();
|
||||
this._setValueErrorsStatus();
|
||||
@ -169,7 +169,7 @@ export class ControlGroup extends AbstractControl {
|
||||
this._setValueErrorsStatus();
|
||||
this._pristine = false;
|
||||
|
||||
ObservableWrapper.callNext(this._valueChangesController, this._value);
|
||||
ObservableWrapper.callNext(this._valueChanges, this._value);
|
||||
|
||||
this._updateParent();
|
||||
}
|
||||
@ -222,8 +222,7 @@ export class ControlArray extends AbstractControl {
|
||||
super(validator);
|
||||
this.controls = controls;
|
||||
|
||||
this._valueChangesController = ObservableWrapper.createController();
|
||||
this.valueChanges = ObservableWrapper.createObservable(this._valueChangesController);
|
||||
this._valueChanges = new EventEmitter();
|
||||
|
||||
this._setParentForControls();
|
||||
this._setValueErrorsStatus();
|
||||
@ -258,7 +257,7 @@ export class ControlArray extends AbstractControl {
|
||||
this._setValueErrorsStatus();
|
||||
this._pristine = false;
|
||||
|
||||
ObservableWrapper.callNext(this._valueChangesController, this._value);
|
||||
ObservableWrapper.callNext(this._valueChanges, this._value);
|
||||
|
||||
this._updateParent();
|
||||
}
|
||||
|
Reference in New Issue
Block a user