Compare commits

..

14 Commits

Author SHA1 Message Date
06ad112998 docs(changelog): update change log to beta.14 2016-04-07 10:19:35 -07:00
cfa1d17afe chore(release): bump version to beta.14 2016-04-07 10:09:46 -07:00
3ca6df87b8 fix(select): update name from ng-value to ngValue
Closes #7939
2016-04-06 22:47:21 +00:00
e310bee9e2 feat(dart/transform): Avoid print in transformer code.
Replace direct uses of `print` in the transformer with explicit uses of
stderr.

Add a few @override annotations for clarification of other `print`
implementations.

Clarify error message on incorrect custom_annotations value.

Closes #7855
2016-04-06 22:31:08 +00:00
4902244cce fix(router): allow forward slashes in query parameters
Closes #7824
2016-04-06 22:01:12 +00:00
8db97b0b7a fix(forms): support both value and ng-value 2016-04-06 14:37:57 -07:00
9be04f8d38 fix(upgrade): leak when angular1 destroys element
Fixes #6401

Closes #7935
2016-04-06 19:58:10 +00:00
74e2bd7e3e fix(select): support objects as select values
Closes #4843

Closes #7842
2016-04-06 17:05:38 +00:00
52d3980d02 Revert "feat(transformers): changes transformers to collect information about providers and resolve identifiers during linking"
This reverts commit 4e9809bcb2.
2016-04-06 09:26:03 -07:00
4e9809bcb2 feat(transformers): changes transformers to collect information about providers and resolve identifiers during linking
Closes #7380
2016-04-04 22:59:43 +00:00
bd8a4215dd refactor(core): remove @Injectable as only classes that are instantiated via DI need it 2016-04-04 22:59:43 +00:00
d23b973e7a refactor(forms): extract Validators.required into a variable as transformers cannot resolve statics 2016-04-04 22:59:43 +00:00
0dbf959548 feat(static-reflector): Added StaticReflector
Added a static reflector that uses metadta produced during build
or, additionally, directly from typescript, to produce the metadata
used by Angular code generation compiler.
2016-04-01 14:45:43 -07:00
20812f446f docs(changelog): fix formatting 2016-03-30 18:10:41 -07:00
19 changed files with 1405 additions and 90 deletions

View File

@ -1,3 +1,24 @@
<a name="2.0.0-beta.14"></a>
# 2.0.0-beta.14 (2016-04-07)
### Bug Fixes
* **forms:** support both value and ng-value ([8db97b0](https://github.com/angular/angular/commit/8db97b0))
* **router:** allow forward slashes in query parameters ([4902244](https://github.com/angular/angular/commit/4902244)), closes [#7824](https://github.com/angular/angular/issues/7824)
* **select:** support objects as select values ([74e2bd7](https://github.com/angular/angular/commit/74e2bd7)), closes [#4843](https://github.com/angular/angular/issues/4843) [#7842](https://github.com/angular/angular/issues/7842)
* **select:** update name from ng-value to ngValue ([3ca6df8](https://github.com/angular/angular/commit/3ca6df8)), closes [#7939](https://github.com/angular/angular/issues/7939)
* **upgrade:** leak when angular1 destroys element ([9be04f8](https://github.com/angular/angular/commit/9be04f8)), closes [#6401](https://github.com/angular/angular/issues/6401) [#7935](https://github.com/angular/angular/issues/7935)
### Features
* **dart/transform:** Avoid `print` in transformer code. ([e310bee](https://github.com/angular/angular/commit/e310bee)), closes [#7855](https://github.com/angular/angular/issues/7855)
* **static-reflector:** Added StaticReflector ([0dbf959](https://github.com/angular/angular/commit/0dbf959))
<a name="2.0.0-beta.13"></a>
# 2.0.0-beta.13 (2016-03-31)
@ -30,14 +51,20 @@
### BREAKING CHANGES
* For static content projection, elements with *-directives are now matched against the element itself vs the template before.
<p *ngIf="condition" foo></p>
`<p *ngIf="condition" foo></p>`
Before:
```html
// Use the implicit template for projection
<ng-content select="template"></ng-content>
```
After:
```html
// Use the actual element for projection
<ng-content select="p[foo]"></ng-content>
```
<a name="2.0.0-beta.12"></a>
# 2.0.0-beta.12 (2016-03-23)

View File

@ -1,21 +1,83 @@
import {
Query,
Directive,
Renderer,
Self,
forwardRef,
Provider,
ElementRef,
QueryList
Input,
Host,
OnDestroy,
Optional
} from 'angular2/core';
import {ObservableWrapper} from 'angular2/src/facade/async';
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from './control_value_accessor';
import {CONST_EXPR} from 'angular2/src/facade/lang';
import {
CONST_EXPR,
StringWrapper,
isPrimitive,
isPresent,
isBlank,
looseIdentical
} from 'angular2/src/facade/lang';
import {MapWrapper} from 'angular2/src/facade/collection';
const SELECT_VALUE_ACCESSOR = CONST_EXPR(new Provider(
NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => SelectControlValueAccessor), multi: true}));
function _buildValueString(id: string, value: any): string {
if (isBlank(id)) return `${value}`;
if (!isPrimitive(value)) value = "Object";
return StringWrapper.slice(`${id}: ${value}`, 0, 50);
}
function _extractId(valueString: string): string {
return valueString.split(":")[0];
}
/**
* The accessor for writing a value and listening to changes on a select element.
*/
@Directive({
selector: 'select[ngControl],select[ngFormControl],select[ngModel]',
host: {'(input)': 'onChange($event.target.value)', '(blur)': 'onTouched()'},
providers: [SELECT_VALUE_ACCESSOR]
})
export class SelectControlValueAccessor implements ControlValueAccessor {
value: any;
_optionMap: Map<string, any> = new Map<string, any>();
_idCounter: number = 0;
onChange = (_: any) => {};
onTouched = () => {};
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
writeValue(value: any): void {
this.value = value;
var valueString = _buildValueString(this._getOptionId(value), value);
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', valueString);
}
registerOnChange(fn: (value: any) => any): void {
this.onChange = (valueString: string) => { fn(this._getOptionValue(valueString)); };
}
registerOnTouched(fn: () => any): void { this.onTouched = fn; }
_registerOption(): string { return (this._idCounter++).toString(); }
_getOptionId(value: any): string {
for (let id of MapWrapper.keys(this._optionMap)) {
if (looseIdentical(this._optionMap.get(id), value)) return id;
}
return null;
}
_getOptionValue(valueString: string): any {
let value = this._optionMap.get(_extractId(valueString));
return isPresent(value) ? value : valueString;
}
}
/**
* Marks `<option>` as dynamic, so Angular can be notified when options change.
*
@ -28,36 +90,37 @@ const SELECT_VALUE_ACCESSOR = CONST_EXPR(new Provider(
* ```
*/
@Directive({selector: 'option'})
export class NgSelectOption {
}
export class NgSelectOption implements OnDestroy {
id: string;
/**
* The accessor for writing a value and listening to changes on a select element.
*/
@Directive({
selector: 'select[ngControl],select[ngFormControl],select[ngModel]',
host: {'(input)': 'onChange($event.target.value)', '(blur)': 'onTouched()'},
bindings: [SELECT_VALUE_ACCESSOR]
})
export class SelectControlValueAccessor implements ControlValueAccessor {
value: string;
onChange = (_: any) => {};
onTouched = () => {};
constructor(private _renderer: Renderer, private _elementRef: ElementRef,
@Query(NgSelectOption, {descendants: true}) query: QueryList<NgSelectOption>) {
this._updateValueWhenListOfOptionsChanges(query);
constructor(private _element: ElementRef, private _renderer: Renderer,
@Optional() @Host() private _select: SelectControlValueAccessor) {
if (isPresent(this._select)) this.id = this._select._registerOption();
}
writeValue(value: any): void {
this.value = value;
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', value);
@Input('ngValue')
set ngValue(value: any) {
if (this._select == null) return;
this._select._optionMap.set(this.id, value);
this._setElementValue(_buildValueString(this.id, value));
this._select.writeValue(this._select.value);
}
registerOnChange(fn: () => any): void { this.onChange = fn; }
registerOnTouched(fn: () => any): void { this.onTouched = fn; }
@Input('value')
set value(value: any) {
if (this._select == null) return;
this._setElementValue(value);
this._select.writeValue(this._select.value);
}
private _updateValueWhenListOfOptionsChanges(query: QueryList<NgSelectOption>) {
ObservableWrapper.subscribe(query.changes, (_) => this.writeValue(this.value));
_setElementValue(value: string): void {
this._renderer.setElementProperty(this._element.nativeElement, 'value', value);
}
ngOnDestroy() {
if (isPresent(this._select)) {
this._select._optionMap.delete(this.id);
this._select.writeValue(this._select.value);
}
}
}

View File

@ -26,8 +26,10 @@ import {NumberWrapper} from "angular2/src/facade/lang";
*/
export interface Validator { validate(c: modelModule.AbstractControl): {[key: string]: any}; }
const REQUIRED = Validators.required;
const REQUIRED_VALIDATOR =
CONST_EXPR(new Provider(NG_VALIDATORS, {useValue: Validators.required, multi: true}));
CONST_EXPR(new Provider(NG_VALIDATORS, {useValue: REQUIRED, multi: true}));
/**
* A Directive that adds the `required` validator to any controls marked with the

View File

@ -0,0 +1,499 @@
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {
isArray,
isBlank,
isNumber,
isPresent,
isPrimitive,
isString,
Type
} from 'angular2/src/facade/lang';
import {
AttributeMetadata,
DirectiveMetadata,
ComponentMetadata,
ContentChildrenMetadata,
ContentChildMetadata,
InputMetadata,
HostBindingMetadata,
HostListenerMetadata,
OutputMetadata,
PipeMetadata,
ViewMetadata,
ViewChildMetadata,
ViewChildrenMetadata,
ViewQueryMetadata,
QueryMetadata,
} from 'angular2/src/core/metadata';
/**
* The host of the static resolver is expected to be able to provide module metadata in the form of
* ModuleMetadata. Angular 2 CLI will produce this metadata for a module whenever a .d.ts files is
* produced and the module has exported variables or classes with decorators. Module metadata can
* also be produced directly from TypeScript sources by using MetadataCollector in tools/metadata.
*/
export interface StaticReflectorHost {
/**
* Return a ModuleMetadata for the give module.
*
* @param moduleId is a string identifier for a module in the form that would expected in a
* module import of an import statement.
* @returns the metadata for the given module.
*/
getMetadataFor(moduleId: string): {[key: string]: any};
}
/**
* A token representing the a reference to a static type.
*
* This token is unique for a moduleId and name and can be used as a hash table key.
*/
export class StaticType {
constructor(public moduleId: string, public name: string) {}
}
/**
* A static reflector implements enough of the Reflector API that is necessary to compile
* templates statically.
*/
export class StaticReflector {
private typeCache = new Map<string, StaticType>();
private annotationCache = new Map<StaticType, any[]>();
private propertyCache = new Map<StaticType, {[key: string]: any}>();
private parameterCache = new Map<StaticType, any[]>();
private metadataCache = new Map<string, {[key: string]: any}>();
constructor(private host: StaticReflectorHost) { this.initializeConversionMap(); }
/**
* getStatictype produces a Type whose metadata is known but whose implementation is not loaded.
* All types passed to the StaticResolver should be pseudo-types returned by this method.
*
* @param moduleId the module identifier as would be passed to an import statement.
* @param name the name of the type.
*/
public getStaticType(moduleId: string, name: string): StaticType {
let key = `"${moduleId}".${name}`;
let result = this.typeCache.get(key);
if (!isPresent(result)) {
result = new StaticType(moduleId, name);
this.typeCache.set(key, result);
}
return result;
}
public annotations(type: StaticType): any[] {
let annotations = this.annotationCache.get(type);
if (!isPresent(annotations)) {
let classMetadata = this.getTypeMetadata(type);
if (isPresent(classMetadata['decorators'])) {
annotations = (<any[]>classMetadata['decorators'])
.map(decorator => this.convertKnownDecorator(type.moduleId, decorator))
.filter(decorator => isPresent(decorator));
}
this.annotationCache.set(type, annotations);
}
return annotations;
}
public propMetadata(type: StaticType): {[key: string]: any} {
let propMetadata = this.propertyCache.get(type);
if (!isPresent(propMetadata)) {
let classMetadata = this.getTypeMetadata(type);
propMetadata = this.getPropertyMetadata(type.moduleId, classMetadata['members']);
this.propertyCache.set(type, propMetadata);
}
return propMetadata;
}
public parameters(type: StaticType): any[] {
let parameters = this.parameterCache.get(type);
if (!isPresent(parameters)) {
let classMetadata = this.getTypeMetadata(type);
let ctorData = classMetadata['members']['__ctor__'];
if (isPresent(ctorData)) {
let ctor = (<any[]>ctorData).find(a => a['__symbolic'] === 'constructor');
parameters = this.simplify(type.moduleId, ctor['parameters']);
this.parameterCache.set(type, parameters);
}
}
return parameters;
}
private conversionMap = new Map<StaticType, (moduleContext: string, expression: any) => any>();
private initializeConversionMap() {
let core_metadata = 'angular2/src/core/metadata';
let conversionMap = this.conversionMap;
conversionMap.set(this.getStaticType(core_metadata, 'Directive'),
(moduleContext, expression) => {
let p0 = this.getDecoratorParameter(moduleContext, expression, 0);
if (!isPresent(p0)) {
p0 = {};
}
return new DirectiveMetadata({
selector: p0['selector'],
inputs: p0['inputs'],
outputs: p0['outputs'],
events: p0['events'],
host: p0['host'],
bindings: p0['bindings'],
providers: p0['providers'],
exportAs: p0['exportAs'],
queries: p0['queries'],
});
});
conversionMap.set(this.getStaticType(core_metadata, 'Component'),
(moduleContext, expression) => {
let p0 = this.getDecoratorParameter(moduleContext, expression, 0);
if (!isPresent(p0)) {
p0 = {};
}
return new ComponentMetadata({
selector: p0['selector'],
inputs: p0['inputs'],
outputs: p0['outputs'],
properties: p0['properties'],
events: p0['events'],
host: p0['host'],
exportAs: p0['exportAs'],
moduleId: p0['moduleId'],
bindings: p0['bindings'],
providers: p0['providers'],
viewBindings: p0['viewBindings'],
viewProviders: p0['viewProviders'],
changeDetection: p0['changeDetection'],
queries: p0['queries'],
templateUrl: p0['templateUrl'],
template: p0['template'],
styleUrls: p0['styleUrls'],
styles: p0['styles'],
directives: p0['directives'],
pipes: p0['pipes'],
encapsulation: p0['encapsulation']
});
});
conversionMap.set(this.getStaticType(core_metadata, 'Input'),
(moduleContext, expression) => new InputMetadata(
this.getDecoratorParameter(moduleContext, expression, 0)));
conversionMap.set(this.getStaticType(core_metadata, 'Output'),
(moduleContext, expression) => new OutputMetadata(
this.getDecoratorParameter(moduleContext, expression, 0)));
conversionMap.set(this.getStaticType(core_metadata, 'View'), (moduleContext, expression) => {
let p0 = this.getDecoratorParameter(moduleContext, expression, 0);
if (!isPresent(p0)) {
p0 = {};
}
return new ViewMetadata({
templateUrl: p0['templateUrl'],
template: p0['template'],
directives: p0['directives'],
pipes: p0['pipes'],
encapsulation: p0['encapsulation'],
styles: p0['styles'],
});
});
conversionMap.set(this.getStaticType(core_metadata, 'Attribute'),
(moduleContext, expression) => new AttributeMetadata(
this.getDecoratorParameter(moduleContext, expression, 0)));
conversionMap.set(this.getStaticType(core_metadata, 'Query'), (moduleContext, expression) => {
let p0 = this.getDecoratorParameter(moduleContext, expression, 0);
let p1 = this.getDecoratorParameter(moduleContext, expression, 1);
if (!isPresent(p1)) {
p1 = {};
}
return new QueryMetadata(p0, {descendants: p1.descendants, first: p1.first});
});
conversionMap.set(this.getStaticType(core_metadata, 'ContentChildren'),
(moduleContext, expression) => new ContentChildrenMetadata(
this.getDecoratorParameter(moduleContext, expression, 0)));
conversionMap.set(this.getStaticType(core_metadata, 'ContentChild'),
(moduleContext, expression) => new ContentChildMetadata(
this.getDecoratorParameter(moduleContext, expression, 0)));
conversionMap.set(this.getStaticType(core_metadata, 'ViewChildren'),
(moduleContext, expression) => new ViewChildrenMetadata(
this.getDecoratorParameter(moduleContext, expression, 0)));
conversionMap.set(this.getStaticType(core_metadata, 'ViewChild'),
(moduleContext, expression) => new ViewChildMetadata(
this.getDecoratorParameter(moduleContext, expression, 0)));
conversionMap.set(this.getStaticType(core_metadata, 'ViewQuery'),
(moduleContext, expression) => {
let p0 = this.getDecoratorParameter(moduleContext, expression, 0);
let p1 = this.getDecoratorParameter(moduleContext, expression, 1);
if (!isPresent(p1)) {
p1 = {};
}
return new ViewQueryMetadata(p0, {
descendants: p1['descendants'],
first: p1['first'],
});
});
conversionMap.set(this.getStaticType(core_metadata, 'Pipe'), (moduleContext, expression) => {
let p0 = this.getDecoratorParameter(moduleContext, expression, 0);
if (!isPresent(p0)) {
p0 = {};
}
return new PipeMetadata({
name: p0['name'],
pure: p0['pure'],
});
});
conversionMap.set(this.getStaticType(core_metadata, 'HostBinding'),
(moduleContext, expression) => new HostBindingMetadata(
this.getDecoratorParameter(moduleContext, expression, 0)));
conversionMap.set(this.getStaticType(core_metadata, 'HostListener'),
(moduleContext, expression) => new HostListenerMetadata(
this.getDecoratorParameter(moduleContext, expression, 0),
this.getDecoratorParameter(moduleContext, expression, 1)));
}
private convertKnownDecorator(moduleContext: string, expression: {[key: string]: any}): any {
let converter = this.conversionMap.get(this.getDecoratorType(moduleContext, expression));
if (isPresent(converter)) return converter(moduleContext, expression);
return null;
}
private getDecoratorType(moduleContext: string, expression: {[key: string]: any}): StaticType {
if (isMetadataSymbolicCallExpression(expression)) {
let target = expression['expression'];
if (isMetadataSymbolicReferenceExpression(target)) {
let moduleId = this.normalizeModuleName(moduleContext, target['module']);
return this.getStaticType(moduleId, target['name']);
}
}
return null;
}
private getDecoratorParameter(moduleContext: string, expression: {[key: string]: any},
index: number): any {
if (isMetadataSymbolicCallExpression(expression) && isPresent(expression['arguments']) &&
(<any[]>expression['arguments']).length <= index + 1) {
return this.simplify(moduleContext, (<any[]>expression['arguments'])[index]);
}
return null;
}
private getPropertyMetadata(moduleContext: string,
value: {[key: string]: any}): {[key: string]: any} {
if (isPresent(value)) {
let result = {};
StringMapWrapper.forEach(value, (value, name) => {
let data = this.getMemberData(moduleContext, value);
if (isPresent(data)) {
let propertyData = data.filter(d => d['kind'] == "property")
.map(d => d['directives'])
.reduce((p, c) => (<any[]>p).concat(<any[]>c), []);
if (propertyData.length != 0) {
StringMapWrapper.set(result, name, propertyData);
}
}
});
return result;
}
return null;
}
// clang-format off
private getMemberData(moduleContext: string, member: { [key: string]: any }[]): { [key: string]: any }[] {
// clang-format on
let result = [];
if (isPresent(member)) {
for (let item of member) {
result.push({
kind: item['__symbolic'],
directives:
isPresent(item['decorators']) ?
(<any[]>item['decorators'])
.map(decorator => this.convertKnownDecorator(moduleContext, decorator))
.filter(d => isPresent(d)) :
null
});
}
}
return result;
}
/** @internal */
public simplify(moduleContext: string, value: any): any {
let _this = this;
function simplify(expression: any): any {
if (isPrimitive(expression)) {
return expression;
}
if (isArray(expression)) {
let result = [];
for (let item of(<any>expression)) {
result.push(simplify(item));
}
return result;
}
if (isPresent(expression)) {
if (isPresent(expression['__symbolic'])) {
switch (expression['__symbolic']) {
case "binop":
let left = simplify(expression['left']);
let right = simplify(expression['right']);
switch (expression['operator']) {
case '&&':
return left && right;
case '||':
return left || right;
case '|':
return left | right;
case '^':
return left ^ right;
case '&':
return left & right;
case '==':
return left == right;
case '!=':
return left != right;
case '===':
return left === right;
case '!==':
return left !== right;
case '<':
return left < right;
case '>':
return left > right;
case '<=':
return left <= right;
case '>=':
return left >= right;
case '<<':
return left << right;
case '>>':
return left >> right;
case '+':
return left + right;
case '-':
return left - right;
case '*':
return left * right;
case '/':
return left / right;
case '%':
return left % right;
}
return null;
case "pre":
let operand = simplify(expression['operand']);
switch (expression['operator']) {
case '+':
return operand;
case '-':
return -operand;
case '!':
return !operand;
case '~':
return ~operand;
}
return null;
case "index":
let indexTarget = simplify(expression['expression']);
let index = simplify(expression['index']);
if (isPresent(indexTarget) && isPrimitive(index)) return indexTarget[index];
return null;
case "select":
let selectTarget = simplify(expression['expression']);
let member = simplify(expression['member']);
if (isPresent(selectTarget) && isPrimitive(member)) return selectTarget[member];
return null;
case "reference":
let referenceModuleName =
_this.normalizeModuleName(moduleContext, expression['module']);
let referenceModule = _this.getModuleMetadata(referenceModuleName);
let referenceValue = referenceModule['metadata'][expression['name']];
if (isClassMetadata(referenceValue)) {
// Convert to a pseudo type
return _this.getStaticType(referenceModuleName, expression['name']);
}
return _this.simplify(referenceModuleName, referenceValue);
case "call":
return null;
}
return null;
}
let result = {};
StringMapWrapper.forEach(expression, (value, name) => { result[name] = simplify(value); });
return result;
}
return null;
}
return simplify(value);
}
private getModuleMetadata(module: string): {[key: string]: any} {
let moduleMetadata = this.metadataCache.get(module);
if (!isPresent(moduleMetadata)) {
moduleMetadata = this.host.getMetadataFor(module);
if (!isPresent(moduleMetadata)) {
moduleMetadata = {__symbolic: "module", module: module, metadata: {}};
}
this.metadataCache.set(module, moduleMetadata);
}
return moduleMetadata;
}
private getTypeMetadata(type: StaticType): {[key: string]: any} {
let moduleMetadata = this.getModuleMetadata(type.moduleId);
let result = moduleMetadata['metadata'][type.name];
if (!isPresent(result)) {
result = {__symbolic: "class"};
}
return result;
}
private normalizeModuleName(from: string, to: string): string {
if (to.startsWith('.')) {
return pathTo(from, to);
}
return to;
}
}
function isMetadataSymbolicCallExpression(expression: any): boolean {
return !isPrimitive(expression) && !isArray(expression) && expression['__symbolic'] == 'call';
}
function isMetadataSymbolicReferenceExpression(expression: any): boolean {
return !isPrimitive(expression) && !isArray(expression) &&
expression['__symbolic'] == 'reference';
}
function isClassMetadata(expression: any): boolean {
return !isPrimitive(expression) && !isArray(expression) && expression['__symbolic'] == 'class';
}
function splitPath(path: string): string[] {
return path.split(/\/|\\/g);
}
function resolvePath(pathParts: string[]): string {
let result = [];
ListWrapper.forEachWithIndex(pathParts, (part, index) => {
switch (part) {
case '':
case '.':
if (index > 0) return;
break;
case '..':
if (index > 0 && result.length != 0) result.pop();
return;
}
result.push(part);
});
return result.join('/');
}
function pathTo(from: string, to: string): string {
let result = to;
if (to.startsWith('.')) {
let fromParts = splitPath(from);
fromParts.pop(); // remove the file name.
let toParts = splitPath(to);
result = resolvePath(fromParts.concat(toParts));
}
return result;
}

View File

@ -31,7 +31,6 @@ export interface IterableDifferFactory {
/**
* A repository of different iterable diffing strategies used by NgFor, NgClass, and others.
*/
@Injectable()
@CONST()
export class IterableDiffers {
constructor(public factories: IterableDifferFactory[]) {}

View File

@ -23,7 +23,6 @@ export interface KeyValueDifferFactory {
/**
* A repository of different Map diffing strategies used by NgClass, NgStyle, and others.
*/
@Injectable()
@CONST()
export class KeyValueDiffers {
constructor(public factories: KeyValueDifferFactory[]) {}

View File

@ -59,7 +59,6 @@ class UIMessageBusSource extends IsolateMessageBusSource {
* Wrapper class that exposes the Isolate
* and underlying {@link MessageBus} for lower level message passing.
*/
@Injectable()
class WebWorkerInstance {
Isolate worker;
MessageBus bus;

View File

@ -84,6 +84,11 @@ function matchUrlSegment(str: string): string {
var match = RegExpWrapper.firstMatch(SEGMENT_RE, str);
return isPresent(match) ? match[0] : '';
}
var QUERY_PARAM_VALUE_RE = RegExpWrapper.create('^[^\\(\\)\\?;&#]+');
function matchUrlQueryParamValue(str: string): string {
var match = RegExpWrapper.firstMatch(QUERY_PARAM_VALUE_RE, str);
return isPresent(match) ? match[0] : '';
}
export class UrlParser {
private _remaining: string;
@ -163,10 +168,10 @@ export class UrlParser {
parseQueryParams(): {[key: string]: any} {
var params: {[key: string]: any} = {};
this.capture('?');
this.parseParam(params);
this.parseQueryParam(params);
while (this._remaining.length > 0 && this.peekStartsWith('&')) {
this.capture('&');
this.parseParam(params);
this.parseQueryParam(params);
}
return params;
}
@ -199,6 +204,25 @@ export class UrlParser {
params[key] = value;
}
parseQueryParam(params: {[key: string]: any}): void {
var key = matchUrlSegment(this._remaining);
if (isBlank(key)) {
return;
}
this.capture(key);
var value: any = true;
if (this.peekStartsWith('=')) {
this.capture('=');
var valueMatch = matchUrlQueryParamValue(this._remaining);
if (isPresent(valueMatch)) {
value = valueMatch;
this.capture(value);
}
}
params[key] = value;
}
parseAuxiliaryRoutes(): Url[] {
var routes: Url[] = [];
this.capture('(');

View File

@ -160,7 +160,7 @@ export class DowngradeNg2ComponentAdapter {
}
registerCleanup() {
this.element.bind('$remove', () => this.viewManager.destroyRootHostView(this.hostViewRef));
this.element.bind('$destroy', () => this.viewManager.destroyRootHostView(this.hostViewRef));
}
}

View File

@ -94,14 +94,14 @@ export function main() {
});
it("should return select accessor when provided", () => {
var selectAccessor = new SelectControlValueAccessor(null, null, new QueryList<any>());
var selectAccessor = new SelectControlValueAccessor(null, null);
expect(selectValueAccessor(dir, [defaultAccessor, selectAccessor]))
.toEqual(selectAccessor);
});
it("should throw when more than one build-in accessor is provided", () => {
var checkboxAccessor = new CheckboxControlValueAccessor(null, null);
var selectAccessor = new SelectControlValueAccessor(null, null, new QueryList<any>());
var selectAccessor = new SelectControlValueAccessor(null, null);
expect(() => selectValueAccessor(dir, [checkboxAccessor, selectAccessor])).toThrowError();
});

View File

@ -38,7 +38,7 @@ import {
import {Provider, forwardRef, Input} from 'angular2/core';
import {By} from 'angular2/platform/browser';
import {ListWrapper} from 'angular2/src/facade/collection';
import {ObservableWrapper} from 'angular2/src/facade/async';
import {ObservableWrapper, TimerWrapper} from 'angular2/src/facade/async';
import {CONST_EXPR} from 'angular2/src/facade/lang';
import {PromiseWrapper} from "angular2/src/facade/promise";
@ -357,8 +357,30 @@ export function main() {
});
}));
it("should support <select>",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
describe("should support <select>", () => {
it("with basic selection",
inject([TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async) => {
var t = `<select>
<option value="SF"></option>
<option value="NYC"></option>
</select>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((fixture) => {
fixture.detectChanges();
var select = fixture.debugElement.query(By.css("select"));
var sfOption = fixture.debugElement.query(By.css("option"));
expect(select.nativeElement.value).toEqual("SF");
expect(sfOption.nativeElement.selected).toBe(true);
async.done();
});
}));
it("with ngControl",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder,
async) => {
var t = `<div [ngFormModel]="form">
<select ngControl="city">
<option value="SF"></option>
@ -373,10 +395,12 @@ export function main() {
var select = fixture.debugElement.query(By.css("select"));
var sfOption = fixture.debugElement.query(By.css("option"));
expect(select.nativeElement.value).toEqual('SF');
expect(select.nativeElement.value).toEqual("SF");
expect(sfOption.nativeElement.selected).toBe(true);
select.nativeElement.value = 'NYC';
select.nativeElement.value = "NYC";
dispatchEvent(select.nativeElement, "input");
expect(fixture.debugElement.componentInstance.form.value).toEqual({"city": 'NYC'});
@ -385,7 +409,7 @@ export function main() {
});
}));
it("should support <select> with a dynamic list of options",
it("with a dynamic list of options",
inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
var t = `<div [ngFormModel]="form">
<select ngControl="city">
@ -400,14 +424,189 @@ export function main() {
fixture.debugElement.componentInstance.form =
new ControlGroup({"city": new Control("NYC")});
fixture.debugElement.componentInstance.data = ['SF', 'NYC'];
fixture.detectChanges();
tick();
var select = fixture.debugElement.query(By.css('select'));
expect(select.nativeElement.value).toEqual('NYC');
var select = fixture.debugElement.query(By.css("select"));
expect(select.nativeElement.value).toEqual("NYC");
})));
it("with option values that are objects",
inject([TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async) => {
var t = `<div>
<select [(ngModel)]="selectedCity">
<option *ngFor="#c of list" [ngValue]="c">{{c['name']}}</option>
</select>
</div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((fixture) => {
var testComp = fixture.debugElement.componentInstance;
testComp.list = [{"name": "SF"}, {"name": "NYC"}, {"name": "Buffalo"}];
testComp.selectedCity = testComp.list[1];
fixture.detectChanges();
var select = fixture.debugElement.query(By.css("select"));
var nycOption = fixture.debugElement.queryAll(By.css("option"))[1];
expect(select.nativeElement.value).toEqual("1: Object");
expect(nycOption.nativeElement.selected).toBe(true);
select.nativeElement.value = "2: Object";
dispatchEvent(select.nativeElement, "input");
fixture.detectChanges();
TimerWrapper.setTimeout(() => {
expect(testComp.selectedCity['name']).toEqual("Buffalo");
async.done();
}, 0);
});
}));
it("when new options are added",
inject([TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async) => {
var t = `<div>
<select [(ngModel)]="selectedCity">
<option *ngFor="#c of list" [ngValue]="c">{{c['name']}}</option>
</select>
</div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((fixture) => {
var testComp: MyComp = fixture.debugElement.componentInstance;
testComp.list = [{"name": "SF"}, {"name": "NYC"}];
testComp.selectedCity = testComp.list[1];
fixture.detectChanges();
testComp.list.push({"name": "Buffalo"});
testComp.selectedCity = testComp.list[2];
fixture.detectChanges();
var select = fixture.debugElement.query(By.css("select"));
var buffalo = fixture.debugElement.queryAll(By.css("option"))[2];
expect(select.nativeElement.value).toEqual("2: Object");
expect(buffalo.nativeElement.selected).toBe(true);
async.done();
});
}));
it("when options are removed",
inject([TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async) => {
var t = `<div>
<select [(ngModel)]="selectedCity">
<option *ngFor="#c of list" [ngValue]="c">{{c}}</option>
</select>
</div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((fixture) => {
var testComp: MyComp = fixture.debugElement.componentInstance;
testComp.list = [{"name": "SF"}, {"name": "NYC"}];
testComp.selectedCity = testComp.list[1];
fixture.detectChanges();
var select = fixture.debugElement.query(By.css("select"));
expect(select.nativeElement.value).toEqual("1: Object");
testComp.list.pop();
fixture.detectChanges();
expect(select.nativeElement.value).not.toEqual("1: Object");
async.done();
});
}));
it("when option values change identity while tracking by index",
inject([TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async) => {
var t = `<div>
<select [(ngModel)]="selectedCity">
<option *ngFor="#c of list; trackBy:customTrackBy" [ngValue]="c">{{c}}</option>
</select>
</div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((fixture) => {
var testComp = fixture.debugElement.componentInstance;
testComp.list = [{"name": "SF"}, {"name": "NYC"}];
testComp.selectedCity = testComp.list[0];
fixture.detectChanges();
testComp.list[1] = "Buffalo";
testComp.selectedCity = testComp.list[1];
fixture.detectChanges();
var select = fixture.debugElement.query(By.css("select"));
var buffalo = fixture.debugElement.queryAll(By.css("option"))[1];
expect(select.nativeElement.value).toEqual("1: Buffalo");
expect(buffalo.nativeElement.selected).toBe(true);
async.done();
});
}));
it("with duplicate option values",
inject([TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async) => {
var t = `<div>
<select [(ngModel)]="selectedCity">
<option *ngFor="#c of list" [ngValue]="c">{{c}}</option>
</select>
</div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((fixture) => {
var testComp = fixture.debugElement.componentInstance;
testComp.list = [{"name": "NYC"}, {"name": "SF"}, {"name": "SF"}];
testComp.selectedCity = testComp.list[0];
fixture.detectChanges();
testComp.selectedCity = testComp.list[1];
fixture.detectChanges();
var select = fixture.debugElement.query(By.css("select"));
var firstSF = fixture.debugElement.queryAll(By.css("option"))[1];
expect(select.nativeElement.value).toEqual("1: Object");
expect(firstSF.nativeElement.selected).toBe(true);
async.done();
});
}));
it("when option values have same content, but different identities",
inject([TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async) => {
var t = `<div>
<select [(ngModel)]="selectedCity">
<option *ngFor="#c of list" [ngValue]="c">{{c['name']}}</option>
</select>
</div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((fixture) => {
var testComp = fixture.debugElement.componentInstance;
testComp.list = [{"name": "SF"}, {"name": "NYC"}, {"name": "NYC"}];
testComp.selectedCity = testComp.list[0];
fixture.detectChanges();
testComp.selectedCity = testComp.list[2];
fixture.detectChanges();
var select = fixture.debugElement.query(By.css("select"));
var secondNYC = fixture.debugElement.queryAll(By.css("option"))[2];
expect(select.nativeElement.value).toEqual("2: Object");
expect(secondNYC.nativeElement.selected).toBe(true);
async.done();
});
}));
});
it("should support custom value accessors",
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var t = `<div [ngFormModel]="form">
@ -849,7 +1048,6 @@ export function main() {
<input type="radio" name="food" ngControl="chicken" [(ngModel)]="data['chicken1']">
<input type="radio" name="food" ngControl="fish" [(ngModel)]="data['fish1']">
</form>
<form>
<input type="radio" name="food" ngControl="chicken" [(ngModel)]="data['chicken2']">
<input type="radio" name="food" ngControl="fish" [(ngModel)]="data['fish2']">
@ -1134,6 +1332,9 @@ class MyComp {
form: any;
name: string;
data: any;
list: any[];
selectedCity: any;
customTrackBy(index: number, obj: any): number { return index; };
}
function sortedClassList(el) {

View File

@ -0,0 +1,441 @@
import {
ddescribe,
describe,
xdescribe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
AsyncTestCompleter,
inject,
beforeEachProviders
} from 'angular2/testing_internal';
import {StaticReflector, StaticReflectorHost} from 'angular2/src/compiler/static_reflector';
export function main() {
describe('StaticRefelector', () => {
it('should get annotations for NgFor', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
let NgFor = reflector.getStaticType('angular2/src/common/directives/ng_for', 'NgFor');
let annotations = reflector.annotations(NgFor);
expect(annotations.length).toEqual(1);
let annotation = annotations[0];
expect(annotation.selector).toEqual('[ngFor][ngForOf]');
expect(annotation.inputs).toEqual(['ngForTrackBy', 'ngForOf', 'ngForTemplate']);
});
it('should get constructor for NgFor', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
let NgFor = reflector.getStaticType('angular2/src/common/directives/ng_for', 'NgFor');
let ViewContainerRef = reflector.getStaticType('angular2/src/core/linker/view_container_ref',
'ViewContainerRef');
let TemplateRef =
reflector.getStaticType('angular2/src/core/linker/template_ref', 'TemplateRef');
let IterableDiffers = reflector.getStaticType(
'angular2/src/core/change_detection/differs/iterable_differs', 'IterableDiffers');
let ChangeDetectorRef = reflector.getStaticType(
'angular2/src/core/change_detection/change_detector_ref', 'ChangeDetectorRef');
let parameters = reflector.parameters(NgFor);
expect(parameters)
.toEqual([ViewContainerRef, TemplateRef, IterableDiffers, ChangeDetectorRef]);
});
it('should get annotations for HeroDetailComponent', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
let HeroDetailComponent =
reflector.getStaticType('./app/hero-detail.component', 'HeroDetailComponent');
let annotations = reflector.annotations(HeroDetailComponent);
expect(annotations.length).toEqual(1);
let annotation = annotations[0];
expect(annotation.selector).toEqual('my-hero-detail');
});
it('should get propMetadata for HeroDetailComponent', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
let HeroDetailComponent =
reflector.getStaticType('./app/hero-detail.component', 'HeroDetailComponent');
let props = reflector.propMetadata(HeroDetailComponent);
expect(props['hero']).toBeTruthy();
});
it('should simplify primitive into itself', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', 1)).toBe(1);
expect(reflector.simplify('', true)).toBe(true);
expect(reflector.simplify('', "some value")).toBe("some value");
});
it('should simplify an array into a copy of the array', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', [1, 2, 3])).toEqual([1, 2, 3]);
});
it('should simplify an object to a copy of the object', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
let expr = {a: 1, b: 2, c: 3};
expect(reflector.simplify('', expr)).toEqual(expr);
});
it('should simplify &&', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '&&', left: true, right: true}))).toBe(true);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '&&', left: true, right: false}))).toBe(false);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '&&', left: false, right: true}))).toBe(false);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '&&', left: false, right: false}))).toBe(false);
});
it('should simplify ||', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '||', left: true, right: true}))).toBe(true);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '||', left: true, right: false}))).toBe(true);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '||', left: false, right: true}))).toBe(true);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '||', left: false, right: false}))).toBe(false);
});
it('should simplify &', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '&', left: 0x22, right: 0x0F}))).toBe(0x22 & 0x0F);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '&', left: 0x22, right: 0xF0}))).toBe(0x22 & 0xF0);
});
it('should simplify |', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '|', left: 0x22, right: 0x0F}))).toBe(0x22 | 0x0F);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '|', left: 0x22, right: 0xF0}))).toBe(0x22 | 0xF0);
});
it('should simplify ^', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '|', left: 0x22, right: 0x0F}))).toBe(0x22 | 0x0F);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '|', left: 0x22, right: 0xF0}))).toBe(0x22 | 0xF0);
});
it('should simplify ==', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '==', left: 0x22, right: 0x22}))).toBe(0x22 == 0x22);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '==', left: 0x22, right: 0xF0}))).toBe(0x22 == 0xF0);
});
it('should simplify !=', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '!=', left: 0x22, right: 0x22}))).toBe(0x22 != 0x22);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '!=', left: 0x22, right: 0xF0}))).toBe(0x22 != 0xF0);
});
it('should simplify ===', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '===', left: 0x22, right: 0x22}))).toBe(0x22 === 0x22);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '===', left: 0x22, right: 0xF0}))).toBe(0x22 === 0xF0);
});
it('should simplify !==', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '!==', left: 0x22, right: 0x22}))).toBe(0x22 !== 0x22);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '!==', left: 0x22, right: 0xF0}))).toBe(0x22 !== 0xF0);
});
it('should simplify >', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '>', left: 1, right: 1}))).toBe(1 > 1);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '>', left: 1, right: 0}))).toBe(1 > 0);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '>', left: 0, right: 1}))).toBe(0 > 1);
});
it('should simplify >=', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '>=', left: 1, right: 1}))).toBe(1 >= 1);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '>=', left: 1, right: 0}))).toBe(1 >= 0);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '>=', left: 0, right: 1}))).toBe(0 >= 1);
});
it('should simplify <=', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '<=', left: 1, right: 1}))).toBe(1 <= 1);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '<=', left: 1, right: 0}))).toBe(1 <= 0);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '<=', left: 0, right: 1}))).toBe(0 <= 1);
});
it('should simplify <', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '<', left: 1, right: 1}))).toBe(1 < 1);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '<', left: 1, right: 0}))).toBe(1 < 0);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '<', left: 0, right: 1}))).toBe(0 < 1);
});
it('should simplify <<', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '<<', left: 0x55, right: 2}))).toBe(0x55 << 2);
});
it('should simplify >>', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '>>', left: 0x55, right: 2}))).toBe(0x55 >> 2);
});
it('should simplify +', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '+', left: 0x55, right: 2}))).toBe(0x55 + 2);
});
it('should simplify -', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '-', left: 0x55, right: 2}))).toBe(0x55 - 2);
});
it('should simplify *', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '*', left: 0x55, right: 2}))).toBe(0x55 * 2);
});
it('should simplify /', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '/', left: 0x55, right: 2}))).toBe(0x55 / 2);
});
it('should simplify %', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '%', left: 0x55, right: 2}))).toBe(0x55 % 2);
});
it('should simplify prefix -', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'pre', operator: '-', operand: 2}))).toBe(-2);
});
it('should simplify prefix ~', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'pre', operator: '~', operand: 2}))).toBe(~2);
});
it('should simplify prefix !', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({ __symbolic: 'pre', operator: '!', operand: true}))).toBe(!true);
expect(reflector.simplify('', ({ __symbolic: 'pre', operator: '!', operand: false}))).toBe(!false);
});
it('should simpify an array index', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(reflector.simplify('', ({__symbolic: "index", expression: [1, 2, 3], index: 2})))
.toBe(3);
});
it('should simplify an object index', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
let expr = {__symbolic: "select", expression: {a: 1, b: 2, c: 3}, member: "b"};
expect(reflector.simplify('', expr)).toBe(2);
});
it('should simplify a module reference', () => {
let host = new MockReflectorHost();
let reflector = new StaticReflector(host);
expect(
reflector.simplify('./cases', ({__symbolic: "reference", module: "./extern", name: "s"})))
.toEqual("s");
});
});
}
class MockReflectorHost implements StaticReflectorHost {
getMetadataFor(moduleId: string): any {
return {
'angular2/src/common/directives/ng_for':
{
"__symbolic": "module",
"module": "./ng_for",
"metadata":
{
"NgFor":
{
"__symbolic": "class",
"decorators":
[
{
"__symbolic": "call",
"expression": {
"__symbolic": "reference",
"name": "Directive",
"module": "../../core/metadata"
},
"arguments":
[
{
"selector": "[ngFor][ngForOf]",
"inputs": ["ngForTrackBy", "ngForOf", "ngForTemplate"]
}
]
}
],
"members":
{
"__ctor__": [
{
"__symbolic": "constructor",
"parameters":
[
{
"__symbolic": "reference",
"module": "../../core/linker/view_container_ref",
"name": "ViewContainerRef"
},
{
"__symbolic": "reference",
"module": "../../core/linker/template_ref",
"name": "TemplateRef"
},
{
"__symbolic": "reference",
"module":
"../../core/change_detection/differs/iterable_differs",
"name": "IterableDiffers"
},
{
"__symbolic": "reference",
"module":
"../../core/change_detection/change_detector_ref",
"name": "ChangeDetectorRef"
}
]
}
]
}
}
}
},
'angular2/src/core/linker/view_container_ref':
{
"module": "./view_container_ref",
"metadata": {"ViewContainerRef": {"__symbolic": "class"}}
},
'angular2/src/core/linker/template_ref':
{"module": "./template_ref", "metadata": {"TemplateRef": {"__symbolic": "class"}}},
'angular2/src/core/change_detection/differs/iterable_differs':
{
"module": "./iterable_differs",
"metadata": {"IterableDiffers": {"__symbolic": "class"}}
},
'angular2/src/core/change_detection/change_detector_ref':
{
"module": "./change_detector_ref",
"metadata": {"ChangeDetectorRef": {"__symbolic": "class"}}
},
'./app/hero-detail.component':
{
"__symbolic": "module",
"module": "./hero-detail.component",
"metadata":
{
"HeroDetailComponent":
{
"__symbolic": "class",
"decorators":
[
{
"__symbolic": "call",
"expression": {
"__symbolic": "reference",
"name": "Component",
"module": "angular2/src/core/metadata"
},
"arguments": [
{
"selector": "my-hero-detail",
"template": "\n <div *ngIf=\"hero\">\n <h2>{{hero.name}} details!</h2>\n <div><label>id: </label>{{hero.id}}</div>\n <div>\n <label>name: </label>\n <input [(ngModel)]=\"hero.name\" placeholder=\"name\"/>\n </div>\n </div>\n"
}
]
}
],
"members":
{
"hero": [
{
"__symbolic": "property",
"decorators":
[
{
"__symbolic": "call",
"expression":
{
"__symbolic": "reference",
"name": "Input",
"module": "angular2/src/core/metadata"
}
}
]
}
]
}
}
}
},
'./extern': {
"__symbolic": "module", module: './extern', metadata: { s: "s" }
}
}
[moduleId];
}
}

View File

@ -124,5 +124,12 @@ export function main() {
var url = urlParser.parse('hello/there;sort=asc(modal)?friend=true');
expect(url.toString()).toEqual('hello/there;sort=asc(modal)?friend=true');
});
it('should allow slashes within query parameters', () => {
var url = urlParser.parse(
'hello?code=4/B8o0n_Y7XZTb-pVKBw5daZyGAUbMljyLf7uNgTy6ja8&scope=https://www.googleapis.com/auth/analytics');
expect(url.toString())
.toEqual(
'hello?code=4/B8o0n_Y7XZTb-pVKBw5daZyGAUbMljyLf7uNgTy6ja8&scope=https://www.googleapis.com/auth/analytics');
});
});
}

View File

@ -230,6 +230,35 @@ export function main() {
});
}));
it('should properly run cleanup when ng1 directive is destroyed',
inject([AsyncTestCompleter], (async) => {
var adapter: UpgradeAdapter = new UpgradeAdapter();
var ng1Module = angular.module('ng1', []);
var onDestroyed: EventEmitter<string> = new EventEmitter<string>();
ng1Module.directive('ng1', () => {
return {
template: '<div ng-if="!destroyIt"><ng2></ng2></div>',
controller: function($rootScope, $timeout) {
$timeout(function() { $rootScope.destroyIt = true; });
}
};
});
var Ng2 = Component({selector: 'ng2', template: 'test'})
.Class({
constructor: function() {},
ngOnDestroy: function() { onDestroyed.emit('destroyed'); }
});
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
var element = html('<ng1></ng1>');
adapter.bootstrap(element, ['ng1'])
.ready((ref) => {onDestroyed.subscribe(() => {
ref.dispose();
async.done();
})});
}));
});
describe('upgrade ng1 component', () => {

View File

@ -1,6 +1,7 @@
library angular2.transform.common.async_string_writer;
import 'dart:async';
import 'package:analyzer/src/generated/java_core.dart';
/// [PrintWriter] implementation that allows asynchronous printing via
@ -18,6 +19,7 @@ class AsyncStringWriter extends PrintWriter {
AsyncStringWriter([Object content = ""]) : this._(new StringBuffer(content));
@override
void print(x) {
_curr.write(x);
}
@ -54,6 +56,7 @@ class AsyncStringWriter extends PrintWriter {
}).whenComplete(_semaphoreDecrementAndCleanup);
}
@override
String toString() => _bufs.map((buf) => '$buf').join('(async gap)');
void _semaphoreIncrement() {

View File

@ -1,6 +1,7 @@
library angular2.src.transform.common.logging;
import 'dart:async';
import 'dart:io' show stderr;
import 'package:barback/barback.dart';
import 'package:source_span/source_span.dart';
@ -49,12 +50,16 @@ void _logElapsed(Stopwatch timer, String operationName, AssetId assetId) {
log.fine(buf.toString(), asset: assetId);
}
/// Prints logged messages to the console.
/// Writes logged messages to the provided [StringSink].
///
/// A simple implementation of [TransformLogger] that prints messages to the
/// console and discards `asset` and `span` information.
class PrintLogger implements TransformLogger {
void _printWithPrefix(prefix, msg) => print('$prefix: $msg');
/// A simple implementation of [TransformLogger] that writes messages to a
/// [StringSink] and discards `asset` and `span` information.
class SinkLogger implements TransformLogger {
final StringSink _sink;
SinkLogger(this._sink);
void _printWithPrefix(prefix, msg) => _sink.writeln('$prefix: $msg');
@override
void info(msg, {AssetId asset, SourceSpan span}) =>
@ -74,6 +79,14 @@ class PrintLogger implements TransformLogger {
}
}
/// Prints logged messages to stderr.
///
/// A simple implementation of [TransformLogger] that prints messages to
/// [stderr] and discards `asset` and `span` information.
class PrintLogger extends SinkLogger {
PrintLogger() : super(stderr);
}
class PrintLoggerError extends Error {
final String message;
final AssetId asset;

View File

@ -1,12 +1,15 @@
library angular2.transform.common.options_reader;
import 'dart:io';
import 'package:barback/barback.dart';
import 'annotation_matcher.dart';
import 'mirror_mode.dart';
import 'options.dart';
import './url_resolver.dart';
TransformerOptions parseBarbackSettings(BarbackSettings settings) {
TransformerOptions parseBarbackSettings(BarbackSettings settings) {
var config = settings.configuration;
var entryPoints = _readStringList(config, ENTRY_POINT_PARAM);
var initReflector =
@ -79,7 +82,8 @@ List<String> _readStringList(Map config, String paramName) {
error = true;
}
if (error) {
print('Invalid value for "$paramName" in the Angular 2 transformer.');
stderr.writeln(
'Invalid value for "$paramName" in the Angular 2 transformer.');
}
return result;
}
@ -111,7 +115,7 @@ List<ClassDescriptor> _readCustomAnnotations(Map config) {
}
}
if (error) {
print(CUSTOM_ANNOTATIONS_ERROR);
stderr.writeln(CUSTOM_ANNOTATIONS_ERROR);
}
return descriptors;
}
@ -121,7 +125,7 @@ const CUSTOM_ANNOTATIONS_ERROR = '''
Expected something that looks like the following:
transformers:
- angular2:
- angular2[/transform/codegen]:
custom_annotations:
- name: MyAnnotation
import: 'package:my_package/my_annotation.dart'

View File

@ -1,6 +1,6 @@
{
"name": "angular-srcs",
"version": "2.0.0-beta.13",
"version": "2.0.0-beta.14",
"branchPattern": "2.0.*",
"description": "Angular 2 - a web framework for modern web apps",
"homepage": "https://github.com/angular/angular",

View File

@ -777,6 +777,11 @@ const COMMON = [
'NgPlural.ngPlural=(value:number)',
'NgPluralCase',
'NgSelectOption',
'NgSelectOption.constructor(_element:ElementRef, _renderer:Renderer, _select:SelectControlValueAccessor)',
'NgSelectOption.id:string',
'NgSelectOption.ngOnDestroy():any',
'NgSelectOption.ngValue=(value:any)',
'NgSelectOption.value=(value:any)',
'NgStyle',
'NgStyle.constructor(_differs:KeyValueDiffers, _ngEl:ElementRef, _renderer:Renderer)',
'NgStyle.ngDoCheck():any',
@ -798,12 +803,12 @@ const COMMON = [
'ReplacePipe.transform(value:any, args:any[]):any',
'RequiredValidator',
'SelectControlValueAccessor',
'SelectControlValueAccessor.constructor(_renderer:Renderer, _elementRef:ElementRef, query:QueryList<NgSelectOption>)',
'SelectControlValueAccessor.constructor(_renderer:Renderer, _elementRef:ElementRef)',
'SelectControlValueAccessor.onChange:any',
'SelectControlValueAccessor.onTouched:any',
'SelectControlValueAccessor.registerOnChange(fn:() => any):void',
'SelectControlValueAccessor.registerOnChange(fn:(value: any) => any):void',
'SelectControlValueAccessor.registerOnTouched(fn:() => any):void',
'SelectControlValueAccessor.value:string',
'SelectControlValueAccessor.value:any',
'SelectControlValueAccessor.writeValue(value:any):void',
'SlicePipe',
'SlicePipe.transform(value:any, args:any[]):any',