Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
712d1a7c37 | |||
16601f9359 | |||
b81e2e7a31 | |||
98fac36706 | |||
3e780c032e | |||
e09882180e | |||
0e18c57a17 | |||
51e2b9c073 | |||
f218e240d3 | |||
af6b219f8e | |||
20addf5f9f | |||
2860418a3c | |||
39e251eea7 | |||
d7d716d5db | |||
a95d65241c | |||
fdb22bd185 |
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
@ -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 ]
|
||||
|
19
CHANGELOG.md
19
CHANGELOG.md
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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[] = [
|
||||
|
@ -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();
|
||||
|
@ -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) {}
|
||||
|
@ -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(
|
||||
|
@ -67,7 +67,7 @@ export enum Plural {
|
||||
Two,
|
||||
Few,
|
||||
Many,
|
||||
Other
|
||||
Other,
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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] = [];
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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); }
|
||||
}
|
||||
|
@ -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>();
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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; }
|
||||
|
@ -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 {
|
||||
|
@ -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]);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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';
|
||||
|
@ -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[]>;
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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 [];
|
||||
|
@ -16,7 +16,7 @@
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export var Type = Function;
|
||||
export const Type = Function;
|
||||
|
||||
|
||||
export interface Type<T> extends Function { new (...args: any[]): T; }
|
||||
|
@ -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', () => {
|
||||
|
@ -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', () => {
|
||||
|
@ -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 {
|
||||
/**
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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', () => {
|
||||
|
@ -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', () => {
|
||||
|
@ -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>
|
||||
|
@ -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';
|
||||
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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');
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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(); }
|
||||
}
|
||||
|
@ -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') {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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); });
|
||||
|
@ -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]);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
|
@ -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>) => {
|
||||
|
@ -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();
|
||||
|
@ -4642,7 +4642,7 @@
|
||||
}
|
||||
},
|
||||
"zone.js": {
|
||||
"version": "0.6.21"
|
||||
"version": "0.6.25"
|
||||
}
|
||||
},
|
||||
"name": "angular-srcs",
|
||||
|
6
npm-shrinkwrap.json
generated
6
npm-shrinkwrap.json
generated
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
4
tools/public_api_guard/core/index.d.ts
vendored
4
tools/public_api_guard/core/index.d.ts
vendored
@ -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 {
|
||||
|
@ -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;
|
||||
|
Reference in New Issue
Block a user