Compare commits

...

16 Commits
5.0.5 ... 2.0.1

Author SHA1 Message Date
712d1a7c37 chore(release): v2.0.1 2016-09-23 11:28:36 -07:00
16601f9359 docs(changelog): add changelog for 2.0.1 2016-09-23 10:50:42 -07:00
b81e2e7a31 fix(upgrade): allow attribute selectors for components in ng2 which are not part of upgrade (#11808)
fixes #11280
2016-09-23 10:48:47 -07:00
98fac36706 docs(Component): API docs for .encapsulation and .interpolation 2016-09-23 10:22:24 -07:00
3e780c032e refactor: misc cleanup 2016-09-23 10:22:05 -07:00
e09882180e refactor(common): cleanup (#11668) 2016-09-23 10:21:58 -07:00
0e18c57a17 docs(core): mark TestBed as stable api and add preliminary docs (#11767)
TestBed was accidentaly ommited from the 'stable' api list during the API sweep before final. We do consider it to be stable.
2016-09-23 10:21:50 -07:00
51e2b9c073 docs(contributing): remove preview references (#11795) 2016-09-23 10:21:44 -07:00
f218e240d3 ci(BrowserStack): add Safari 10 (#11796) 2016-09-23 10:21:31 -07:00
af6b219f8e refactor(TemplateParser): clearer error message for on* binding (#11802)
fixes #11756
2016-09-23 10:21:15 -07:00
20addf5f9f chore(ISSUE_TEMPLATE): update Angular version field (#11821) 2016-09-23 10:21:02 -07:00
2860418a3c fix(forms): disable all radios with disable() 2016-09-23 10:19:58 -07:00
39e251eea7 fix(forms): support unbound disabled in ngModel (#11736) 2016-09-23 10:19:46 -07:00
d7d716d5db chore(zone.js): update to 0.6.25 (#11725) 2016-09-23 10:19:30 -07:00
a95d65241c fix(compiler): Safe property access expressions work in event bindings (#11724) 2016-09-23 10:19:24 -07:00
fdb22bd185 refactor: misc cleanup (#11654) 2016-09-23 10:18:38 -07:00
72 changed files with 552 additions and 402 deletions

View File

@ -20,7 +20,7 @@
**Please tell us about your environment:**
<!-- Operating system, IDE, package manager, HTTP server, ... -->
* **Angular version:** 2.0.0-rc.X
* **Angular version:** 2.0.X
<!-- Check whether this is still an issue in the most recent Angular version -->
* **Browser:** [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]

View File

@ -1,3 +1,22 @@
<a name="2.0.1"></a>
## [2.0.1](https://github.com/angular/angular/compare/2.0.0...2.0.1) (2016-09-23)
### Bug Fixes
* **common:** fix ngOnChanges signature of NgTemplateOutlet directive ([14ee759](https://github.com/angular/angular/commit/14ee759))
* **compiler:** `[attribute~=value]` selector ([#11696](https://github.com/angular/angular/issues/11696)) ([734b8b8](https://github.com/angular/angular/commit/734b8b8)), closes [#9644](https://github.com/angular/angular/issues/9644)
* **compiler:** safe property access expressions work in event bindings ([#11724](https://github.com/angular/angular/issues/11724)) ([a95d652](https://github.com/angular/angular/commit/a95d652))
* **compiler:** throw when Component.moduleId is not a string ([bd4045b](https://github.com/angular/angular/commit/bd4045b)), closes [#11590](https://github.com/angular/angular/issues/11590)
* **compiler:** do not provide I18N values when they're not specified ([03aedbe](https://github.com/angular/angular/commit/03aedbe)), closes [#11643](https://github.com/angular/angular/issues/11643)
* **core:** ContentChild descendants should be queried by default ([0dc15eb](https://github.com/angular/angular/commit/0dc15eb)), closes [#1645](https://github.com/angular/angular/issues/1645)
* **forms:** disable all radios with disable() ([2860418](https://github.com/angular/angular/commit/2860418))
* **forms:** make setDisabledState optional for reactive form directives ([#11731](https://github.com/angular/angular/issues/11731)) ([51d73d3](https://github.com/angular/angular/commit/51d73d3)), closes [#11719](https://github.com/angular/angular/issues/11719)
* **forms:** support unbound disabled in ngModel ([#11736](https://github.com/angular/angular/issues/11736)) ([39e251e](https://github.com/angular/angular/commit/39e251e))
* **upgrade:** allow attribute selectors for components in ng2 which are not part of upgrade ([#11808](https://github.com/angular/angular/issues/11808)) ([b81e2e7](https://github.com/angular/angular/commit/b81e2e7)), closes [#11280](https://github.com/angular/angular/issues/11280)
<a name="2.0.0"></a>
# [2.0.0](https://github.com/angular/angular/compare/2.0.0-rc.7...2.0.0) (2016-09-14)

View File

@ -18,7 +18,7 @@ Help us keep Angular open and inclusive. Please read and follow our [Code of Con
## <a name="question"></a> Got a Question or Problem?
If you have questions about how to *use* Angular, please direct them to the [Google Group][angular-group]
discussion list or [StackOverflow][stackoverflow]. Please note that Angular 2 is still in early developer preview, and the core team's capacity to answer usage questions is limited. We are also available on [Gitter][gitter].
discussion list or [StackOverflow][stackoverflow]. Please note that the Angular team's capacity to answer usage questions is limited. We are also available on [Gitter][gitter].
## <a name="issue"></a> Found an Issue?
If you find a bug in the source code, you can help us by
@ -28,8 +28,7 @@ If you find a bug in the source code, you can help us by
## <a name="feature"></a> Want a Feature?
You can *request* a new feature by [submitting an issue](#submit-issue) to our [GitHub
Repository][github]. If you would like to *implement* a new feature, please submit an issue with
a proposal for your work first, to be sure that we can use it. Angular 2 is in developer preview
and we are not ready to accept major contributions ahead of the full release.
a proposal for your work first, to be sure that we can use it.
Please consider what kind of change it is:
* For a **Major Feature**, first open an issue and outline your proposal so that it can be

View File

@ -22,9 +22,11 @@ var CIconfiguration = {
'Safari7': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'Safari8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'Safari9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'Safari10': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'iOS7': { unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}},
'iOS8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'iOS9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'iOS10': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'WindowsPhone': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
};
@ -83,6 +85,12 @@ var customLaunchers = {
platform: 'OS X 10.11',
version: '9.0'
},
'SL_SAFARI10': {
base: 'SauceLabs',
browserName: 'safari',
platform: 'OS X 10.12',
version: '10.0'
},
'SL_IOS7': {
base: 'SauceLabs',
browserName: 'iphone',
@ -101,6 +109,12 @@ var customLaunchers = {
platform: 'OS X 10.10',
version: '9.3'
},
'SL_IOS10': {
base: 'SauceLabs',
browserName: 'iphone',
platform: 'OS X 10.10',
version: '10.0'
},
'SL_IE9': {
base: 'SauceLabs',
browserName: 'internet explorer',
@ -186,6 +200,12 @@ var customLaunchers = {
os: 'OS X',
os_version: 'El Capitan'
},
'BS_SAFARI10': {
base: 'BrowserStack',
browser: 'safari',
os: 'OS X',
os_version: 'Sierra'
},
'BS_IOS7': {
base: 'BrowserStack',
device: 'iPhone 5S',
@ -204,6 +224,12 @@ var customLaunchers = {
os: 'ios',
os_version: '9.1'
},
'BS_IOS10': {
base: 'BrowserStack',
device: 'iPhone SE',
os: 'ios',
os_version: '10.0'
},
'BS_IE9': {
base: 'BrowserStack',
browser: 'ie',
@ -271,12 +297,12 @@ var customLaunchers = {
var sauceAliases = {
'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'SauceLabs';}),
'DESKTOP': ['SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'],
'MOBILE': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5', 'SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
'DESKTOP': ['SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9', 'SL_SAFARI10'],
'MOBILE': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5', 'SL_IOS7', 'SL_IOS8', 'SL_IOS9', 'SL_IOS10'],
'ANDROID': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5'],
'IE': ['SL_IE9', 'SL_IE10', 'SL_IE11'],
'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'],
'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9', 'SL_IOS10'],
'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9', 'SL_SAFARI10'],
'BETA': ['SL_CHROMEBETA', 'SL_FIREFOXBETA'],
'DEV': ['SL_CHROMEDEV', 'SL_FIREFOXDEV'],
'CI_REQUIRED': buildConfiguration('unitTest', 'SL', true),
@ -285,12 +311,12 @@ var sauceAliases = {
var browserstackAliases = {
'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'BrowserStack';}),
'DESKTOP': ['BS_CHROME', 'BS_FIREFOX', 'BS_IE9', 'BS_IE10', 'BS_IE11', 'BS_EDGE', 'BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9'],
'MOBILE': ['BS_ANDROID4.3', 'BS_ANDROID4.4', 'BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_WINDOWSPHONE'],
'DESKTOP': ['BS_CHROME', 'BS_FIREFOX', 'BS_IE9', 'BS_IE10', 'BS_IE11', 'BS_EDGE', 'BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9', 'BS_SAFARI10'],
'MOBILE': ['BS_ANDROID4.3', 'BS_ANDROID4.4', 'BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_IOS10', 'BS_WINDOWSPHONE'],
'ANDROID': ['BS_ANDROID4.3', 'BS_ANDROID4.4'],
'IE': ['BS_IE9', 'BS_IE10', 'BS_IE11'],
'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9'],
'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9'],
'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_IOS10'],
'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9', 'BS_SAFARI10'],
'CI_REQUIRED': buildConfiguration('unitTest', 'BS', true),
'CI_OPTIONAL': buildConfiguration('unitTest', 'BS', false)
};

View File

@ -6,18 +6,15 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Map} from './facade/collection';
import {Date, DateWrapper} from './facade/lang';
export class MeasureValues {
constructor(
public runIndex: number, public timeStamp: Date, public values: {[key: string]: any}) {}
toJson() {
return {
'timeStamp': DateWrapper.toJson(this.timeStamp),
'timeStamp': this.timeStamp.toJSON(),
'runIndex': this.runIndex,
'values': this.values
'values': this.values,
};
}
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Inject, Injectable, OpaqueToken, Provider} from '@angular/core';
import {Inject, Injectable} from '@angular/core';
import {Options} from '../common_options';
import {StringMapWrapper} from '../facade/collection';
@ -48,9 +48,9 @@ export class UserMetric extends Metric {
if (values.every(isNumber)) {
Promise.all(names.map(name => adapter.executeScript(`delete window.${name}`)))
.then((_: any[]) => {
let map = StringMapWrapper.create();
let map: {[k: string]: any} = {};
for (let i = 0, n = names.length; i < n; i++) {
StringMapWrapper.set(map, names[i], values[i]);
map[names[i]] = values[i];
}
resolve(map);
}, reject);

View File

@ -33,7 +33,7 @@ export function main() {
new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true});
}
if (isBlank(microMetrics)) {
microMetrics = StringMapWrapper.create();
microMetrics = {};
}
var providers: Provider[] = [
Options.DEFAULT_PROVIDERS, PerflogMetric.PROVIDERS,

View File

@ -24,7 +24,7 @@ export function main() {
new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true});
}
if (isBlank(userMetrics)) {
userMetrics = StringMapWrapper.create();
userMetrics = {};
}
wdAdapter = new MockDriverAdapter();
var providers: Provider[] = [

View File

@ -61,8 +61,7 @@ export class NgPlural {
addCase(value: string, switchView: SwitchView): void { this._caseViews[value] = switchView; }
/** @internal */
_updateView(): void {
private _updateView(): void {
this._clearViews();
const cases = Object.keys(this._caseViews);
@ -70,13 +69,11 @@ export class NgPlural {
this._activateView(this._caseViews[key]);
}
/** @internal */
_clearViews() {
private _clearViews() {
if (this._activeView) this._activeView.destroy();
}
/** @internal */
_activateView(view: SwitchView) {
private _activateView(view: SwitchView) {
if (view) {
this._activeView = view;
this._activeView.create();

View File

@ -32,10 +32,8 @@ import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDif
*/
@Directive({selector: '[ngStyle]'})
export class NgStyle implements DoCheck {
/** @internal */
_ngStyle: {[key: string]: string};
/** @internal */
_differ: KeyValueDiffer;
private _ngStyle: {[key: string]: string};
private _differ: KeyValueDiffer;
constructor(
private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer) {}

View File

@ -111,8 +111,7 @@ export class NgSwitch {
}
}
/** @internal */
_emptyAllActiveViews(): void {
private _emptyAllActiveViews(): void {
const activeContainers = this._activeViews;
for (var i = 0; i < activeContainers.length; i++) {
activeContainers[i].destroy();
@ -120,9 +119,7 @@ export class NgSwitch {
this._activeViews = [];
}
/** @internal */
_activateViews(views: SwitchView[]): void {
// TODO(vicb): assert(this._activeViews.length === 0);
private _activateViews(views: SwitchView[]): void {
if (views) {
for (var i = 0; i < views.length; i++) {
views[i].create();
@ -141,8 +138,7 @@ export class NgSwitch {
views.push(view);
}
/** @internal */
_deregisterView(value: any, view: SwitchView): void {
private _deregisterView(value: any, view: SwitchView): void {
// `_CASE_DEFAULT` is used a marker for non-registered cases
if (value === _CASE_DEFAULT) return;
const views = this._valueViews.get(value);
@ -181,10 +177,8 @@ export class NgSwitch {
@Directive({selector: '[ngSwitchCase]'})
export class NgSwitchCase {
// `_CASE_DEFAULT` is used as a marker for a not yet initialized value
/** @internal */
_value: any = _CASE_DEFAULT;
/** @internal */
_view: SwitchView;
private _value: any = _CASE_DEFAULT;
private _view: SwitchView;
private _switch: NgSwitch;
constructor(

View File

@ -67,7 +67,7 @@ export enum Plural {
Two,
Few,
Many,
Other
Other,
}
/**

View File

@ -49,16 +49,20 @@ export class Location {
_subject: EventEmitter<any> = new EventEmitter();
/** @internal */
_baseHref: string;
/** @internal */
_platformStrategy: LocationStrategy;
constructor(platformStrategy: LocationStrategy) {
this._platformStrategy = platformStrategy;
var browserBaseHref = this._platformStrategy.getBaseHref();
const browserBaseHref = this._platformStrategy.getBaseHref();
this._baseHref = Location.stripTrailingSlash(_stripIndexHtml(browserBaseHref));
this._platformStrategy.onPopState(
(ev) => { this._subject.emit({'url': this.path(true), 'pop': true, 'type': ev.type}); });
this._platformStrategy.onPopState((ev) => {
this._subject.emit({
'url': this.path(true),
'pop': true,
'type': ev.type,
});
});
}
/**

View File

@ -18,9 +18,7 @@ import {EventEmitter, Injectable} from '@angular/core';
@Injectable()
export class SpyLocation implements Location {
urlChanges: string[] = [];
/** @internal */
private _history: LocationState[] = [new LocationState('', '')];
/** @internal */
private _historyIndex: number = 0;
/** @internal */
_subject: EventEmitter<any> = new EventEmitter();

View File

@ -115,27 +115,27 @@ export class DirectiveNormalizer {
const templateStyles = this.normalizeStylesheet(new CompileStylesheetMetadata(
{styles: visitor.styles, styleUrls: visitor.styleUrls, moduleUrl: templateAbsUrl}));
const allStyles = templateMetadataStyles.styles.concat(templateStyles.styles);
const allStyleUrls = templateMetadataStyles.styleUrls.concat(templateStyles.styleUrls);
let encapsulation = templateMeta.encapsulation;
if (isBlank(encapsulation)) {
encapsulation = this._config.defaultEncapsulation;
}
if (encapsulation === ViewEncapsulation.Emulated && allStyles.length === 0 &&
allStyleUrls.length === 0) {
const styles = templateMetadataStyles.styles.concat(templateStyles.styles);
const styleUrls = templateMetadataStyles.styleUrls.concat(templateStyles.styleUrls);
if (encapsulation === ViewEncapsulation.Emulated && styles.length === 0 &&
styleUrls.length === 0) {
encapsulation = ViewEncapsulation.None;
}
return new CompileTemplateMetadata({
encapsulation,
template: template,
templateUrl: templateAbsUrl,
styles: allStyles,
styleUrls: allStyleUrls,
template,
templateUrl: templateAbsUrl, styles, styleUrls,
externalStylesheets: templateMeta.externalStylesheets,
ngContentSelectors: visitor.ngContentSelectors,
animations: templateMeta.animations,
interpolation: templateMeta.interpolation
interpolation: templateMeta.interpolation,
});
}
@ -251,7 +251,6 @@ function _cloneDirectiveWithTemplate(
viewProviders: directive.viewProviders,
queries: directive.queries,
viewQueries: directive.viewQueries,
entryComponents: directive.entryComponents,
template: template
entryComponents: directive.entryComponents, template,
});
}

View File

@ -376,7 +376,7 @@ export class AstTransformer implements AstVisitor {
}
visitAll(asts: any[]): any[] {
var res = ListWrapper.createFixedSize(asts.length);
var res = new Array(asts.length);
for (var i = 0; i < asts.length; ++i) {
res[i] = asts[i].visit(this);
}

View File

@ -854,7 +854,7 @@ class TemplateParseVisitor implements html.Visitor {
boundPropertyName = this._schemaRegistry.getMappedPropName(partValue);
securityContext = this._schemaRegistry.securityContext(elementName, boundPropertyName);
bindingType = PropertyBindingType.Property;
this._assertNoEventBinding(boundPropertyName, sourceSpan);
this._assertNoEventBinding(boundPropertyName, sourceSpan, false);
if (!this._schemaRegistry.hasProperty(elementName, boundPropertyName, this._schemas)) {
let errorMsg =
`Can't bind to '${boundPropertyName}' since it isn't a known property of '${elementName}'.`;
@ -869,7 +869,7 @@ class TemplateParseVisitor implements html.Visitor {
} else {
if (parts[0] == ATTRIBUTE_PREFIX) {
boundPropertyName = parts[1];
this._assertNoEventBinding(boundPropertyName, sourceSpan);
this._assertNoEventBinding(boundPropertyName, sourceSpan, true);
// NB: For security purposes, use the mapped property name, not the attribute name.
const mapPropName = this._schemaRegistry.getMappedPropName(boundPropertyName);
securityContext = this._schemaRegistry.securityContext(elementName, mapPropName);
@ -902,12 +902,23 @@ class TemplateParseVisitor implements html.Visitor {
boundPropertyName, bindingType, securityContext, ast, unit, sourceSpan);
}
private _assertNoEventBinding(propName: string, sourceSpan: ParseSourceSpan): void {
/**
* @param propName the name of the property / attribute
* @param sourceSpan
* @param isAttr true when binding to an attribute
* @private
*/
private _assertNoEventBinding(propName: string, sourceSpan: ParseSourceSpan, isAttr: boolean):
void {
if (propName.toLowerCase().startsWith('on')) {
this._reportError(
`Binding to event attribute '${propName}' is disallowed ` +
`for security reasons, please use (${propName.slice(2)})=...`,
sourceSpan, ParseErrorLevel.FATAL);
let msg = `Binding to event attribute '${propName}' is disallowed for security reasons, ` +
`please use (${propName.slice(2)})=...`;
if (!isAttr) {
msg +=
`\nIf '${propName}' is a directive input, make sure the directive is imported by the` +
` current module.`;
}
this._reportError(msg, sourceSpan, ParseErrorLevel.FATAL);
}
}

View File

@ -117,7 +117,7 @@ export class CompileElement extends CompileNode {
setComponentView(compViewExpr: o.Expression) {
this._compViewExpr = compViewExpr;
this.contentNodesByNgContentIndex =
ListWrapper.createFixedSize(this.component.template.ngContentSelectors.length);
new Array(this.component.template.ngContentSelectors.length);
for (var i = 0; i < this.contentNodesByNgContentIndex.length; i++) {
this.contentNodesByNgContentIndex[i] = [];
}

View File

@ -340,7 +340,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
// Notice that the first guard condition is the left hand of the left most safe access node
// which comes in as leftMostSafe to this routine.
let guardedExpression = this.visit(leftMostSafe.receiver, mode);
let guardedExpression = this.visit(leftMostSafe.receiver, _Mode.Expression);
let temporary: o.ReadVarExpr;
if (this.needsTemporary(leftMostSafe.receiver)) {
// If the expression has method calls or pipes then we need to save the result into a
@ -369,7 +369,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
}
// Recursively convert the node now without the guarded member access.
const access = this.visit(ast, mode);
const access = this.visit(ast, _Mode.Expression);
// Remove the mapping. This is not strictly required as the converter only traverses each node
// once but is safer if the conversion is changed to traverse the nodes more than once.
@ -381,7 +381,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
}
// Produce the conditional
return condition.conditional(o.literal(null), access);
return convertToStatementIfNeeded(mode, condition.conditional(o.literal(null), access));
}
// Given a expression of the form a?.b.c?.d.e the the left most safe node is

View File

@ -8,8 +8,7 @@
import {DirectiveResolver} from '@angular/compiler';
import {AnimationEntryMetadata, Compiler, Component, Directive, Injectable, Injector, Provider, Type, resolveForwardRef} from '@angular/core';
import {Map} from './facade/collection';
import {isArray, isPresent} from './facade/lang';
import {isPresent} from './facade/lang';
import {ViewMetadata} from './private_import_core';
@ -156,7 +155,7 @@ function flattenArray(tree: any[], out: Array<Type<any>|any[]>): void {
if (!isPresent(tree)) return;
for (var i = 0; i < tree.length; i++) {
var item = resolveForwardRef(tree[i]);
if (isArray(item)) {
if (Array.isArray(item)) {
flattenArray(item, out);
} else {
out.push(item);

View File

@ -9,18 +9,12 @@
import {NgModuleResolver} from '@angular/compiler';
import {Compiler, Injectable, Injector, NgModule, Type} from '@angular/core';
import {Map} from './facade/collection';
@Injectable()
export class MockNgModuleResolver extends NgModuleResolver {
private _ngModules = new Map<Type<any>, NgModule>();
constructor(private _injector: Injector) { super(); }
private get _compiler(): Compiler { return this._injector.get(Compiler); }
private _clearCacheFor(component: Type<any>) { this._compiler.clearCacheFor(component); }
/**
* Overrides the {@link NgModule} for a module.
*/
@ -36,10 +30,10 @@ export class MockNgModuleResolver extends NgModuleResolver {
* `NgModuleResolver`, see `setNgModule`.
*/
resolve(type: Type<any>, throwIfNotFound = true): NgModule {
var metadata = this._ngModules.get(type);
if (!metadata) {
metadata = super.resolve(type, throwIfNotFound);
}
return metadata;
return this._ngModules.get(type) || super.resolve(type, throwIfNotFound);
}
private get _compiler(): Compiler { return this._injector.get(Compiler); }
private _clearCacheFor(component: Type<any>) { this._compiler.clearCacheFor(component); }
}

View File

@ -9,8 +9,6 @@
import {PipeResolver} from '@angular/compiler';
import {Compiler, Injectable, Injector, Pipe, Type} from '@angular/core';
import {Map} from './facade/collection';
@Injectable()
export class MockPipeResolver extends PipeResolver {
private _pipes = new Map<Type<any>, Pipe>();

View File

@ -7,11 +7,9 @@
*/
import {ResourceLoader} from '@angular/compiler';
import {ListWrapper, Map} from './facade/collection';
import {ListWrapper} from './facade/collection';
import {isBlank, normalizeBlank} from './facade/lang';
/**
* A mock implementation of {@link ResourceLoader} that allows outgoing requests to be mocked
* and responded to within a single test, without going to the network.

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ListWrapper, Map, StringMapWrapper} from '../facade/collection';
import {ListWrapper, StringMapWrapper} from '../facade/collection';
import {isPresent} from '../facade/lang';
import {AnimationPlayer} from './animation_player';
@ -47,12 +47,12 @@ export class ViewAnimationMap {
getAllPlayers(): AnimationPlayer[] { return this._allPlayers; }
remove(element: any, animationName: string): void {
var playersByAnimation = this._map.get(element);
if (isPresent(playersByAnimation)) {
var player = playersByAnimation[animationName];
const playersByAnimation = this._map.get(element);
if (playersByAnimation) {
const player = playersByAnimation[animationName];
delete playersByAnimation[animationName];
var index = this._allPlayers.indexOf(player);
ListWrapper.removeAt(this._allPlayers, index);
const index = this._allPlayers.indexOf(player);
this._allPlayers.splice(index, 1);
if (StringMapWrapper.isEmpty(playersByAnimation)) {
this._map.delete(element);

View File

@ -14,7 +14,6 @@ import {LOCALE_ID} from './i18n/tokens';
import {Compiler} from './linker/compiler';
import {ViewUtils} from './linker/view_utils';
import {NgModule} from './metadata';
import {Type} from './type';
export function _iterableDiffersFactory() {
return defaultIterableDiffers;

View File

@ -9,7 +9,7 @@
import {ErrorHandler} from '../src/error_handler';
import {ListWrapper} from '../src/facade/collection';
import {unimplemented} from '../src/facade/errors';
import {isBlank, isPresent, stringify} from '../src/facade/lang';
import {stringify} from '../src/facade/lang';
import {isPromise} from '../src/util/lang';
import {ApplicationInitStatus} from './application_init';
@ -26,9 +26,9 @@ import {Testability, TestabilityRegistry} from './testability/testability';
import {Type} from './type';
import {NgZone} from './zone/ng_zone';
var _devMode: boolean = true;
var _runModeLocked: boolean = false;
var _platform: PlatformRef;
let _devMode: boolean = true;
let _runModeLocked: boolean = false;
let _platform: PlatformRef;
/**
* Disable Angular's development mode, which turns off assertions and other
@ -67,13 +67,13 @@ export function isDevMode(): boolean {
* @experimental APIs related to application bootstrap are currently under review.
*/
export function createPlatform(injector: Injector): PlatformRef {
if (isPresent(_platform) && !_platform.destroyed) {
if (_platform && !_platform.destroyed) {
throw new Error(
'There can be only one platform. Destroy the previous one to create a new one.');
}
_platform = injector.get(PlatformRef);
const inits: Function[] = <Function[]>injector.get(PLATFORM_INITIALIZER, null);
if (isPresent(inits)) inits.forEach(init => init());
if (inits) inits.forEach(init => init());
return _platform;
}
@ -107,14 +107,17 @@ export function createPlatformFactory(
* @experimental APIs related to application bootstrap are currently under review.
*/
export function assertPlatform(requiredToken: any): PlatformRef {
var platform = getPlatform();
if (isBlank(platform)) {
const platform = getPlatform();
if (!platform) {
throw new Error('No platform exists!');
}
if (isPresent(platform) && isBlank(platform.injector.get(requiredToken, null))) {
if (!platform.injector.get(requiredToken, null)) {
throw new Error(
'A platform with a different configuration has been created. Please destroy it first.');
}
return platform;
}
@ -124,7 +127,7 @@ export function assertPlatform(requiredToken: any): PlatformRef {
* @experimental APIs related to application bootstrap are currently under review.
*/
export function destroyPlatform(): void {
if (isPresent(_platform) && !_platform.destroyed) {
if (_platform && !_platform.destroyed) {
_platform.destroy();
}
}
@ -135,7 +138,7 @@ export function destroyPlatform(): void {
* @experimental APIs related to application bootstrap are currently under review.
*/
export function getPlatform(): PlatformRef {
return isPresent(_platform) && !_platform.destroyed ? _platform : null;
return _platform && !_platform.destroyed ? _platform : null;
}
/**
@ -224,9 +227,9 @@ function _callAndReportToErrorHandler(errorHandler: ErrorHandler, callback: () =
// rethrow as the exception handler might not do it
throw e;
});
} else {
return result;
}
return result;
} catch (e) {
errorHandler.handleError(e);
// rethrow as the exception handler might not do it
@ -238,7 +241,6 @@ function _callAndReportToErrorHandler(errorHandler: ErrorHandler, callback: () =
export class PlatformRef_ extends PlatformRef {
private _modules: NgModuleRef<any>[] = [];
private _destroyListeners: Function[] = [];
private _destroyed: boolean = false;
constructor(private _injector: Injector) { super(); }
@ -253,8 +255,8 @@ export class PlatformRef_ extends PlatformRef {
if (this._destroyed) {
throw new Error('The platform has already been destroyed!');
}
ListWrapper.clone(this._modules).forEach((app) => app.destroy());
this._destroyListeners.forEach((dispose) => dispose());
this._modules.slice().forEach(module => module.destroy());
this._destroyListeners.forEach(listener => listener());
this._destroyed = true;
}
@ -301,7 +303,7 @@ export class PlatformRef_ extends PlatformRef {
componentFactoryCallback?: any): Promise<NgModuleRef<M>> {
const compilerFactory: CompilerFactory = this.injector.get(CompilerFactory);
const compiler = compilerFactory.createCompiler(
compilerOptions instanceof Array ? compilerOptions : [compilerOptions]);
Array.isArray(compilerOptions) ? compilerOptions : [compilerOptions]);
// ugly internal api hack: generate host component factories for all declared components and
// pass the factories into the callback - this is used by UpdateAdapter to get hold of all
@ -424,10 +426,10 @@ export class ApplicationRef_ extends ApplicationRef {
componentFactory = this._componentFactoryResolver.resolveComponentFactory(componentOrFactory);
}
this._rootComponentTypes.push(componentFactory.componentType);
var compRef = componentFactory.create(this._injector, [], componentFactory.selector);
const compRef = componentFactory.create(this._injector, [], componentFactory.selector);
compRef.onDestroy(() => { this._unloadComponent(compRef); });
var testability = compRef.injector.get(Testability, null);
if (isPresent(testability)) {
const testability = compRef.injector.get(Testability, null);
if (testability) {
compRef.injector.get(TestabilityRegistry)
.registerApplication(compRef.location.nativeElement, testability);
}
@ -454,7 +456,7 @@ export class ApplicationRef_ extends ApplicationRef {
/** @internal */
_unloadComponent(componentRef: ComponentRef<any>): void {
if (!ListWrapper.contains(this._rootComponents, componentRef)) {
if (this._rootComponents.indexOf(componentRef) == -1) {
return;
}
this.unregisterChangeDetector(componentRef.changeDetectorRef);
@ -466,7 +468,7 @@ export class ApplicationRef_ extends ApplicationRef {
throw new Error('ApplicationRef.tick is called recursively');
}
var s = ApplicationRef_._tickScope();
const scope = ApplicationRef_._tickScope();
try {
this._runningTick = true;
this._changeDetectorRefs.forEach((detector) => detector.detectChanges());
@ -475,13 +477,13 @@ export class ApplicationRef_ extends ApplicationRef {
}
} finally {
this._runningTick = false;
wtfLeave(s);
wtfLeave(scope);
}
}
ngOnDestroy() {
// TODO(alxhub): Dispose of the NgZone.
ListWrapper.clone(this._rootComponents).forEach((ref) => ref.destroy());
this._rootComponents.slice().forEach((component) => component.destroy());
}
get componentTypes(): Type<any>[] { return this._rootComponentTypes; }

View File

@ -34,7 +34,7 @@ export function _appIdRandomProviderFactory() {
export const APP_ID_RANDOM_PROVIDER = {
provide: APP_ID,
useFactory: _appIdRandomProviderFactory,
deps: <any[]>[]
deps: <any[]>[],
};
function _randomChar(): string {

View File

@ -121,7 +121,7 @@ export class ReflectiveProtoInjectorDynamicStrategy implements ReflectiveProtoIn
constructor(protoInj: ReflectiveProtoInjector, public providers: ResolvedReflectiveProvider[]) {
var len = providers.length;
this.keyIds = ListWrapper.createFixedSize(len);
this.keyIds = new Array(len);
for (var i = 0; i < len; i++) {
this.keyIds[i] = providers[i].key.id;
@ -286,7 +286,7 @@ export class ReflectiveInjectorDynamicStrategy implements ReflectiveInjectorStra
constructor(
public protoStrategy: ReflectiveProtoInjectorDynamicStrategy,
public injector: ReflectiveInjector_) {
this.objs = ListWrapper.createFixedSize(protoStrategy.providers.length);
this.objs = new Array(protoStrategy.providers.length);
ListWrapper.fill(this.objs, UNDEFINED);
}
@ -648,7 +648,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
private _instantiateProvider(provider: ResolvedReflectiveProvider): any {
if (provider.multiProvider) {
var res = ListWrapper.createFixedSize(provider.resolvedFactories.length);
var res = new Array(provider.resolvedFactories.length);
for (var i = 0; i < provider.resolvedFactories.length; ++i) {
res[i] = this._instantiate(provider, provider.resolvedFactories[i]);
}

View File

@ -10,7 +10,6 @@ import {APP_ID} from '../application_tokens';
import {devModeEqual} from '../change_detection/change_detection';
import {UNINITIALIZED} from '../change_detection/change_detection_util';
import {Inject, Injectable} from '../di';
import {ListWrapper} from '../facade/collection';
import {isBlank, isPresent, looseIdentical} from '../facade/lang';
import {ViewEncapsulation} from '../metadata/view';
import {RenderComponentType, Renderer, RootRenderer} from '../render/api';
@ -77,7 +76,7 @@ export function ensureSlotCount(projectableNodes: any[][], expectedSlotCount: nu
res = EMPTY_ARR;
} else if (projectableNodes.length < expectedSlotCount) {
var givenSlotCount = projectableNodes.length;
res = ListWrapper.createFixedSize(expectedSlotCount);
res = new Array(expectedSlotCount);
for (var i = 0; i < expectedSlotCount; i++) {
res[i] = (i < givenSlotCount) ? projectableNodes[i] : EMPTY_ARR;
}

View File

@ -15,8 +15,6 @@ import {Attribute, ContentChild, ContentChildren, Query, ViewChild, ViewChildren
import {Component, Directive, HostBinding, HostListener, Input, Output, Pipe} from './metadata/directives';
import {ModuleWithProviders, NgModule, SchemaMetadata} from './metadata/ng_module';
import {ViewEncapsulation} from './metadata/view';
import {Type} from './type';
import {TypeDecorator, makeParamDecorator, makePropDecorator} from './util/decorators';
export {ANALYZE_FOR_ENTRY_COMPONENTS, Attribute, ContentChild, ContentChildDecorator, ContentChildren, ContentChildrenDecorator, Query, ViewChild, ViewChildDecorator, ViewChildren, ViewChildrenDecorator} from './metadata/di';
export {Component, ComponentDecorator, Directive, DirectiveDecorator, HostBinding, HostListener, Input, Output, Pipe} from './metadata/directives';

View File

@ -448,15 +448,15 @@ export interface ComponentDecorator {
* * **changeDetection** - change detection strategy used by this component
* * **encapsulation** - style encapsulation strategy used by this component
* * **entryComponents** - list of components that are dynamically inserted into the view of this
* component
* component
* * **exportAs** - name under which the component instance is exported in a template
* * **host** - map of class property to host element bindings for events, properties and
* attributes
* attributes
* * **inputs** - list of class property names to data-bind as component inputs
* * **interpolation** - custom interpolation markers used in this component's template
* * **moduleId** - ES/CommonJS module id of the file in which this component is defined
* * **outputs** - list of class property names that expose output events that others can
* subscribe to
* subscribe to
* * **providers** - list of providers available to this component and its children
* * **queries** - configure queries that can be injected into the component
* * **selector** - css selector that identifies this component in a template
@ -655,20 +655,33 @@ export interface Component extends Directive {
animations?: AnimationEntryMetadata[];
/**
* Specify how the template and the styles should be encapsulated.
* The default is {@link ViewEncapsulation#Emulated `ViewEncapsulation.Emulated`} if the view
* has styles,
* otherwise {@link ViewEncapsulation#None `ViewEncapsulation.None`}.
* Specifies how the template and the styles should be encapsulated:
* - {@link ViewEncapsulation#Native `ViewEncapsulation.Native`} to use shadow roots - only works
* if natively available on the platform,
* - {@link ViewEncapsulation#Emulated `ViewEncapsulation.Emulated`} to use shimmed CSS that
* emulates the native behavior,
* - {@link ViewEncapsulation#None `ViewEncapsulation.None`} to use global CSS without any
* encapsulation.
*
* When no `encapsulation` is defined for the component, the default value from the
* {@link CompilerConfig} is used. The default is `ViewEncapsulation.Emulated`}. Provide a new
* `CompilerConfig` to override this value.
*
* If the encapsulation is set to `ViewEncapsulation.Emulated` and the component has no `styles`
* nor `styleUrls` the encapsulation will automatically be switched to `ViewEncapsulation.None`.
*/
encapsulation?: ViewEncapsulation;
/**
* Overrides the default encapsulation start and end delimiters (respectively `{{` and `}}`)
*/
interpolation?: [string, string];
/**
* Defines the components that should be compiled as well when
* this component is defined. For each components listed here,
* Angular will create a {@link ComponentFactory ComponentFactory} and store it in the
* {@link ComponentFactoryResolver ComponentFactoryResolver}.
* Angular will create a {@link ComponentFactory} and store it in the
* {@link ComponentFactoryResolver}.
*/
entryComponents?: Array<Type<any>|any[]>;
}

View File

@ -7,8 +7,6 @@
*/
import {AnimationEntryMetadata} from '../animation/metadata';
import {Type} from '../type';
/**
* Defines template and style encapsulation options available for Component's {@link Component}.
@ -46,13 +44,6 @@ export var VIEW_ENCAPSULATION_VALUES =
/**
* Metadata properties available for configuring Views.
*
* Each Angular component requires a single `@Component` and at least one `@View` annotation. The
* `@View` annotation specifies the HTML template to use, and lists the directives that are active
* within the template.
*
* When a component is instantiated, the template is loaded into the component's shadow root, and
* the expressions and statements in the template are evaluated against the component.
*
* For details on the `@Component` annotation, see {@link Component}.
*
* ### Example
@ -61,7 +52,6 @@ export var VIEW_ENCAPSULATION_VALUES =
* @Component({
* selector: 'greet',
* template: 'Hello {{name}}!',
* directives: [GreetUser, Bold]
* })
* class Greet {
* name: string;
@ -73,46 +63,23 @@ export var VIEW_ENCAPSULATION_VALUES =
* ```
*
* @deprecated Use Component instead.
*
* {@link Component}
*/
export class ViewMetadata {
/**
* Specifies a template URL for an Angular component.
*
* NOTE: Only one of `templateUrl` or `template` can be defined per View.
*
* <!-- TODO: what's the url relative to? -->
*/
/** {@link Component.templateUrl} */
templateUrl: string;
/**
* Specifies an inline template for an Angular component.
*
* NOTE: Only one of `templateUrl` or `template` can be defined per View.
*/
/** {@link Component.template} */
template: string;
/**
* Specifies stylesheet URLs for an Angular component.
*
* <!-- TODO: what's the url relative to? -->
*/
/** {@link Component.stylesUrl} */
styleUrls: string[];
/**
* Specifies an inline stylesheet for an Angular component.
*/
/** {@link Component.styles} */
styles: string[];
/**
* Specify how the template and the styles should be encapsulated.
* The default is {@link ViewEncapsulation#Emulated `ViewEncapsulation.Emulated`} if the view
* has styles,
* otherwise {@link ViewEncapsulation#None `ViewEncapsulation.None`}.
*/
/** {@link Component.encapsulation} */
encapsulation: ViewEncapsulation;
/** {@link Component.animation} */
animations: AnimationEntryMetadata[];
/** {@link Component.interpolation} */
interpolation: [string, string];
constructor(

View File

@ -8,7 +8,7 @@
import {PlatformRef, PlatformRef_, createPlatformFactory} from './application_ref';
import {Console} from './console';
import {ClassProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ValueProvider} from './di';
import {Provider} from './di';
import {Reflector, reflector} from './reflection/reflection';
import {ReflectorReader} from './reflection/reflector_reader';
import {TestabilityRegistry} from './testability/testability';
@ -18,9 +18,12 @@ function _reflector(): Reflector {
}
const _CORE_PLATFORM_PROVIDERS: Provider[] = [
PlatformRef_, {provide: PlatformRef, useExisting: PlatformRef_},
PlatformRef_,
{provide: PlatformRef, useExisting: PlatformRef_},
{provide: Reflector, useFactory: _reflector, deps: []},
{provide: ReflectorReader, useExisting: Reflector}, TestabilityRegistry, Console
{provide: ReflectorReader, useExisting: Reflector},
TestabilityRegistry,
Console,
];
/**

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Map, MapWrapper, Set, SetWrapper, StringMapWrapper} from '../facade/collection';
import {MapWrapper, SetWrapper, StringMapWrapper} from '../facade/collection';
import {isPresent} from '../facade/lang';
import {Type} from '../type';
import {PlatformReflectionCapabilities} from './platform_reflection_capabilities';
@ -40,14 +40,9 @@ export class Reflector extends ReflectorReader {
/** @internal */
_methods = new Map<string, MethodFn>();
/** @internal */
_usedKeys: Set<any>;
reflectionCapabilities: PlatformReflectionCapabilities;
_usedKeys: Set<any> = null;
constructor(reflectionCapabilities: PlatformReflectionCapabilities) {
super();
this._usedKeys = null;
this.reflectionCapabilities = reflectionCapabilities;
}
constructor(public reflectionCapabilities: PlatformReflectionCapabilities) { super(); }
updateCapabilities(caps: PlatformReflectionCapabilities) { this.reflectionCapabilities = caps; }

View File

@ -7,7 +7,7 @@
*/
import {Injectable} from '../di';
import {Map, MapWrapper} from '../facade/collection';
import {MapWrapper} from '../facade/collection';
import {scheduleMicroTask} from '../facade/lang';
import {NgZone} from '../zone/ng_zone';
@ -110,6 +110,7 @@ export class Testability implements PublicTestability {
getPendingRequestCount(): number { return this._pendingCount; }
/** @deprecated use findProviders */
findBindings(using: any, provider: string, exactMatch: boolean): any[] {
// TODO(juliemr): implement.
return [];

View File

@ -16,7 +16,7 @@
*
* @stable
*/
export var Type = Function;
export const Type = Function;
export interface Type<T> extends Function { new (...args: any[]): T; }

View File

@ -79,6 +79,24 @@ function declareTests({useJit}: {useJit: boolean}) {
expect(fixture.nativeElement).toHaveText('counting method value');
expect(MyCountingComp.calls).toBe(1);
});
it('should evalute a conditional in a statement binding', () => {
@Component({selector: 'some-comp', template: '<p (click)="nullValue?.click()"></p>'})
class SomeComponent {
nullValue: SomeReferencedClass;
}
class SomeReferencedClass {
click() {}
}
expect(() => {
const fixture = TestBed.configureTestingModule({declarations: [SomeComponent]})
.createComponent(SomeComponent);
fixture.detectChanges(/* checkNoChanges */ false);
}).not.toThrow();
});
});
describe('providers', () => {

View File

@ -6,9 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA} from '@angular/core';
import {Component} from '@angular/core/src/metadata';
import {TestBed, getTestBed} from '@angular/core/testing';
import {Component, Directive, Input, NO_ERRORS_SCHEMA} from '@angular/core';
import {ComponentFixture, TestBed, getTestBed} from '@angular/core/testing';
import {afterEach, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it} from '@angular/core/testing/testing_internal';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {DomSanitizer} from '@angular/platform-browser/src/security/dom_sanitization_service';
@ -24,12 +23,22 @@ class SecuredComponent {
ctxProp: any = 'some value';
}
@Directive({selector: '[onPrefixedProp]'})
class OnPrefixDir {
@Input() onPrefixedProp: any;
@Input() onclick: any;
}
function declareTests({useJit}: {useJit: boolean}) {
describe('security integration tests', function() {
beforeEach(() => {
TestBed.configureCompiler({useJit: useJit});
TestBed.configureTestingModule({declarations: [SecuredComponent]});
TestBed.configureCompiler({useJit: useJit}).configureTestingModule({
declarations: [
SecuredComponent,
OnPrefixDir,
]
});
});
let originalLog: (msg: any) => any;
@ -43,15 +52,10 @@ function declareTests({useJit}: {useJit: boolean}) {
it('should disallow binding to attr.on*', () => {
const template = `<div [attr.onclick]="ctxProp"></div>`;
TestBed.overrideComponent(SecuredComponent, {set: {template}});
try {
TestBed.createComponent(SecuredComponent);
throw 'Should throw';
} catch (e) {
expect(e.message).toContain(
`Template parse errors:\n` +
`Binding to event attribute 'onclick' is disallowed ` +
`for security reasons, please use (click)=... `);
}
expect(() => TestBed.createComponent(SecuredComponent))
.toThrowError(
/Binding to event attribute 'onclick' is disallowed for security reasons, please use \(click\)=.../);
});
it('should disallow binding to on* with NO_ERRORS_SCHEMA', () => {
@ -59,17 +63,31 @@ function declareTests({useJit}: {useJit: boolean}) {
TestBed.overrideComponent(SecuredComponent, {set: {template}}).configureTestingModule({
schemas: [NO_ERRORS_SCHEMA]
});
;
try {
TestBed.createComponent(SecuredComponent);
throw 'Should throw';
} catch (e) {
expect(e.message).toContain(
`Template parse errors:\n` +
`Binding to event attribute 'onclick' is disallowed ` +
`for security reasons, please use (click)=... `);
}
expect(() => TestBed.createComponent(SecuredComponent))
.toThrowError(
/Binding to event attribute 'onclick' is disallowed for security reasons, please use \(click\)=.../);
});
it('should disallow binding to on* unless it is consumed by a directive', () => {
const template = `<div [onPrefixedProp]="ctxProp" [onclick]="ctxProp"></div>`;
TestBed.overrideComponent(SecuredComponent, {set: {template}}).configureTestingModule({
schemas: [NO_ERRORS_SCHEMA]
});
// should not throw for inputs starting with "on"
let cmp: ComponentFixture<SecuredComponent>;
expect(() => cmp = TestBed.createComponent(SecuredComponent)).not.toThrow();
// must bind to the directive not to the property of the div
const value = cmp.componentInstance.ctxProp = {};
cmp.detectChanges();
const div = cmp.debugElement.children[0];
expect(div.injector.get(OnPrefixDir).onclick).toBe(value);
expect(getDOM().getProperty(div.nativeElement, 'onclick')).not.toBe(value);
expect(getDOM().hasAttribute(div.nativeElement, 'onclick')).toEqual(false);
});
});
describe('safe HTML values', function() {
@ -157,12 +175,8 @@ function declareTests({useJit}: {useJit: boolean}) {
const template = `<svg:circle [xlink:href]="ctxProp">Text</svg:circle>`;
TestBed.overrideComponent(SecuredComponent, {set: {template}});
try {
TestBed.createComponent(SecuredComponent);
throw 'Should throw';
} catch (e) {
expect(e.message).toContain(`Can't bind to 'xlink:href'`);
}
expect(() => TestBed.createComponent(SecuredComponent))
.toThrowError(/Can't bind to 'xlink:href'/);
});
it('should escape unsafe HTML values', () => {

View File

@ -47,7 +47,13 @@ export type TestModuleMetadata = {
};
/**
* @experimental
* @whatItDoes Configures and initializes environment for unit testing and provides methods for
* creating components and services in unit tests.
* @description
*
* TestBed is the primary api for writing unit tests for Angular applications and libraries.
*
* @stable
*/
export class TestBed implements Injector {
/**

View File

@ -8,12 +8,9 @@
import {getSymbolIterator, global, isArray, isBlank, isJsObject, isPresent} from './lang';
export var Map = global.Map;
export var Set = global.Set;
// Safari and Internet Explorer do not support the iterable parameter to the
// Map constructor. We work around that by manually adding the items.
var createMapFromPairs: {(pairs: any[]): Map<any, any>} = (function() {
const createMapFromPairs: {(pairs: any[]): Map<any, any>} = (function() {
try {
if (new Map(<any>[[1, 2]]).size === 1) {
return function createMapFromPairs(pairs: any[]): Map<any, any> { return new Map(pairs); };
@ -29,7 +26,7 @@ var createMapFromPairs: {(pairs: any[]): Map<any, any>} = (function() {
return map;
};
})();
var createMapFromMap: {(m: Map<any, any>): Map<any, any>} = (function() {
const createMapFromMap: {(m: Map<any, any>): Map<any, any>} = (function() {
try {
if (new Map(<any>new Map())) {
return function createMapFromMap(m: Map<any, any>): Map<any, any> { return new Map(<any>m); };
@ -42,7 +39,7 @@ var createMapFromMap: {(m: Map<any, any>): Map<any, any>} = (function() {
return map;
};
})();
var _clearValues: {(m: Map<any, any>): void} = (function() {
const _clearValues: {(m: Map<any, any>): void} = (function() {
if ((<any>(new Map()).keys()).next) {
return function _clearValues(m: Map<any, any>) {
var keyIterator = m.keys();
@ -69,7 +66,7 @@ var _arrayFromMap: {(m: Map<any, any>, getValues: boolean): any[]} = (function()
} catch (e) {
}
return function createArrayFromMapWithForeach(m: Map<any, any>, getValues: boolean): any[] {
var res = ListWrapper.createFixedSize(m.size), i = 0;
var res = new Array(m.size), i = 0;
m.forEach((v, k) => {
res[i] = getValues ? v : k;
i++;
@ -79,7 +76,6 @@ var _arrayFromMap: {(m: Map<any, any>, getValues: boolean): any[]} = (function()
})();
export class MapWrapper {
static clone<K, V>(m: Map<K, V>): Map<K, V> { return createMapFromMap(m); }
static createFromStringMap<T>(stringMap: {[key: string]: T}): Map<string, T> {
var result = new Map<string, T>();
for (var prop in stringMap) {
@ -93,7 +89,6 @@ export class MapWrapper {
return r;
}
static createFromPairs(pairs: any[]): Map<any, any> { return createMapFromPairs(pairs); }
static clearValues(m: Map<any, any>) { _clearValues(m); }
static iterable<T>(m: T): T { return m; }
static keys<K>(m: Map<K, any>): K[] { return _arrayFromMap(m, false); }
static values<V>(m: Map<any, V>): V[] { return _arrayFromMap(m, true); }
@ -103,15 +98,6 @@ export class MapWrapper {
* Wraps Javascript Objects
*/
export class StringMapWrapper {
static create(): {[k: /*any*/ string]: any} {
// Note: We are not using Object.create(null) here due to
// performance!
// http://jsperf.com/ng2-object-create-null
return {};
}
static contains(map: {[key: string]: any}, key: string): boolean {
return map.hasOwnProperty(key);
}
static get<V>(map: {[key: string]: V}, key: string): V {
return map.hasOwnProperty(key) ? map[key] : undefined;
}
@ -127,7 +113,6 @@ export class StringMapWrapper {
}
return true;
}
static delete (map: {[key: string]: any}, key: string) { delete map[key]; }
static forEach<K, V>(map: {[key: string]: V}, callback: (v: V, K: string) => void) {
for (let k of Object.keys(map)) {
callback(map[k], k);

View File

@ -14,15 +14,13 @@ export function unimplemented(): any {
* @stable
*/
export class BaseError extends Error {
/**
* @internal
*/
/** @internal **/
_nativeError: Error;
constructor(message: string) {
// Errors don't use current this, instead they create a new instance.
// We have to do forward all of our api to the nativeInstance.
var nativeError = super(message) as any as Error;
const nativeError = super(message) as any as Error;
this._nativeError = nativeError;
}
@ -40,11 +38,6 @@ export class BaseError extends Error {
export class WrappedError extends BaseError {
originalError: any;
/**
* @internal
*/
_nativeError: Error;
constructor(message: string, error: any) {
super(`${message} caused by: ${error instanceof Error ? error.message: error }`);
this.originalError = error;

View File

@ -200,7 +200,9 @@ export class NgModel extends NgControl implements OnChanges,
private _updateDisabled(changes: SimpleChanges) {
const disabledValue = changes['isDisabled'].currentValue;
const isDisabled = disabledValue != null && disabledValue != false;
const isDisabled =
disabledValue === '' || (disabledValue && disabledValue !== 'false');
resolvedPromise.then(() => {
if (isDisabled && !this.control.disabled) {

View File

@ -9,7 +9,6 @@
import {Directive, Inject, Input, OnChanges, Optional, Output, Self, SimpleChanges, forwardRef} from '@angular/core';
import {EventEmitter} from '../../facade/async';
import {StringMapWrapper} from '../../facade/collection';
import {FormControl} from '../../model';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../../validators';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '../control_value_accessor';
@ -117,6 +116,6 @@ export class FormControlDirective extends NgControl implements OnChanges {
}
private _isControlChanged(changes: {[key: string]: any}): boolean {
return StringMapWrapper.contains(changes, 'form');
return changes.hasOwnProperty('form');
}
}

View File

@ -9,7 +9,7 @@
import {Directive, Inject, Input, OnChanges, Optional, Output, Self, SimpleChanges, forwardRef} from '@angular/core';
import {EventEmitter} from '../../facade/async';
import {ListWrapper, StringMapWrapper} from '../../facade/collection';
import {ListWrapper} from '../../facade/collection';
import {isBlank} from '../../facade/lang';
import {FormArray, FormControl, FormGroup} from '../../model';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from '../../validators';
@ -80,7 +80,7 @@ export class FormGroupDirective extends ControlContainer implements Form,
ngOnChanges(changes: SimpleChanges): void {
this._checkFormPresent();
if (StringMapWrapper.contains(changes, 'form')) {
if (changes.hasOwnProperty('form')) {
this._updateValidators();
this._updateDomValue();
this._updateRegistrations();

View File

@ -7,7 +7,7 @@
*/
import {ListWrapper, StringMapWrapper} from '../facade/collection';
import {ListWrapper} from '../facade/collection';
import {hasConstructor, isBlank, isPresent, looseIdentical} from '../facade/lang';
import {FormArray, FormControl, FormGroup} from '../model';
import {Validators} from '../validators';
@ -120,8 +120,8 @@ export function composeAsyncValidators(validators: /* Array<Validator|Function>
}
export function isPropertyUpdated(changes: {[key: string]: any}, viewModel: any): boolean {
if (!StringMapWrapper.contains(changes, 'model')) return false;
var change = changes['model'];
if (!changes.hasOwnProperty('model')) return false;
const change = changes['model'];
if (change.isFirstChange()) return true;
return !looseIdentical(viewModel, change.currentValue);

View File

@ -16,7 +16,6 @@ import {isBlank, isPresent, isStringMap, normalizeBool} from './facade/lang';
import {isPromise} from './private_import_core';
/**
* Indicates that a FormControl is valid, i.e. that no errors exist in the input value.
*/
@ -335,7 +334,7 @@ export abstract class AbstractControl {
}
this._updateAncestors(onlySelf);
this._onDisabledChange(true);
this._onDisabledChange.forEach((changeFn) => changeFn(true));
}
/**
@ -351,7 +350,7 @@ export abstract class AbstractControl {
this.updateValueAndValidity({onlySelf: true, emitEvent: emitEvent});
this._updateAncestors(onlySelf);
this._onDisabledChange(false);
this._onDisabledChange.forEach((changeFn) => changeFn(false));
}
private _updateAncestors(onlySelf: boolean) {
@ -596,7 +595,7 @@ export abstract class AbstractControl {
}
/** @internal */
_onDisabledChange(isDisabled: boolean): void {}
_onDisabledChange: Function[] = [];
/** @internal */
_isBoxedValue(formState: any): boolean {
@ -771,14 +770,16 @@ export class FormControl extends AbstractControl {
*/
_clearChangeFns(): void {
this._onChange = [];
this._onDisabledChange = null;
this._onDisabledChange = [];
this._onCollectionChange = () => {};
}
/**
* Register a listener for disabled events.
*/
registerOnDisabledChange(fn: (isDisabled: boolean) => void): void { this._onDisabledChange = fn; }
registerOnDisabledChange(fn: (isDisabled: boolean) => void): void {
this._onDisabledChange.push(fn);
}
/**
* @internal
@ -886,7 +887,7 @@ export class FormGroup extends AbstractControl {
*/
removeControl(name: string): void {
if (this.controls[name]) this.controls[name]._registerOnCollectionChange(() => {});
StringMapWrapper.delete(this.controls, name);
delete (this.controls[name]);
this.updateValueAndValidity();
this._onCollectionChange();
}
@ -896,7 +897,7 @@ export class FormGroup extends AbstractControl {
*/
setControl(name: string, control: AbstractControl): void {
if (this.controls[name]) this.controls[name]._registerOnCollectionChange(() => {});
StringMapWrapper.delete(this.controls, name);
delete (this.controls[name]);
if (control) this.registerControl(name, control);
this.updateValueAndValidity();
this._onCollectionChange();

View File

@ -485,7 +485,7 @@ export function main() {
});
describe('NgModel', () => {
var ngModel: any /** TODO #9100 */;
let ngModel: NgModel;
beforeEach(() => {
ngModel = new NgModel(
@ -539,6 +539,45 @@ export function main() {
expect(ngModel.control.errors).toEqual({'async': true});
}));
it('should mark as disabled properly', fakeAsync(() => {
ngModel.ngOnChanges({isDisabled: new SimpleChange('', undefined)});
tick();
expect(ngModel.control.disabled).toEqual(false);
ngModel.ngOnChanges({isDisabled: new SimpleChange('', null)});
tick();
expect(ngModel.control.disabled).toEqual(false);
ngModel.ngOnChanges({isDisabled: new SimpleChange('', false)});
tick();
expect(ngModel.control.disabled).toEqual(false);
ngModel.ngOnChanges({isDisabled: new SimpleChange('', 'false')});
tick();
expect(ngModel.control.disabled).toEqual(false);
ngModel.ngOnChanges({isDisabled: new SimpleChange('', 0)});
tick();
expect(ngModel.control.disabled).toEqual(false);
ngModel.ngOnChanges({isDisabled: new SimpleChange(null, '')});
tick();
expect(ngModel.control.disabled).toEqual(true);
ngModel.ngOnChanges({isDisabled: new SimpleChange(null, 'true')});
tick();
expect(ngModel.control.disabled).toEqual(true);
ngModel.ngOnChanges({isDisabled: new SimpleChange(null, true)});
tick();
expect(ngModel.control.disabled).toEqual(true);
ngModel.ngOnChanges({isDisabled: new SimpleChange(null, 'anything else')});
tick();
expect(ngModel.control.disabled).toEqual(true);
}));
});
describe('FormControlName', () => {

View File

@ -1022,6 +1022,54 @@ export function main() {
});
it('should disable all radio buttons when disable() is called', () => {
const fixture = TestBed.createComponent(FormControlRadioButtons);
const form =
new FormGroup({food: new FormControl('fish'), drink: new FormControl('cola')});
fixture.componentInstance.form = form;
fixture.detectChanges();
const inputs = fixture.debugElement.queryAll(By.css('input'));
expect(inputs[0].nativeElement.disabled).toEqual(false);
expect(inputs[1].nativeElement.disabled).toEqual(false);
expect(inputs[2].nativeElement.disabled).toEqual(false);
expect(inputs[3].nativeElement.disabled).toEqual(false);
form.get('food').disable();
expect(inputs[0].nativeElement.disabled).toEqual(true);
expect(inputs[1].nativeElement.disabled).toEqual(true);
expect(inputs[2].nativeElement.disabled).toEqual(false);
expect(inputs[3].nativeElement.disabled).toEqual(false);
form.disable();
expect(inputs[0].nativeElement.disabled).toEqual(true);
expect(inputs[1].nativeElement.disabled).toEqual(true);
expect(inputs[2].nativeElement.disabled).toEqual(true);
expect(inputs[3].nativeElement.disabled).toEqual(true);
form.enable();
expect(inputs[0].nativeElement.disabled).toEqual(false);
expect(inputs[1].nativeElement.disabled).toEqual(false);
expect(inputs[2].nativeElement.disabled).toEqual(false);
expect(inputs[3].nativeElement.disabled).toEqual(false);
});
it('should disable all radio buttons when initially disabled', () => {
const fixture = TestBed.createComponent(FormControlRadioButtons);
const form = new FormGroup({
food: new FormControl({value: 'fish', disabled: true}),
drink: new FormControl('cola')
});
fixture.componentInstance.form = form;
fixture.detectChanges();
const inputs = fixture.debugElement.queryAll(By.css('input'));
expect(inputs[0].nativeElement.disabled).toEqual(true);
expect(inputs[1].nativeElement.disabled).toEqual(true);
expect(inputs[2].nativeElement.disabled).toEqual(false);
expect(inputs[3].nativeElement.disabled).toEqual(false);
});
});
describe('custom value accessors', () => {

View File

@ -420,6 +420,64 @@ export function main() {
});
}));
it('should disable a control with unbound disabled attr', fakeAsync(() => {
TestBed.overrideComponent(NgModelForm, {
set: {
template: `
<form>
<input name="name" [(ngModel)]="name" disabled>
</form>
`,
}
});
const fixture = TestBed.createComponent(NgModelForm);
fixture.detectChanges();
tick();
const form = fixture.debugElement.children[0].injector.get(NgForm);
expect(form.control.get('name').disabled).toBe(true);
const input = fixture.debugElement.query(By.css('input'));
expect(input.nativeElement.disabled).toEqual(true);
form.control.enable();
fixture.detectChanges();
tick();
expect(input.nativeElement.disabled).toEqual(false);
}));
it('should disable radio controls properly with programmatic call', fakeAsync(() => {
const fixture = TestBed.createComponent(NgModelRadioForm);
fixture.componentInstance.food = 'fish';
fixture.detectChanges();
tick();
const form = fixture.debugElement.children[0].injector.get(NgForm);
form.control.get('food').disable();
tick();
const inputs = fixture.debugElement.queryAll(By.css('input'));
expect(inputs[0].nativeElement.disabled).toBe(true);
expect(inputs[1].nativeElement.disabled).toBe(true);
expect(inputs[2].nativeElement.disabled).toBe(false);
expect(inputs[3].nativeElement.disabled).toBe(false);
form.control.disable();
tick();
expect(inputs[0].nativeElement.disabled).toBe(true);
expect(inputs[1].nativeElement.disabled).toBe(true);
expect(inputs[2].nativeElement.disabled).toBe(true);
expect(inputs[3].nativeElement.disabled).toBe(true);
form.control.enable();
tick();
expect(inputs[0].nativeElement.disabled).toBe(false);
expect(inputs[1].nativeElement.disabled).toBe(false);
expect(inputs[2].nativeElement.disabled).toBe(false);
expect(inputs[3].nativeElement.disabled).toBe(false);
}));
});
describe('radio controls', () => {
@ -903,7 +961,7 @@ class NgModelOptionsStandalone {
<form>
<input type="radio" name="food" [(ngModel)]="food" value="chicken">
<input type="radio" name="food" [(ngModel)]="food" value="fish">
<input type="radio" name="drink" [(ngModel)]="drink" value="cola">
<input type="radio" name="drink" [(ngModel)]="drink" value="sprite">
</form>

View File

@ -7,7 +7,7 @@
*/
import {ListWrapper, Map, MapWrapper, StringMapWrapper, isListLikeIterable, iterateListLike} from '../src/facade/collection';
import {ListWrapper, MapWrapper, StringMapWrapper, isListLikeIterable, iterateListLike} from '../src/facade/collection';
import {isBlank} from '../src/facade/lang';

View File

@ -6,9 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ListWrapper, Map, isListLikeIterable} from '../src/facade/collection';
import {isPresent} from '../src/facade/lang';
function paramParser(rawParams: string = ''): Map<string, string[]> {
const map = new Map<string, string[]>();
if (rawParams.length > 0) {
@ -97,23 +94,16 @@ export class URLSearchParams {
has(param: string): boolean { return this.paramsMap.has(param); }
get(param: string): string {
var storedParam = this.paramsMap.get(param);
if (isListLikeIterable(storedParam)) {
return ListWrapper.first(storedParam);
} else {
return null;
}
const storedParam = this.paramsMap.get(param);
return Array.isArray(storedParam) ? storedParam[0] : null;
}
getAll(param: string): string[] {
var mapParam = this.paramsMap.get(param);
return isPresent(mapParam) ? mapParam : [];
}
getAll(param: string): string[] { return this.paramsMap.get(param) || []; }
set(param: string, val: string) {
var mapParam = this.paramsMap.get(param);
var list = isPresent(mapParam) ? mapParam : [];
ListWrapper.clear(list);
const list = this.paramsMap.get(param) || [];
list.length = 0;
list.push(val);
this.paramsMap.set(param, list);
}
@ -126,17 +116,15 @@ export class URLSearchParams {
// TODO(@caitp): document this better
setAll(searchParams: URLSearchParams) {
searchParams.paramsMap.forEach((value, param) => {
var mapParam = this.paramsMap.get(param);
var list = isPresent(mapParam) ? mapParam : [];
ListWrapper.clear(list);
const list = this.paramsMap.get(param) || [];
list.length = 0;
list.push(value[0]);
this.paramsMap.set(param, list);
});
}
append(param: string, val: string): void {
var mapParam = this.paramsMap.get(param);
var list = isPresent(mapParam) ? mapParam : [];
const list = this.paramsMap.get(param) || [];
list.push(val);
this.paramsMap.set(param, list);
}
@ -150,9 +138,8 @@ export class URLSearchParams {
// TODO(@caitp): document this better
appendAll(searchParams: URLSearchParams) {
searchParams.paramsMap.forEach((value, param) => {
var mapParam = this.paramsMap.get(param);
var list = isPresent(mapParam) ? mapParam : [];
for (var i = 0; i < value.length; ++i) {
const list = this.paramsMap.get(param) || [];
for (let i = 0; i < value.length; ++i) {
list.push(value[i]);
}
this.paramsMap.set(param, list);
@ -169,9 +156,8 @@ export class URLSearchParams {
// TODO(@caitp): document this better
replaceAll(searchParams: URLSearchParams) {
searchParams.paramsMap.forEach((value, param) => {
var mapParam = this.paramsMap.get(param);
var list = isPresent(mapParam) ? mapParam : [];
ListWrapper.clear(list);
const list = this.paramsMap.get(param) || [];
list.length = 0;
for (var i = 0; i < value.length; ++i) {
list.push(value[i]);
}
@ -180,7 +166,7 @@ export class URLSearchParams {
}
toString(): string {
var paramsList: string[] = [];
const paramsList: string[] = [];
this.paramsMap.forEach((values, k) => {
values.forEach(
v => paramsList.push(

View File

@ -9,19 +9,16 @@
import {ReflectiveInjector} from '@angular/core';
import {AsyncTestCompleter, SpyObject, afterEach, beforeEach, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {expect} from '@angular/platform-browser/testing/matchers';
import {BrowserJsonp} from '../../src/backends/browser_jsonp';
import {JSONPBackend, JSONPBackend_, JSONPConnection, JSONPConnection_} from '../../src/backends/jsonp_backend';
import {BaseRequestOptions, RequestOptions} from '../../src/base_request_options';
import {BaseResponseOptions, ResponseOptions} from '../../src/base_response_options';
import {ReadyState, RequestMethod, ResponseType} from '../../src/enums';
import {Map} from '../../src/facade/collection';
import {isPresent} from '../../src/facade/lang';
import {Request} from '../../src/static_request';
import {Response} from '../../src/static_response';
var existingScripts: MockBrowserJsonp[] = [];
var unused: Response;
class MockBrowserJsonp extends BrowserJsonp {
src: string;

View File

@ -15,7 +15,6 @@ import {CookieXSRFStrategy, XHRBackend, XHRConnection} from '../../src/backends/
import {BaseRequestOptions, RequestOptions} from '../../src/base_request_options';
import {BaseResponseOptions, ResponseOptions} from '../../src/base_response_options';
import {ResponseContentType, ResponseType} from '../../src/enums';
import {Map} from '../../src/facade/collection';
import {Json} from '../../src/facade/lang';
import {Headers} from '../../src/headers';
import {XSRFStrategy} from '../../src/interfaces';
@ -27,9 +26,7 @@ var abortSpy: any;
var sendSpy: any;
var openSpy: any;
var setRequestHeaderSpy: any;
var addEventListenerSpy: any;
var existingXHRs: MockBrowserXHR[] = [];
var unused: Response;
class MockBrowserXHR extends BrowserXhr {
abort: any;

View File

@ -7,8 +7,6 @@
*/
import {beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {Map, StringMapWrapper} from '../src/facade/collection';
import {Json} from '../src/facade/lang';
import {Headers} from '../src/headers';
@ -24,21 +22,20 @@ export function main() {
// Spec at https://tools.ietf.org/html/rfc2616
expect(firstHeaders.get('content-type')).toBe('image/jpeg');
expect(firstHeaders.get('content-Type')).toBe('image/jpeg');
var httpHeaders = StringMapWrapper.create();
StringMapWrapper.set(httpHeaders, 'Content-Type', 'image/jpeg');
StringMapWrapper.set(httpHeaders, 'Accept-Charset', 'utf-8');
StringMapWrapper.set(httpHeaders, 'X-My-Custom-Header', 'Zeke are cool');
var secondHeaders = new Headers(httpHeaders);
var secondHeadersObj = new Headers(secondHeaders);
const httpHeaders = {
'Content-Type': 'image/jpeg',
'Accept-Charset': 'utf-8',
'X-My-Custom-Header': 'Zeke are cool',
};
const secondHeaders = new Headers(httpHeaders);
const secondHeadersObj = new Headers(secondHeaders);
expect(secondHeadersObj.get('Content-Type')).toBe('image/jpeg');
});
describe('initialization', () => {
it('should merge values in provided dictionary', () => {
var map = StringMapWrapper.create();
StringMapWrapper.set(map, 'foo', 'bar');
var headers = new Headers(map);
var headers = new Headers({'foo': 'bar'});
expect(headers.get('foo')).toBe('bar');
expect(headers.getAll('foo')).toEqual(['bar']);
});
@ -55,9 +52,7 @@ export function main() {
describe('.set()', () => {
it('should clear all values and re-set for the provided key', () => {
var map = StringMapWrapper.create();
StringMapWrapper.set(map, 'foo', 'bar');
var headers = new Headers(map);
var headers = new Headers({'foo': 'bar'});
expect(headers.get('foo')).toBe('bar');
expect(headers.getAll('foo')).toEqual(['bar']);
headers.set('foo', 'baz');

View File

@ -164,7 +164,7 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter {
childNodes(el: any /** TODO #9100 */): Node[] { return el.childNodes; }
childNodesAsList(el: any /** TODO #9100 */): any[] {
var childNodes = el.childNodes;
var res = ListWrapper.createFixedSize(childNodes.length);
var res = new Array(childNodes.length);
for (var i = 0; i < childNodes.length; i++) {
res[i] = childNodes[i];
}

View File

@ -6,8 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import {StringMapWrapper} from '../../facade/collection';
import {EventManagerPlugin} from './event_manager';
var _eventNames = {
@ -53,7 +51,6 @@ export class HammerGesturesPluginCommon extends EventManagerPlugin {
constructor() { super(); }
supports(eventName: string): boolean {
eventName = eventName.toLowerCase();
return StringMapWrapper.contains(_eventNames, eventName);
return _eventNames.hasOwnProperty(eventName.toLowerCase());
}
}

View File

@ -72,7 +72,7 @@ export class KeyEventsPlugin extends EventManagerPlugin {
// returning null instead of throwing to let another plugin process the event
return null;
}
var result = StringMapWrapper.create();
var result = {};
StringMapWrapper.set(result, 'domEventName', domEventName);
StringMapWrapper.set(result, 'fullKey', fullKey);
return result;

View File

@ -11,12 +11,10 @@ import {beforeEach, ddescribe, describe, expect, iit, it, xdescribe, xit} from '
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {DomEventsPlugin} from '@angular/platform-browser/src/dom/events/dom_events';
import {EventManager, EventManagerPlugin} from '@angular/platform-browser/src/dom/events/event_manager';
import {ListWrapper, Map} from '../../../src/facade/collection';
import {el} from '../../../testing/browser_util';
export function main() {
var domEventPlugin: any /** TODO #9100 */;
var domEventPlugin: DomEventsPlugin;
describe('EventManager', () => {
@ -29,7 +27,7 @@ export function main() {
var plugin = new FakeEventManagerPlugin(['click']);
var manager = new EventManager([domEventPlugin, plugin], new FakeNgZone());
manager.addEventListener(element, 'click', handler);
expect(plugin._eventHandler.get('click')).toBe(handler);
expect(plugin.eventHandler['click']).toBe(handler);
});
it('should delegate event bindings to the first plugin supporting the event', () => {
@ -41,10 +39,8 @@ export function main() {
var manager = new EventManager([plugin2, plugin1], new FakeNgZone());
manager.addEventListener(element, 'click', clickHandler);
manager.addEventListener(element, 'dblclick', dblClickHandler);
expect(plugin1._eventHandler.has('click')).toBe(false);
expect(plugin2._eventHandler.get('click')).toBe(clickHandler);
expect(plugin2._eventHandler.has('dblclick')).toBe(false);
expect(plugin1._eventHandler.get('dblclick')).toBe(dblClickHandler);
expect(plugin2.eventHandler['click']).toBe(clickHandler);
expect(plugin1.eventHandler['dblclick']).toBe(dblClickHandler);
});
it('should throw when no plugin can handle the event', () => {
@ -91,22 +87,23 @@ export function main() {
});
}
/** @internal */
class FakeEventManagerPlugin extends EventManagerPlugin {
/** @internal */
_eventHandler = new Map<string, Function>();
constructor(public _supports: string[]) { super(); }
eventHandler: {[event: string]: Function} = {};
supports(eventName: string): boolean { return ListWrapper.contains(this._supports, eventName); }
constructor(public supportedEvents: string[]) { super(); }
addEventListener(element: any /** TODO #9100 */, eventName: string, handler: Function) {
this._eventHandler.set(eventName, handler);
return () => { this._eventHandler.delete(eventName); };
supports(eventName: string): boolean { return this.supportedEvents.indexOf(eventName) > -1; }
addEventListener(element: any, eventName: string, handler: Function) {
this.eventHandler[eventName] = handler;
return () => { delete (this.eventHandler[eventName]); };
}
}
class FakeNgZone extends NgZone {
constructor() { super({enableLongStackTrace: false}); }
run(fn: any /** TODO #9100 */) { fn(); }
run(fn: Function) { fn(); }
runOutsideAngular(fn: any /** TODO #9100 */) { return fn(); }
runOutsideAngular(fn: Function) { return fn(); }
}

View File

@ -12,8 +12,6 @@ import {ListWrapper, StringMapWrapper} from '../src/facade/collection';
import {DomAdapter, setRootDomAdapter} from './private_import_platform-browser';
import {isPresent, isBlank, global, setValueOnPath, DateWrapper} from '../src/facade/lang';
import {SelectorMatcher, CssSelector} from './private_import_compiler';
import {Type} from '@angular/core';
import {ResourceLoader} from '@angular/compiler';
var parser: any /** TODO #9100 */ = null;
var serializer: any /** TODO #9100 */ = null;
@ -136,17 +134,13 @@ export class Parse5DomAdapter extends DomAdapter {
return result;
}
on(el: any /** TODO #9100 */, evt: any /** TODO #9100 */, listener: any /** TODO #9100 */) {
var listenersMap: {[k: /*any*/ string]: any} = el._eventListenersMap;
var listenersMap: {[k: string]: any} = el._eventListenersMap;
if (isBlank(listenersMap)) {
var listenersMap: {[k: /*any*/ string]: any} = StringMapWrapper.create();
var listenersMap: {[k: string]: any} = {};
el._eventListenersMap = listenersMap;
}
var listeners = StringMapWrapper.get(listenersMap, evt);
if (isBlank(listeners)) {
listeners = [];
}
listeners.push(listener);
StringMapWrapper.set(listenersMap, evt, listeners);
const listeners = listenersMap[evt] || [];
listenersMap[evt] = [...listeners, listener];
}
onAndCancel(
el: any /** TODO #9100 */, evt: any /** TODO #9100 */,
@ -209,7 +203,7 @@ export class Parse5DomAdapter extends DomAdapter {
childNodes(el: any /** TODO #9100 */): Node[] { return el.childNodes; }
childNodesAsList(el: any /** TODO #9100 */): any[] {
var childNodes = el.childNodes;
var res = ListWrapper.createFixedSize(childNodes.length);
var res = new Array(childNodes.length);
for (var i = 0; i < childNodes.length; i++) {
res[i] = childNodes[i];
}
@ -489,7 +483,7 @@ export class Parse5DomAdapter extends DomAdapter {
}
removeAttribute(element: any /** TODO #9100 */, attribute: string) {
if (attribute) {
StringMapWrapper.delete(element.attribs, attribute);
delete element.attribs[attribute];
}
}
removeAttributeNS(element: any /** TODO #9100 */, ns: string, name: string) {
@ -507,7 +501,7 @@ export class Parse5DomAdapter extends DomAdapter {
this.appendChild(newDoc, body);
StringMapWrapper.set(newDoc, 'head', head);
StringMapWrapper.set(newDoc, 'body', body);
StringMapWrapper.set(newDoc, '_window', StringMapWrapper.create());
StringMapWrapper.set(newDoc, '_window', {});
return newDoc;
}
defaultDoc(): Document {
@ -546,7 +540,7 @@ export class Parse5DomAdapter extends DomAdapter {
var rules: any[] /** TODO #9100 */ = [];
for (var i = 0; i < parsedRules.length; i++) {
var parsedRule = parsedRules[i];
var rule: {[key: string]: any} = StringMapWrapper.create();
var rule: {[key: string]: any} = {};
StringMapWrapper.set(rule, 'cssText', css);
StringMapWrapper.set(rule, 'style', {content: '', cssText: ''});
if (parsedRule.type == 'rule') {

View File

@ -156,15 +156,11 @@ class MessageData {
}
/**
* Returns the value from the StringMap if present. Otherwise returns null
* Returns the value if present, otherwise returns null
* @internal
*/
_getValueIfPresent(data: {[key: string]: any}, key: string) {
if (StringMapWrapper.contains(data, key)) {
return StringMapWrapper.get(data, key);
} else {
return null;
}
return data.hasOwnProperty(key) ? data[key] : null;
}
}

View File

@ -9,7 +9,6 @@
import {Injectable, NgZone} from '@angular/core';
import {EventEmitter} from '../../facade/async';
import {StringMapWrapper} from '../../facade/collection';
import {MessageBus, MessageBusSink, MessageBusSource} from './message_bus';
@ -22,7 +21,7 @@ export interface PostMessageTarget {
export class PostMessageBusSink implements MessageBusSink {
private _zone: NgZone;
private _channels: {[key: string]: _Channel} = StringMapWrapper.create();
private _channels: {[key: string]: _Channel} = {};
private _messageBuffer: Array<Object> = [];
constructor(private _postMessageTarget: PostMessageTarget) {}
@ -34,7 +33,7 @@ export class PostMessageBusSink implements MessageBusSink {
}
initChannel(channel: string, runInZone: boolean = true): void {
if (StringMapWrapper.contains(this._channels, channel)) {
if (this._channels.hasOwnProperty(channel)) {
throw new Error(`${channel} has already been initialized`);
}
@ -52,7 +51,7 @@ export class PostMessageBusSink implements MessageBusSink {
}
to(channel: string): EventEmitter<any> {
if (StringMapWrapper.contains(this._channels, channel)) {
if (this._channels.hasOwnProperty(channel)) {
return this._channels[channel].emitter;
} else {
throw new Error(`${channel} is not set up. Did you forget to call initChannel?`);
@ -71,7 +70,7 @@ export class PostMessageBusSink implements MessageBusSink {
export class PostMessageBusSource implements MessageBusSource {
private _zone: NgZone;
private _channels: {[key: string]: _Channel} = StringMapWrapper.create();
private _channels: {[key: string]: _Channel} = {};
constructor(eventTarget?: EventTarget) {
if (eventTarget) {
@ -86,7 +85,7 @@ export class PostMessageBusSource implements MessageBusSource {
attachToZone(zone: NgZone) { this._zone = zone; }
initChannel(channel: string, runInZone: boolean = true) {
if (StringMapWrapper.contains(this._channels, channel)) {
if (this._channels.hasOwnProperty(channel)) {
throw new Error(`${channel} has already been initialized`);
}
@ -96,7 +95,7 @@ export class PostMessageBusSource implements MessageBusSource {
}
from(channel: string): EventEmitter<any> {
if (StringMapWrapper.contains(this._channels, channel)) {
if (this._channels.hasOwnProperty(channel)) {
return this._channels[channel].emitter;
} else {
throw new Error(`${channel} is not set up. Did you forget to call initChannel?`);
@ -112,7 +111,7 @@ export class PostMessageBusSource implements MessageBusSource {
private _handleMessage(data: any): void {
var channel = data.channel;
if (StringMapWrapper.contains(this._channels, channel)) {
if (this._channels.hasOwnProperty(channel)) {
var channelInfo = this._channels[channel];
if (channelInfo.runInZone) {
this._zone.run(() => { channelInfo.emitter.emit(data.message); });

View File

@ -9,7 +9,6 @@
import {Injectable, Type} from '@angular/core';
import {EventEmitter} from '../../facade/async';
import {ListWrapper, Map} from '../../facade/collection';
import {FunctionWrapper, isPresent} from '../../facade/lang';
import {MessageBus} from '../shared/message_bus';
import {Serializer} from '../shared/serializer';
@ -72,7 +71,7 @@ export class ServiceMessageBroker_ extends ServiceMessageBroker {
this._methods.set(methodName, (message: ReceivedMessage) => {
var serializedArgs = message.args;
let numArgs = signature === null ? 0 : signature.length;
var deserializedArgs: any[] = ListWrapper.createFixedSize(numArgs);
var deserializedArgs: any[] = new Array(numArgs);
for (var i = 0; i < numArgs; i++) {
var serializedArg = serializedArgs[i];
deserializedArgs[i] = this._serializer.deserialize(serializedArg, signature[i]);

View File

@ -6,9 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Set} from '../../facade/collection';
import {isPresent} from '../../facade/lang';
const MOUSE_EVENT_PROPERTIES = [
'altKey', 'button', 'clientX', 'clientY', 'metaKey', 'movementX', 'movementY', 'offsetX',
'offsetY', 'region', 'screenX', 'screenY', 'shiftKey'
@ -56,7 +53,7 @@ function addTarget(e: Event, serializedEvent: {[key: string]: any}): {[key: stri
if (NODES_WITH_VALUE.has((<HTMLElement>e.target).tagName.toLowerCase())) {
var target = <HTMLInputElement>e.target;
serializedEvent['target'] = {'value': target.value};
if (isPresent(target.files)) {
if (target.files) {
serializedEvent['target']['files'] = target.files;
}
}

View File

@ -10,7 +10,6 @@ import {LocationChangeListener, PlatformLocation} from '@angular/common';
import {Injectable} from '@angular/core';
import {EventEmitter} from '../../facade/async';
import {StringMapWrapper} from '../../facade/collection';
import {StringWrapper} from '../../facade/lang';
import {ClientMessageBroker, ClientMessageBrokerFactory, FnArg, UiArguments} from '../shared/client_message_broker';
import {MessageBus} from '../shared/message_bus';
@ -37,7 +36,7 @@ export class WebWorkerPlatformLocation extends PlatformLocation {
this._channelSource.subscribe({
next: (msg: {[key: string]: any}) => {
var listeners: Array<Function> = null;
if (StringMapWrapper.contains(msg, 'event')) {
if (msg.hasOwnProperty('event')) {
let type: string = msg['event']['type'];
if (StringWrapper.equals(type, 'popstate')) {
listeners = this._popStateListeners;

View File

@ -16,8 +16,6 @@ import {SpyMessageBroker} from '../worker/spies';
import {MockEventEmitter} from './mock_event_emitter';
var __unused: Promise<any>; // avoid unused import when Promise union types are erased
/**
* Returns two MessageBus instances that are attached to each other.
* Such that whatever goes into one's sink comes out the others source.
@ -49,9 +47,9 @@ export function expectBrokerCall(
expect(args.method).toEqual(methodName);
if (isPresent(vals)) {
expect(args.args.length).toEqual(vals.length);
ListWrapper.forEachWithIndex(vals, (v, i) => { expect(v).toEqual(args.args[i].value); });
vals.forEach((v, i) => { expect(v).toEqual(args.args[i].value); });
}
var promise: any /** TODO #9100 */ = null;
var promise: Promise<any>|void = null;
if (isPresent(handler)) {
let givenValues = args.args.map((arg) => arg.value);
if (givenValues.length > 0) {
@ -81,13 +79,13 @@ export class MockMessageBusSource implements MessageBusSource {
constructor(private _channels: {[key: string]: MockEventEmitter<any>}) {}
initChannel(channel: string, runInZone = true) {
if (!StringMapWrapper.contains(this._channels, channel)) {
if (!this._channels.hasOwnProperty(channel)) {
this._channels[channel] = new MockEventEmitter();
}
}
from(channel: string): MockEventEmitter<any> {
if (!StringMapWrapper.contains(this._channels, channel)) {
if (!this._channels.hasOwnProperty(channel)) {
throw new Error(`${channel} is not set up. Did you forget to call initChannel?`);
}
return this._channels[channel];
@ -100,13 +98,13 @@ export class MockMessageBusSink implements MessageBusSink {
constructor(private _channels: {[key: string]: MockEventEmitter<any>}) {}
initChannel(channel: string, runInZone = true) {
if (!StringMapWrapper.contains(this._channels, channel)) {
if (!this._channels.hasOwnProperty(channel)) {
this._channels[channel] = new MockEventEmitter();
}
}
to(channel: string): MockEventEmitter<any> {
if (!StringMapWrapper.contains(this._channels, channel)) {
if (!this._channels.hasOwnProperty(channel)) {
this._channels[channel] = new MockEventEmitter();
}
return this._channels[channel];

View File

@ -401,9 +401,12 @@ export class UpgradeAdapter {
._bootstrapModuleWithZone(
DynamicNgUpgradeModule, undefined, ngZone,
(componentFactories: ComponentFactory<any>[]) => {
componentFactories.forEach((componentFactory) => {
componentFactoryRefMap[getComponentInfo(componentFactory.componentType)
.selector] = componentFactory;
componentFactories.forEach((componentFactory: ComponentFactory<any>) => {
var type: Type<any> = componentFactory.componentType;
if (this.upgradedComponents.indexOf(type) !== -1) {
componentFactoryRefMap[getComponentInfo(type).selector] =
componentFactory;
}
});
})
.then((ref: NgModuleRef<any>) => {

View File

@ -964,6 +964,31 @@ export function main() {
}));
});
it('should allow attribute selectors for components in ng2', async(() => {
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => MyNg2Module));
var ng1Module = angular.module('myExample', []);
@Component({selector: '[works]', template: 'works!'})
class WorksComponent {
}
@Component({selector: 'root-component', template: 'It <div works></div>'})
class RootComponent {
}
@NgModule({imports: [BrowserModule], declarations: [RootComponent, WorksComponent]})
class MyNg2Module {
}
ng1Module.directive('rootComponent', adapter.downgradeNg2Component(RootComponent));
document.body.innerHTML = '<root-component></root-component>';
adapter.bootstrap(document.body.firstElementChild, ['myExample']).ready((ref) => {
expect(multiTrim(document.body.textContent)).toEqual('It works!');
ref.dispose();
});
}));
describe('examples', () => {
it('should verify UpgradeAdapter example', async(() => {
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
@ -994,7 +1019,7 @@ export function main() {
document.body.innerHTML = '<ng2 name="World">project</ng2>';
adapter.bootstrap(document.body, ['myExample']).ready((ref) => {
adapter.bootstrap(document.body.firstElementChild, ['myExample']).ready((ref) => {
expect(multiTrim(document.body.textContent))
.toEqual('ng2[ng1[Hello World!](transclude)](project)');
ref.dispose();

View File

@ -4642,7 +4642,7 @@
}
},
"zone.js": {
"version": "0.6.21"
"version": "0.6.25"
}
},
"name": "angular-srcs",

6
npm-shrinkwrap.json generated
View File

@ -7414,9 +7414,9 @@
}
},
"zone.js": {
"version": "0.6.21",
"from": "zone.js@0.6.21",
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.6.21.tgz"
"version": "0.6.25",
"from": "zone.js@>=0.6.24 <0.7.0",
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.6.25.tgz"
}
}
}

View File

@ -1,6 +1,6 @@
{
"name": "angular-srcs",
"version": "2.0.0",
"version": "2.0.1",
"private": true,
"branchPattern": "2.0.*",
"description": "Angular 2 - a web framework for modern web apps",
@ -21,7 +21,7 @@
"core-js": "^2.4.1",
"reflect-metadata": "^0.1.3",
"rxjs": "5.0.0-beta.12",
"zone.js": "^0.6.21"
"zone.js": "^0.6.25"
},
"devDependencies": {
"@types/angularjs": "^1.5.13-alpha",

View File

@ -880,7 +880,7 @@ export declare abstract class TemplateRef<C> {
export declare class Testability implements PublicTestability {
constructor(_ngZone: NgZone);
decreasePendingRequestCount(): number;
findBindings(using: any, provider: string, exactMatch: boolean): any[];
/** @deprecated */ findBindings(using: any, provider: string, exactMatch: boolean): any[];
findProviders(using: any, provider: string, exactMatch: boolean): any[];
getPendingRequestCount(): number;
increasePendingRequestCount(): number;
@ -916,7 +916,7 @@ export declare const TRANSLATIONS_FORMAT: OpaqueToken;
export declare function trigger(name: string, animation: AnimationMetadata[]): AnimationEntryMetadata;
/** @stable */
export declare var Type: FunctionConstructor;
export declare const Type: FunctionConstructor;
/** @stable */
export interface TypeDecorator {

View File

@ -56,7 +56,7 @@ export declare type MetadataOverride<T> = {
/** @experimental */
export declare function resetFakeAsyncZone(): void;
/** @experimental */
/** @stable */
export declare class TestBed implements Injector {
ngModule: Type<any>;
platform: PlatformRef;