Compare commits

...

23 Commits

Author SHA1 Message Date
69ad99dca6 chore(release): cut the 2.2.0-beta.0 release and add changelog 2016-10-20 14:36:46 -07:00
da5fc696bb fix(router): do not update primary route if only secondary outlet is given (#11797) 2016-10-20 10:59:08 -07:00
b44b6ef8f5 fix(router): module loader should start compiling modules when stubbedModules are set (#11742) 2016-10-20 10:58:53 -07:00
0f21a5823b cleanup(router): add a test verifying than NavigationEnd is not emitted after NavigationCancel 2016-10-20 10:56:12 -07:00
5ae6915600 fix(router): fix lazy loading triggered by redirects from wildcard routes
Closes #12183
2016-10-20 10:56:12 -07:00
8b9ab44eee feat(router): add support for ng1/ng2 migration (#12160) 2016-10-20 10:44:44 -07:00
b0a03fcab3 refactor(compiler): introduce directive wrappers to generate less code
- for now only wraps the `@Input` properties and calls
  to `ngOnInit`, `ngDoCheck` and `ngOnChanges` of directives.
- also groups eval sources by NgModule.

Part of #11683
2016-10-20 10:41:43 -07:00
c951822c35 refactor(compiler): don’t use the OfflineCompiler in extract_i18n 2016-10-20 10:41:43 -07:00
acda82c1ed refactor(compiler): remove private exports
All of `@angular/compiler` is private, so we can export
everything we need directly.
2016-10-20 10:41:43 -07:00
a8815d6b08 chore(ci): re-enable browserstack tests in ci 2016-10-20 10:01:51 -07:00
d6791ff0e0 feat(ngUpgrade): add support for AoT compiled upgrade applications
This commit introduces a new API to the ngUpgrade module, which is compatible
with AoT compilation. Primarily, it removes the dependency on reflection
over the Angular 2 metadata by introducing an API where this information
is explicitly defined, in the source code, in a way that is not lost through
AoT compilation.

This commit is a collaboration between @mhevery (who provided the original
design of the API); @gkalpak & @petebacondarwin (who implemented the
API and migrated the specs from the original ngUpgrade tests) and @alexeagle
(who provided input and review).

This commit is an starting point, there is still work to be done:

* add more documentation
* validate the API via internal projects
* align the ngUpgrade compilation of A1 directives closer to the real A1
  compiler
* add more unit tests
* consider support for async `templateUrl` A1 upgraded components

Closes #12239
2016-10-19 15:27:49 -07:00
a2d35641e3 chore(tslint.json): semicolon rule expects an array 2016-10-19 22:38:14 +01:00
76dd026447 refactor: remove some facades (#12335) 2016-10-19 13:42:39 -07:00
0ecd9b2df0 chore(ci): make browserstack tests optional until they are fixed 2016-10-19 10:41:00 -07:00
0e9503b500 feat(forms) range values need to be numbers instead of strings (#11792) 2016-10-19 10:12:13 -07:00
f77ab6a2d2 feat(datePipe): support narrow forms for month and weekdays (#12297)
Closes #12294
2016-10-19 10:05:13 -07:00
97bc97153b feat(forms): add ng-pending CSS class during async validation (#11243)
Closes #10336
2016-10-19 09:56:31 -07:00
445e5922ec feat(forms): make 'parent' a public property of 'AbstractControl' (#11855) 2016-10-19 09:55:50 -07:00
b9fc090143 feat(forms): Added emitEvent to AbstractControl methods (#11949)
* feat(forms): Added emitEvent to AbstractControl methods

* style(forms): unified named parameter
2016-10-19 09:54:54 -07:00
592f40aa9c feat(forms): add hasError and getError to AbstractControlDirective (#11985)
Allows cleaner expressions in template-driven forms.

Before:

    <label>Username</label><input name="username" ngModel required #username="ngModel">
    <div *ngIf="username.dirty && username.control.hasError('required')">Username is required</div>

After:

    <label>Username</label><input name="username" ngModel required #username="ngModel">
    <div *ngIf="username.dirty && username.hasError('required')">Username is required</div>

Fixes #7255
2016-10-19 09:49:02 -07:00
24facdea2d feat(benchmark): add large form benchmark
This benchmark tracks the generated file size for large forms
as well as the time to create and destroy many form fields.
2016-10-19 09:39:16 -07:00
aa2d3372a5 fix(benchmarks): fix method name in targetable spec 2016-10-19 09:39:16 -07:00
bf60418fdc feat(forms): Validator.pattern accepts a RegExp (#12323) 2016-10-19 09:37:54 -07:00
155 changed files with 5573 additions and 1191 deletions

View File

@ -1,3 +1,38 @@
<a name="2.2.0-beta.0"></a>
# [2.2.0-beta.0](https://github.com/angular/angular/compare/2.1.0...2.2.0-beta.0) (2016-10-20)
### Bug Fixes
* **compiler:** generate aot code for animation trigger output events ([#12291](https://github.com/angular/angular/issues/12291)) ([6e5f8b5](https://github.com/angular/angular/commit/6e5f8b5)), closes [#11707](https://github.com/angular/angular/issues/11707)
* **compiler:** don't redeclare a var in the same scope ([#12386](https://github.com/angular/angular/issues/12386)) ([cca4a5c](https://github.com/angular/angular/commit/cca4a5c))
* **core:** fix decorator defalut values ([bd1dcb5](https://github.com/angular/angular/commit/bd1dcb5))
* **core:** fix property decorators ([3993279](https://github.com/angular/angular/commit/3993279)), closes [#12224](https://github.com/angular/angular/issues/12224)
* **http:** make normalizeMethodName optimizer-compatible. ([#12370](https://github.com/angular/angular/issues/12370)) ([8409b65](https://github.com/angular/angular/commit/8409b65))
* **router:** correctly export filter operator in es5 ([#12286](https://github.com/angular/angular/issues/12286)) ([27d7677](https://github.com/angular/angular/commit/27d7677))
* **router:** do not update primary route if only secondary outlet is given ([#11797](https://github.com/angular/angular/issues/11797)) ([da5fc69](https://github.com/angular/angular/commit/da5fc69))
* **router:** fix lazy loading triggered by redirects from wildcard routes ([5ae6915](https://github.com/angular/angular/commit/5ae6915)), closes [#12183](https://github.com/angular/angular/issues/12183)
* **router:** module loader should start compiling modules when stubbedModules are set ([#11742](https://github.com/angular/angular/issues/11742)) ([b44b6ef](https://github.com/angular/angular/commit/b44b6ef))
### Features
* **common:** support narrow forms for month and weekdays in DatePipe ([#12297](https://github.com/angular/angular/issues/12297)) ([f77ab6a](https://github.com/angular/angular/commit/f77ab6a)), closes [#12294](https://github.com/angular/angular/issues/12294)
* **forms:** add hasError and getError to AbstractControlDirective ([#11985](https://github.com/angular/angular/issues/11985)) ([592f40a](https://github.com/angular/angular/commit/592f40a)), closes [#7255](https://github.com/angular/angular/issues/7255)
* **forms:** add ng-pending CSS class during async validation ([#11243](https://github.com/angular/angular/issues/11243)) ([97bc971](https://github.com/angular/angular/commit/97bc971)), closes [#10336](https://github.com/angular/angular/issues/10336)
* **forms:** Added emitEvent to AbstractControl methods ([#11949](https://github.com/angular/angular/issues/11949)) ([b9fc090](https://github.com/angular/angular/commit/b9fc090))
* **forms:** make 'parent' a public property of 'AbstractControl' ([#11855](https://github.com/angular/angular/issues/11855)) ([445e592](https://github.com/angular/angular/commit/445e592))
* **forms:** Validator.pattern accepts a RegExp ([#12323](https://github.com/angular/angular/issues/12323)) ([bf60418](https://github.com/angular/angular/commit/bf60418))
* **upgrade:** add support for AoT compiled upgrade applications ([d6791ff](https://github.com/angular/angular/commit/d6791ff)), closes [#12239](https://github.com/angular/angular/issues/12239)
* **router:** add support for ng1/ng2 migration ([#12160](https://github.com/angular/angular/issues/12160)) ([8b9ab44](https://github.com/angular/angular/commit/8b9ab44))
### Performance Improvements
* **common:** optimize NgSwitch default case ([fdf4309](https://github.com/angular/angular/commit/fdf4309))
<a name="2.1.0"></a>
# [2.1.0 incremental-metamorphosis](https://github.com/angular/angular/compare/2.1.0-rc.0...2.1.0) (2016-10-12)

View File

@ -23,7 +23,7 @@ module.exports = function(config) {
'node_modules/core-js/client/core.js',
// include Angular v1 for upgrade module testing
'node_modules/angular/angular.min.js',
'node_modules/angular/angular.js',
'node_modules/zone.js/dist/zone.js', 'node_modules/zone.js/dist/long-stack-trace-zone.js',
'node_modules/zone.js/dist/proxy.js', 'node_modules/zone.js/dist/sync-test.js',

View File

@ -9,7 +9,6 @@
import {Inject, Injectable} from '@angular/core';
import {Options} from '../common_options';
import {isNumber} from '../facade/lang';
import {Metric} from '../metric';
import {WebDriverAdapter} from '../web_driver_adapter';
@ -44,7 +43,7 @@ export class UserMetric extends Metric {
function getAndClearValues() {
Promise.all(names.map(name => adapter.executeScript(`return window.${name}`)))
.then((values: any[]) => {
if (values.every(isNumber)) {
if (values.every(v => typeof v === 'number')) {
Promise.all(names.map(name => adapter.executeScript(`delete window.${name}`)))
.then((_: any[]) => {
let map: {[k: string]: any} = {};

View File

@ -9,7 +9,6 @@
import {Inject, Injectable, OpaqueToken} from '@angular/core';
import {Options} from '../common_options';
import {Json} from '../facade/lang';
import {MeasureValues} from '../measure_values';
import {Reporter} from '../reporter';
import {SampleDescription} from '../sample_description';
@ -39,12 +38,14 @@ export class JsonFileReporter extends Reporter {
sortedProps(this._description.metrics).forEach((metricName) => {
stats[metricName] = formatStats(validSample, metricName);
});
var content = Json.stringify({
'description': this._description,
'stats': stats,
'completeSample': completeSample,
'validSample': validSample,
});
var content = JSON.stringify(
{
'description': this._description,
'stats': stats,
'completeSample': completeSample,
'validSample': validSample,
},
null, 2);
var filePath = `${this._path}/${this._description.id}_${this._now().getTime()}.json`;
return this._writeFile(filePath, content);
}

View File

@ -6,20 +6,15 @@
* found in the LICENSE file at https://angular.io/license
*/
import {NumberWrapper} from '../facade/lang';
import {MeasureValues} from '../measure_values';
import {Statistic} from '../statistic';
export function formatNum(n: number) {
return NumberWrapper.toFixed(n, 2);
return n.toFixed(2);
}
export function sortedProps(obj: {[key: string]: any}) {
var props: string[] = [];
props.push(...Object.keys(obj));
props.sort();
return props;
return Object.keys(obj).sort();
}
export function formatStats(validSamples: MeasureValues[], metricName: string): string {
@ -29,5 +24,5 @@ export function formatStats(validSamples: MeasureValues[], metricName: string):
var formattedMean = formatNum(mean);
// Note: Don't use the unicode character for +- as it might cause
// hickups for consoles...
return NumberWrapper.isNaN(cv) ? formattedMean : `${formattedMean}+-${Math.floor(cv)}%`;
return isNaN(cv) ? formattedMean : `${formattedMean}+-${Math.floor(cv)}%`;
}

View File

@ -6,8 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Math} from './facade/math';
export class Statistic {
static calculateCoefficientOfVariation(sample: number[], mean: number) {
return Statistic.calculateStandardDeviation(sample, mean) / mean * 100;

View File

@ -9,7 +9,7 @@
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
import {JsonFileReporter, MeasureValues, Options, ReflectiveInjector, SampleDescription} from '../../index';
import {Json, isPresent} from '../../src/facade/lang';
import {isPresent} from '../../src/facade/lang';
export function main() {
describe('file reporter', () => {
@ -51,7 +51,7 @@ export function main() {
[mv(0, 0, {'a': 3, 'b': 6}), mv(1, 1, {'a': 5, 'b': 9})]);
var regExp = /somePath\/someId_\d+\.json/;
expect(isPresent(loggedFile['filename'].match(regExp))).toBe(true);
var parsedContent = Json.parse(loggedFile['content']);
var parsedContent = JSON.parse(loggedFile['content']);
expect(parsedContent).toEqual({
'description': {
'id': 'someId',

View File

@ -9,7 +9,7 @@
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
import {ChromeDriverExtension, Options, ReflectiveInjector, WebDriverAdapter, WebDriverExtension} from '../../index';
import {Json, isBlank} from '../../src/facade/lang';
import {isBlank} from '../../src/facade/lang';
import {TraceEventFactory} from '../trace_event_factory';
export function main() {
@ -398,8 +398,8 @@ class MockDriverAdapter extends WebDriverAdapter {
if (type === 'performance') {
return Promise.resolve(this._events.map(
(event) => ({
'message':
Json.stringify({'message': {'method': this._messageMethod, 'params': event}})
'message': JSON.stringify(
{'message': {'method': this._messageMethod, 'params': event}}, null, 2)
})));
} else {
return null;

View File

@ -9,7 +9,6 @@
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
import {IOsDriverExtension, ReflectiveInjector, WebDriverAdapter, WebDriverExtension} from '../../index';
import {Json} from '../../src/facade/lang';
import {TraceEventFactory} from '../trace_event_factory';
export function main() {
@ -184,8 +183,9 @@ class MockDriverAdapter extends WebDriverAdapter {
if (type === 'performance') {
return Promise.resolve(this._perfRecords.map(function(record) {
return {
'message': Json.stringify(
{'message': {'method': 'Timeline.eventRecorded', 'params': {'record': record}}})
'message': JSON.stringify(
{'message': {'method': 'Timeline.eventRecorded', 'params': {'record': record}}}, null,
2)
};
}));
} else {

View File

@ -33,21 +33,21 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
* - `'shortTime'`: equivalent to `'jm'` (e.g. `12:05 PM` for `en-US`)
*
*
* | Component | Symbol | Short Form | Long Form | Numeric | 2-digit |
* |-----------|:------:|--------------|-------------------|-----------|-----------|
* | era | G | G (AD) | GGGG (Anno Domini)| - | - |
* | year | y | - | - | y (2015) | yy (15) |
* | month | M | MMM (Sep) | MMMM (September) | M (9) | MM (09) |
* | day | d | - | - | d (3) | dd (03) |
* | weekday | E | EEE (Sun) | EEEE (Sunday) | - | - |
* | hour | j | - | - | j (13) | jj (13) |
* | hour12 | h | - | - | h (1 PM) | hh (01 PM)|
* | hour24 | H | - | - | H (13) | HH (13) |
* | minute | m | - | - | m (5) | mm (05) |
* | second | s | - | - | s (9) | ss (09) |
* | timezone | z | - | z (Pacific Standard Time)| - | - |
* | timezone | Z | Z (GMT-8:00) | - | - | - |
* | timezone | a | a (PM) | - | - | - |
* | Component | Symbol | Narrow | Short Form | Long Form | Numeric | 2-digit |
* |-----------|:------:|--------|--------------|-------------------|-----------|-----------|
* | era | G | G (A) | GGG (AD) | GGGG (Anno Domini)| - | - |
* | year | y | - | - | - | y (2015) | yy (15) |
* | month | M | L (S) | MMM (Sep) | MMMM (September) | M (9) | MM (09) |
* | day | d | - | - | - | d (3) | dd (03) |
* | weekday | E | E (S) | EEE (Sun) | EEEE (Sunday) | - | - |
* | hour | j | - | - | - | j (13) | jj (13) |
* | hour12 | h | - | - | - | h (1 PM) | hh (01 PM)|
* | hour24 | H | - | - | - | H (13) | HH (13) |
* | minute | m | - | - | - | m (5) | mm (05) |
* | second | s | - | - | - | s (9) | ss (09) |
* | timezone | z | - | - | z (Pacific Standard Time)| - | - |
* | timezone | Z | - | Z (GMT-8:00) | - | - | - |
* | timezone | a | - | a (PM) | - | - | - |
*
* In javascript, only the components specified will be respected (not the ordering,
* punctuations, ...) and details of the formatting will be dependent on the locale.

View File

@ -7,7 +7,7 @@
*/
import {Pipe, PipeTransform} from '@angular/core';
import {isBlank, isStringMap} from '../facade/lang';
import {isBlank} from '../facade/lang';
import {NgLocalization, getPluralCategory} from '../localization';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
@ -37,7 +37,7 @@ export class I18nPluralPipe implements PipeTransform {
transform(value: number, pluralMap: {[count: string]: string}): string {
if (isBlank(value)) return '';
if (!isStringMap(pluralMap)) {
if (typeof pluralMap !== 'object' || pluralMap === null) {
throw new InvalidPipeArgumentError(I18nPluralPipe, pluralMap);
}

View File

@ -7,7 +7,7 @@
*/
import {Pipe, PipeTransform} from '@angular/core';
import {isBlank, isStringMap} from '../facade/lang';
import {isBlank} from '../facade/lang';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
/**
@ -31,10 +31,10 @@ export class I18nSelectPipe implements PipeTransform {
transform(value: string, mapping: {[key: string]: string}): string {
if (isBlank(value)) return '';
if (!isStringMap(mapping)) {
if (typeof mapping !== 'object' || mapping === null) {
throw new InvalidPipeArgumentError(I18nSelectPipe, mapping);
}
return mapping.hasOwnProperty(value) ? mapping[value] : '';
return mapping[value] || '';
}
}

View File

@ -8,10 +8,6 @@
import {Pipe, PipeTransform} from '@angular/core';
import {Json} from '../facade/lang';
/**
* @ngModule CommonModule
* @whatItDoes Converts value into JSON string.
@ -27,5 +23,5 @@ import {Json} from '../facade/lang';
*/
@Pipe({name: 'json', pure: false})
export class JsonPipe implements PipeTransform {
transform(value: any): string { return Json.stringify(value); }
transform(value: any): string { return JSON.stringify(value, null, 2); }
}

View File

@ -59,7 +59,7 @@ export function main() {
expect(pipe.transform(date, 'MMM')).toEqual('Jun');
expect(pipe.transform(date, 'MMMM')).toEqual('June');
expect(pipe.transform(date, 'd')).toEqual('15');
expect(pipe.transform(date, 'E')).toEqual('Mon');
expect(pipe.transform(date, 'EEE')).toEqual('Mon');
expect(pipe.transform(date, 'EEEE')).toEqual('Monday');
if (!browserDetection.isOldChrome) {
expect(pipe.transform(date, 'h')).toEqual('9');
@ -72,6 +72,9 @@ export function main() {
if (!browserDetection.isOldChrome) {
expect(pipe.transform(date, 'HH')).toEqual('09');
}
expect(pipe.transform(date, 'E')).toEqual('M');
expect(pipe.transform(date, 'L')).toEqual('J');
expect(pipe.transform(date, 'm')).toEqual('3');
expect(pipe.transform(date, 's')).toEqual('1');
expect(pipe.transform(date, 'mm')).toEqual('03');
@ -81,13 +84,13 @@ export function main() {
});
it('should format common multi component patterns', () => {
expect(pipe.transform(date, 'E, M/d/y')).toEqual('Mon, 6/15/2015');
expect(pipe.transform(date, 'E, M/d')).toEqual('Mon, 6/15');
expect(pipe.transform(date, 'EEE, M/d/y')).toEqual('Mon, 6/15/2015');
expect(pipe.transform(date, 'EEE, M/d')).toEqual('Mon, 6/15');
expect(pipe.transform(date, 'MMM d')).toEqual('Jun 15');
expect(pipe.transform(date, 'dd/MM/yyyy')).toEqual('15/06/2015');
expect(pipe.transform(date, 'MM/dd/yyyy')).toEqual('06/15/2015');
expect(pipe.transform(date, 'yMEd')).toEqual('20156Mon15');
expect(pipe.transform(date, 'MEd')).toEqual('6Mon15');
expect(pipe.transform(date, 'yMEEEd')).toEqual('20156Mon15');
expect(pipe.transform(date, 'MEEEd')).toEqual('6Mon15');
expect(pipe.transform(date, 'MMMd')).toEqual('Jun15');
expect(pipe.transform(date, 'yMMMMEEEEd')).toEqual('Monday, June 15, 2015');
// IE and Edge can't format a date to minutes and seconds without hours

View File

@ -11,8 +11,6 @@ import {Component} from '@angular/core';
import {TestBed, async} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/matchers';
import {Json} from '../../src/facade/lang';
export function main() {
describe('JsonPipe', () => {
var regNewLine = '\n';
@ -48,7 +46,7 @@ export function main() {
it('should return JSON-formatted string similar to Json.stringify', () => {
var dream1 = normalize(pipe.transform(inceptionObj));
var dream2 = normalize(Json.stringify(inceptionObj));
var dream2 = normalize(JSON.stringify(inceptionObj, null, 2));
expect(dream1).toEqual(dream2);
});
});
@ -74,7 +72,6 @@ export function main() {
mutable.push(2);
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('[\n 1,\n 2\n]');
}));
});
});

View File

@ -11,13 +11,12 @@
* Intended to be used in a build step.
*/
import * as compiler from '@angular/compiler';
import {Component, NgModule, ViewEncapsulation} from '@angular/core';
import {Directive, NgModule, ViewEncapsulation} from '@angular/core';
import {AngularCompilerOptions, NgcCliOptions} from '@angular/tsc-wrapped';
import * as path from 'path';
import * as ts from 'typescript';
import {PathMappedReflectorHost} from './path_mapped_reflector_host';
import {CompileMetadataResolver, DirectiveNormalizer, DomElementSchemaRegistry, HtmlParser, Lexer, NgModuleCompiler, Parser, StyleCompiler, TemplateParser, TypeScriptEmitter, ViewCompiler} from './private_import_compiler';
import {Console} from './private_import_core';
import {ReflectorHost, ReflectorHostContext} from './reflector_host';
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
@ -59,7 +58,7 @@ export class CodeGeneratorModuleCollector {
private readFileMetadata(absSourcePath: string): FileMetadata {
const moduleMetadata = this.staticReflector.getModuleMetadata(absSourcePath);
const result: FileMetadata = {components: [], ngModules: [], fileUrl: absSourcePath};
const result: FileMetadata = {directives: [], ngModules: [], fileUrl: absSourcePath};
if (!moduleMetadata) {
console.log(`WARNING: no metadata found for ${absSourcePath}`);
return result;
@ -79,8 +78,8 @@ export class CodeGeneratorModuleCollector {
annotations.forEach((annotation) => {
if (annotation instanceof NgModule) {
result.ngModules.push(staticType);
} else if (annotation instanceof Component) {
result.components.push(staticType);
} else if (annotation instanceof Directive) {
result.directives.push(staticType);
}
});
}
@ -128,7 +127,7 @@ export class CodeGenerator {
(fileMeta) =>
this.compiler
.compile(
fileMeta.fileUrl, analyzedNgModules, fileMeta.components, fileMeta.ngModules)
fileMeta.fileUrl, analyzedNgModules, fileMeta.directives, fileMeta.ngModules)
.then((generatedModules) => {
generatedModules.forEach((generatedModule) => {
const sourceFile = this.program.getSourceFile(fileMeta.fileUrl);
@ -173,28 +172,30 @@ export class CodeGenerator {
const staticReflector = new StaticReflector(reflectorHost);
StaticAndDynamicReflectionCapabilities.install(staticReflector);
const htmlParser =
new compiler.I18NHtmlParser(new HtmlParser(), transContent, cliOptions.i18nFormat);
new compiler.I18NHtmlParser(new compiler.HtmlParser(), transContent, cliOptions.i18nFormat);
const config = new compiler.CompilerConfig({
genDebugInfo: options.debug === true,
defaultEncapsulation: ViewEncapsulation.Emulated,
logBindingUpdate: false,
useJit: false
});
const normalizer = new DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config);
const expressionParser = new Parser(new Lexer());
const elementSchemaRegistry = new DomElementSchemaRegistry();
const normalizer =
new compiler.DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config);
const expressionParser = new compiler.Parser(new compiler.Lexer());
const elementSchemaRegistry = new compiler.DomElementSchemaRegistry();
const console = new Console();
const tmplParser =
new TemplateParser(expressionParser, elementSchemaRegistry, htmlParser, console, []);
const resolver = new CompileMetadataResolver(
const tmplParser = new compiler.TemplateParser(
expressionParser, elementSchemaRegistry, htmlParser, console, []);
const resolver = new compiler.CompileMetadataResolver(
new compiler.NgModuleResolver(staticReflector),
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
elementSchemaRegistry, staticReflector);
// TODO(vicb): do not pass cliOptions.i18nFormat here
const offlineCompiler = new compiler.OfflineCompiler(
resolver, normalizer, tmplParser, new StyleCompiler(urlResolver), new ViewCompiler(config),
new NgModuleCompiler(), new TypeScriptEmitter(reflectorHost), cliOptions.locale,
cliOptions.i18nFormat);
resolver, normalizer, tmplParser, new compiler.StyleCompiler(urlResolver),
new compiler.ViewCompiler(config), new compiler.DirectiveWrapperCompiler(config),
new compiler.NgModuleCompiler(), new compiler.TypeScriptEmitter(reflectorHost),
cliOptions.locale, cliOptions.i18nFormat);
return new CodeGenerator(
options, program, compilerHost, staticReflector, offlineCompiler, reflectorHost);
@ -203,6 +204,6 @@ export class CodeGenerator {
export interface FileMetadata {
fileUrl: string;
components: StaticSymbol[];
directives: StaticSymbol[];
ngModules: StaticSymbol[];
}

View File

@ -21,7 +21,6 @@ import {Component, NgModule, ViewEncapsulation} from '@angular/core';
import * as path from 'path';
import * as ts from 'typescript';
import * as tsc from '@angular/tsc-wrapped';
import {CompileMetadataResolver, DirectiveNormalizer, DomElementSchemaRegistry, HtmlParser, Lexer, NgModuleCompiler, Parser, StyleCompiler, TemplateParser, TypeScriptEmitter, ViewCompiler, ParseError} from './private_import_compiler';
import {Console} from './private_import_core';
import {ReflectorHost, ReflectorHostContext} from './reflector_host';
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
@ -30,7 +29,7 @@ import {StaticReflector, StaticSymbol} from './static_reflector';
function extract(
ngOptions: tsc.AngularCompilerOptions, cliOptions: tsc.I18nExtractionCliOptions,
program: ts.Program, host: ts.CompilerHost) {
const htmlParser = new compiler.I18NHtmlParser(new HtmlParser());
const htmlParser = new compiler.I18NHtmlParser(new compiler.HtmlParser());
const extractor = Extractor.create(ngOptions, cliOptions.i18nFormat, program, host, htmlParser);
const bundlePromise: Promise<compiler.MessageBundle> = extractor.extract();
@ -63,9 +62,9 @@ export class Extractor {
constructor(
private program: ts.Program, public host: ts.CompilerHost,
private staticReflector: StaticReflector, private messageBundle: compiler.MessageBundle,
private reflectorHost: ReflectorHost, private metadataResolver: CompileMetadataResolver,
private directiveNormalizer: DirectiveNormalizer,
private compiler: compiler.OfflineCompiler) {}
private reflectorHost: ReflectorHost,
private metadataResolver: compiler.CompileMetadataResolver,
private directiveNormalizer: compiler.DirectiveNormalizer) {}
private readFileMetadata(absSourcePath: string): FileMetadata {
const moduleMetadata = this.staticReflector.getModuleMetadata(absSourcePath);
@ -105,8 +104,8 @@ export class Extractor {
ngModules.push(...fileMeta.ngModules);
return ngModules;
}, <StaticSymbol[]>[]);
const analyzedNgModules = this.compiler.analyzeModules(ngModules);
const errors: ParseError[] = [];
const analyzedNgModules = compiler.analyzeModules(ngModules, this.metadataResolver);
const errors: compiler.ParseError[] = [];
let bundlePromise =
Promise
@ -114,7 +113,7 @@ export class Extractor {
const url = fileMeta.fileUrl;
return Promise.all(fileMeta.components.map(compType => {
const compMeta = this.metadataResolver.getDirectiveMetadata(<any>compType);
const ngModule = analyzedNgModules.ngModuleByComponent.get(compType);
const ngModule = analyzedNgModules.ngModuleByDirective.get(compType);
if (!ngModule) {
throw new Error(
`Cannot determine the module for component ${compMeta.type.name}!`);
@ -168,26 +167,19 @@ export class Extractor {
useJit: false
});
const normalizer = new DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config);
const expressionParser = new Parser(new Lexer());
const elementSchemaRegistry = new DomElementSchemaRegistry();
const console = new Console();
const tmplParser =
new TemplateParser(expressionParser, elementSchemaRegistry, htmlParser, console, []);
const resolver = new CompileMetadataResolver(
const normalizer =
new compiler.DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config);
const elementSchemaRegistry = new compiler.DomElementSchemaRegistry();
const resolver = new compiler.CompileMetadataResolver(
new compiler.NgModuleResolver(staticReflector),
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
elementSchemaRegistry, staticReflector);
const offlineCompiler = new compiler.OfflineCompiler(
resolver, normalizer, tmplParser, new StyleCompiler(urlResolver), new ViewCompiler(config),
new NgModuleCompiler(), new TypeScriptEmitter(reflectorHost), null, null);
// TODO(vicb): implicit tags & attributes
let messageBundle = new compiler.MessageBundle(htmlParser, [], {});
return new Extractor(
program, compilerHost, staticReflector, messageBundle, reflectorHost, resolver, normalizer,
offlineCompiler);
program, compilerHost, staticReflector, messageBundle, reflectorHost, resolver, normalizer);
}
}

View File

@ -1,54 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {__compiler_private__ as _} from '@angular/compiler';
export type AssetUrl = typeof _._AssetUrl;
export var AssetUrl: typeof _.AssetUrl = _.AssetUrl;
export type ImportGenerator = typeof _._ImportGenerator;
export var ImportGenerator: typeof _.ImportGenerator = _.ImportGenerator;
export type CompileMetadataResolver = typeof _._CompileMetadataResolver;
export var CompileMetadataResolver: typeof _.CompileMetadataResolver = _.CompileMetadataResolver;
export type HtmlParser = typeof _._HtmlParser;
export var HtmlParser: typeof _.HtmlParser = _.HtmlParser;
export type ParseError = typeof _._ParseError;
export var ParseError: typeof _.ParseError = _.ParseError;
export type InterpolationConfig = typeof _._InterpolationConfig;
export var InterpolationConfig: typeof _.InterpolationConfig = _.InterpolationConfig;
export type DirectiveNormalizer = typeof _._DirectiveNormalizer;
export var DirectiveNormalizer: typeof _.DirectiveNormalizer = _.DirectiveNormalizer;
export type Lexer = typeof _._Lexer;
export var Lexer: typeof _.Lexer = _.Lexer;
export type Parser = typeof _._Parser;
export var Parser: typeof _.Parser = _.Parser;
export type TemplateParser = typeof _._TemplateParser;
export var TemplateParser: typeof _.TemplateParser = _.TemplateParser;
export type DomElementSchemaRegistry = typeof _._DomElementSchemaRegistry;
export var DomElementSchemaRegistry: typeof _.DomElementSchemaRegistry = _.DomElementSchemaRegistry;
export type StyleCompiler = typeof _._StyleCompiler;
export var StyleCompiler: typeof _.StyleCompiler = _.StyleCompiler;
export type ViewCompiler = typeof _._ViewCompiler;
export var ViewCompiler: typeof _.ViewCompiler = _.ViewCompiler;
export type NgModuleCompiler = typeof _._NgModuleCompiler;
export var NgModuleCompiler: typeof _.NgModuleCompiler = _.NgModuleCompiler;
export type TypeScriptEmitter = typeof _._TypeScriptEmitter;
export var TypeScriptEmitter: typeof _.TypeScriptEmitter = _.TypeScriptEmitter;

View File

@ -6,12 +6,12 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AssetUrl, ImportGenerator} from '@angular/compiler';
import {AngularCompilerOptions, MetadataCollector, ModuleMetadata} from '@angular/tsc-wrapped';
import * as fs from 'fs';
import * as path from 'path';
import * as ts from 'typescript';
import {AssetUrl, ImportGenerator} from './private_import_compiler';
import {StaticReflectorHost, StaticSymbol} from './static_reflector';
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;

View File

@ -21,6 +21,37 @@
* </p>
* </div>
*/
export * from './src/index';
export * from './src/template_parser/template_ast';
export {TEMPLATE_TRANSFORMS} from './src/template_parser/template_parser';
export {CompilerConfig, RenderTypes} from './src/config';
export * from './src/compile_metadata';
export * from './src/offline_compiler';
export {RuntimeCompiler} from './src/runtime_compiler';
export * from './src/url_resolver';
export * from './src/resource_loader';
export * from './src/compiler';
export {DirectiveResolver} from './src/directive_resolver';
export {PipeResolver} from './src/pipe_resolver';
export {NgModuleResolver} from './src/ng_module_resolver';
export {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from './src/ml_parser/interpolation_config';
export {ElementSchemaRegistry} from './src/schema/element_schema_registry';
export * from './src/i18n/index';
export * from './src/template_parser/template_ast';
export * from './src/directive_normalizer';
export * from './src/expression_parser/lexer';
export * from './src/expression_parser/parser';
export * from './src/metadata_resolver';
export * from './src/ml_parser/html_parser';
export * from './src/ml_parser/interpolation_config';
export {NgModuleCompiler} from './src/ng_module_compiler';
export {DirectiveWrapperCompiler} from './src/directive_wrapper_compiler';
export * from './src/output/path_util';
export * from './src/output/ts_emitter';
export * from './src/parse_util';
export * from './src/schema/dom_element_schema_registry';
export * from './src/selector';
export * from './src/style_compiler';
export * from './src/template_parser/template_parser';
export {ViewCompiler} from './src/view_compiler/view_compiler';
// This file only reexports content of the `src` folder. Keep it that way.

View File

@ -8,8 +8,7 @@
import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata} from '../compile_metadata';
import {ListWrapper, StringMapWrapper} from '../facade/collection';
import {isArray, isBlank, isPresent, isString, isStringMap} from '../facade/lang';
import {Math} from '../facade/math';
import {isBlank, isPresent} from '../facade/lang';
import {ParseError} from '../parse_util';
import {ANY_STATE, FILL_STYLE_FLAG} from '../private_import_core';
@ -97,7 +96,7 @@ function _parseAnimationDeclarationStates(
var styleValues: Styles[] = [];
stateMetadata.styles.styles.forEach(stylesEntry => {
// TODO (matsko): change this when we get CSS class integration support
if (isStringMap(stylesEntry)) {
if (typeof stylesEntry === 'object' && stylesEntry !== null) {
styleValues.push(stylesEntry as Styles);
} else {
errors.push(new AnimationParseError(
@ -172,8 +171,7 @@ function _parseAnimationTransitionExpr(
function _normalizeAnimationEntry(entry: CompileAnimationMetadata | CompileAnimationMetadata[]):
CompileAnimationMetadata {
return isArray(entry) ? new CompileAnimationSequenceMetadata(<CompileAnimationMetadata[]>entry) :
<CompileAnimationMetadata>entry;
return Array.isArray(entry) ? new CompileAnimationSequenceMetadata(entry) : entry;
}
function _normalizeStyleMetadata(
@ -181,7 +179,7 @@ function _normalizeStyleMetadata(
errors: AnimationParseError[]): {[key: string]: string | number}[] {
var normalizedStyles: {[key: string]: string | number}[] = [];
entry.styles.forEach(styleEntry => {
if (isString(styleEntry)) {
if (typeof styleEntry === 'string') {
ListWrapper.addAll(
normalizedStyles, _resolveStylesFromState(<string>styleEntry, stateStyles, errors));
} else {
@ -202,10 +200,10 @@ function _normalizeStyleSteps(
function _mergeAnimationStyles(
stylesList: any[], newItem: {[key: string]: string | number} | string) {
if (isStringMap(newItem) && stylesList.length > 0) {
if (typeof newItem === 'object' && newItem !== null && stylesList.length > 0) {
var lastIndex = stylesList.length - 1;
var lastItem = stylesList[lastIndex];
if (isStringMap(lastItem)) {
if (typeof lastItem === 'object' && lastItem !== null) {
stylesList[lastIndex] = StringMapWrapper.merge(
<{[key: string]: string | number}>lastItem, <{[key: string]: string | number}>newItem);
return;
@ -292,7 +290,7 @@ function _resolveStylesFromState(
`Unable to apply styles due to missing a state: "${normalizedStateName}"`));
} else {
value.styles.forEach(stylesEntry => {
if (isStringMap(stylesEntry)) {
if (typeof stylesEntry === 'object' && stylesEntry !== null) {
styles.push(stylesEntry as Styles);
}
});
@ -504,7 +502,7 @@ function _parseTimeExpression(
var duration: number;
var delay: number = 0;
var easing: string = null;
if (isString(exp)) {
if (typeof exp === 'string') {
const matches = exp.match(regex);
if (matches === null) {
errors.push(new AnimationParseError(`The provided timing value "${exp}" is invalid.`));

View File

@ -8,17 +8,17 @@
import {isDevMode} from '@angular/core';
import {isArray, isBlank, isPresent, isString} from '../src/facade/lang';
import {isBlank, isPresent} from '../src/facade/lang';
export function assertArrayOfStrings(identifier: string, value: any) {
if (!isDevMode() || isBlank(value)) {
return;
}
if (!isArray(value)) {
if (!Array.isArray(value)) {
throw new Error(`Expected '${identifier}' to be an array of strings.`);
}
for (var i = 0; i < value.length; i += 1) {
if (!isString(value[i])) {
if (typeof value[i] !== 'string') {
throw new Error(`Expected '${identifier}' to be an array of strings.`);
}
}
@ -33,7 +33,7 @@ const INTERPOLATION_BLACKLIST_REGEXPS = [
];
export function assertInterpolationSymbols(identifier: string, value: any): void {
if (isPresent(value) && !(isArray(value) && value.length == 2)) {
if (isPresent(value) && !(Array.isArray(value) && value.length == 2)) {
throw new Error(`Expected '${identifier}' to be an array, [start, end].`);
} else if (isDevMode() && !isBlank(value)) {
const start = value[0] as string;

View File

@ -9,7 +9,7 @@
import {ChangeDetectionStrategy, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core';
import {ListWrapper, MapWrapper} from './facade/collection';
import {isPresent, isStringMap, normalizeBlank, normalizeBool} from './facade/lang';
import {isPresent, normalizeBlank, normalizeBool} from './facade/lang';
import {LifecycleHooks} from './private_import_core';
import {CssSelector} from './selector';
import {sanitizeIdentifier, splitAtColon} from './util';
@ -594,7 +594,7 @@ function _normalizeArray(obj: any[]): any[] {
}
export function isStaticSymbol(value: any): value is StaticSymbol {
return isStringMap(value) && isPresent(value['name']) && isPresent(value['filePath']);
return typeof value === 'object' && value !== null && value['name'] && value['filePath'];
}
export interface StaticSymbol {

View File

@ -8,39 +8,27 @@
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, Injectable, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
export * from './template_parser/template_ast';
export {TEMPLATE_TRANSFORMS} from './template_parser/template_parser';
export {CompilerConfig, RenderTypes} from './config';
export * from './compile_metadata';
export * from './offline_compiler';
export {RuntimeCompiler} from './runtime_compiler';
export * from './url_resolver';
export * from './resource_loader';
export {DirectiveResolver} from './directive_resolver';
export {PipeResolver} from './pipe_resolver';
export {NgModuleResolver} from './ng_module_resolver';
import {TemplateParser} from './template_parser/template_parser';
import {HtmlParser} from './ml_parser/html_parser';
import {DirectiveNormalizer} from './directive_normalizer';
import {CompileMetadataResolver} from './metadata_resolver';
import {StyleCompiler} from './style_compiler';
import {ViewCompiler} from './view_compiler/view_compiler';
import {NgModuleCompiler} from './ng_module_compiler';
import {CompilerConfig} from './config';
import {RuntimeCompiler} from './runtime_compiler';
import {ElementSchemaRegistry} from './schema/element_schema_registry';
import {DomElementSchemaRegistry} from './schema/dom_element_schema_registry';
import {UrlResolver, DEFAULT_PACKAGE_URL_PROVIDER} from './url_resolver';
import {Parser} from './expression_parser/parser';
import {Lexer} from './expression_parser/lexer';
import {DirectiveNormalizer} from './directive_normalizer';
import {DirectiveResolver} from './directive_resolver';
import {PipeResolver} from './pipe_resolver';
import {NgModuleResolver} from './ng_module_resolver';
import {Console, Reflector, reflector, ReflectorReader, ReflectionCapabilities} from './private_import_core';
import {ResourceLoader} from './resource_loader';
import {DirectiveWrapperCompiler} from './directive_wrapper_compiler';
import {Lexer} from './expression_parser/lexer';
import {Parser} from './expression_parser/parser';
import * as i18n from './i18n/index';
import {CompileMetadataResolver} from './metadata_resolver';
import {HtmlParser} from './ml_parser/html_parser';
import {NgModuleCompiler} from './ng_module_compiler';
import {NgModuleResolver} from './ng_module_resolver';
import {PipeResolver} from './pipe_resolver';
import {Console, ReflectionCapabilities, Reflector, ReflectorReader, reflector} from './private_import_core';
import {ResourceLoader} from './resource_loader';
import {RuntimeCompiler} from './runtime_compiler';
import {DomElementSchemaRegistry} from './schema/dom_element_schema_registry';
import {ElementSchemaRegistry} from './schema/element_schema_registry';
import {StyleCompiler} from './style_compiler';
import {TemplateParser} from './template_parser/template_parser';
import {DEFAULT_PACKAGE_URL_PROVIDER, UrlResolver} from './url_resolver';
import {ViewCompiler} from './view_compiler/view_compiler';
const _NO_RESOURCE_LOADER: ResourceLoader = {
get(url: string): Promise<string>{
@ -77,6 +65,7 @@ export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> =
StyleCompiler,
ViewCompiler,
NgModuleCompiler,
DirectiveWrapperCompiler,
{provide: CompilerConfig, useValue: new CompilerConfig()},
RuntimeCompiler,
{provide: Compiler, useExisting: RuntimeCompiler},

View File

@ -0,0 +1,186 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Injectable} from '@angular/core';
import {CompileDirectiveMetadata, CompileIdentifierMetadata} from './compile_metadata';
import {CompilerConfig} from './config';
import {Identifiers, resolveIdentifier} from './identifiers';
import * as o from './output/output_ast';
import {LifecycleHooks, isDefaultChangeDetectionStrategy} from './private_import_core';
export class DirectiveWrapperCompileResult {
constructor(public statements: o.Statement[], public dirWrapperClassVar: string) {}
}
const CONTEXT_FIELD_NAME = 'context';
const CHANGES_FIELD_NAME = 'changes';
const CHANGED_FIELD_NAME = 'changed';
const CURR_VALUE_VAR = o.variable('currValue');
const THROW_ON_CHANGE_VAR = o.variable('throwOnChange');
const FORCE_UPDATE_VAR = o.variable('forceUpdate');
const VIEW_VAR = o.variable('view');
const RENDER_EL_VAR = o.variable('el');
const RESET_CHANGES_STMT = o.THIS_EXPR.prop(CHANGES_FIELD_NAME).set(o.literalMap([])).toStmt();
/**
* We generate directive wrappers to prevent code bloat when a directive is used.
* A directive wrapper encapsulates
* the dirty checking for `@Input`, the handling of `@HostListener` / `@HostBinding`
* and calling the lifecyclehooks `ngOnInit`, `ngOnChanges`, `ngDoCheck`.
*
* So far, only `@Input` and the lifecycle hooks have been implemented.
*/
@Injectable()
export class DirectiveWrapperCompiler {
static dirWrapperClassName(id: CompileIdentifierMetadata) { return `Wrapper_${id.name}`; }
constructor(private compilerConfig: CompilerConfig) {}
compile(dirMeta: CompileDirectiveMetadata): DirectiveWrapperCompileResult {
const dirDepParamNames: string[] = [];
for (let i = 0; i < dirMeta.type.diDeps.length; i++) {
dirDepParamNames.push(`p${i}`);
}
const dirLifecycleHooks = dirMeta.type.lifecycleHooks;
let lifecycleHooks: GenConfig = {
genChanges: dirLifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1 ||
this.compilerConfig.logBindingUpdate,
ngOnChanges: dirLifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1,
ngOnInit: dirLifecycleHooks.indexOf(LifecycleHooks.OnInit) !== -1,
ngDoCheck: dirLifecycleHooks.indexOf(LifecycleHooks.DoCheck) !== -1
};
const fields: o.ClassField[] = [
new o.ClassField(CONTEXT_FIELD_NAME, o.importType(dirMeta.type)),
new o.ClassField(CHANGED_FIELD_NAME, o.BOOL_TYPE),
];
const ctorStmts: o.Statement[] =
[o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(false)).toStmt()];
if (lifecycleHooks.genChanges) {
fields.push(new o.ClassField(CHANGES_FIELD_NAME, new o.MapType(o.DYNAMIC_TYPE)));
ctorStmts.push(RESET_CHANGES_STMT);
}
const methods: o.ClassMethod[] = [];
Object.keys(dirMeta.inputs).forEach((inputFieldName, idx) => {
const fieldName = `_${inputFieldName}`;
// private is fine here as no child view will reference the cached value...
fields.push(new o.ClassField(fieldName, null, [o.StmtModifier.Private]));
ctorStmts.push(o.THIS_EXPR.prop(fieldName)
.set(o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED)))
.toStmt());
methods.push(checkInputMethod(inputFieldName, o.THIS_EXPR.prop(fieldName), lifecycleHooks));
});
methods.push(detectChangesInternalMethod(lifecycleHooks, this.compilerConfig.genDebugInfo));
ctorStmts.push(
o.THIS_EXPR.prop(CONTEXT_FIELD_NAME)
.set(o.importExpr(dirMeta.type)
.instantiate(dirDepParamNames.map((paramName) => o.variable(paramName))))
.toStmt());
const ctor = new o.ClassMethod(
null, dirDepParamNames.map((paramName) => new o.FnParam(paramName, o.DYNAMIC_TYPE)),
ctorStmts);
const wrapperClassName = DirectiveWrapperCompiler.dirWrapperClassName(dirMeta.type);
const classStmt = new o.ClassStmt(wrapperClassName, null, fields, [], ctor, methods);
return new DirectiveWrapperCompileResult([classStmt], wrapperClassName);
}
}
function detectChangesInternalMethod(
lifecycleHooks: GenConfig, logBindingUpdate: boolean): o.ClassMethod {
const changedVar = o.variable('changed');
const stmts: o.Statement[] = [
changedVar.set(o.THIS_EXPR.prop(CHANGED_FIELD_NAME)).toDeclStmt(),
o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(false)).toStmt(),
];
const lifecycleStmts: o.Statement[] = [];
if (lifecycleHooks.genChanges) {
const onChangesStmts: o.Statement[] = [];
if (lifecycleHooks.ngOnChanges) {
onChangesStmts.push(o.THIS_EXPR.prop(CONTEXT_FIELD_NAME)
.callMethod('ngOnChanges', [o.THIS_EXPR.prop(CHANGES_FIELD_NAME)])
.toStmt());
}
if (logBindingUpdate) {
onChangesStmts.push(
o.importExpr(resolveIdentifier(Identifiers.setBindingDebugInfoForChanges))
.callFn(
[VIEW_VAR.prop('renderer'), RENDER_EL_VAR, o.THIS_EXPR.prop(CHANGES_FIELD_NAME)])
.toStmt());
}
onChangesStmts.push(RESET_CHANGES_STMT);
lifecycleStmts.push(new o.IfStmt(changedVar, onChangesStmts));
}
if (lifecycleHooks.ngOnInit) {
lifecycleStmts.push(new o.IfStmt(
VIEW_VAR.prop('numberOfChecks').identical(new o.LiteralExpr(0)),
[o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).callMethod('ngOnInit', []).toStmt()]));
}
if (lifecycleHooks.ngDoCheck) {
lifecycleStmts.push(o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).callMethod('ngDoCheck', []).toStmt());
}
if (lifecycleStmts.length > 0) {
stmts.push(new o.IfStmt(o.not(THROW_ON_CHANGE_VAR), lifecycleStmts));
}
stmts.push(new o.ReturnStatement(changedVar));
return new o.ClassMethod(
'detectChangesInternal',
[
new o.FnParam(
VIEW_VAR.name, o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
new o.FnParam(RENDER_EL_VAR.name, o.DYNAMIC_TYPE),
new o.FnParam(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
],
stmts, o.BOOL_TYPE);
}
function checkInputMethod(
input: string, fieldExpr: o.ReadPropExpr, lifecycleHooks: GenConfig): o.ClassMethod {
var onChangeStatements: o.Statement[] = [
o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(true)).toStmt(),
o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).prop(input).set(CURR_VALUE_VAR).toStmt(),
];
if (lifecycleHooks.genChanges) {
onChangeStatements.push(o.THIS_EXPR.prop(CHANGES_FIELD_NAME)
.key(o.literal(input))
.set(o.importExpr(resolveIdentifier(Identifiers.SimpleChange))
.instantiate([fieldExpr, CURR_VALUE_VAR]))
.toStmt());
}
onChangeStatements.push(fieldExpr.set(CURR_VALUE_VAR).toStmt());
var methodBody: o.Statement[] = [
new o.IfStmt(
FORCE_UPDATE_VAR.or(o.importExpr(resolveIdentifier(Identifiers.checkBinding))
.callFn([THROW_ON_CHANGE_VAR, fieldExpr, CURR_VALUE_VAR])),
onChangeStatements),
];
return new o.ClassMethod(
`check_${input}`,
[
new o.FnParam(CURR_VALUE_VAR.name, o.DYNAMIC_TYPE),
new o.FnParam(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
new o.FnParam(FORCE_UPDATE_VAR.name, o.BOOL_TYPE),
],
methodBody);
}
interface GenConfig {
genChanges: boolean;
ngOnChanges: boolean;
ngOnInit: boolean;
ngDoCheck: boolean;
}

View File

@ -8,7 +8,7 @@
import {Injectable} from '@angular/core';
import * as chars from '../chars';
import {NumberWrapper, StringJoiner, isPresent} from '../facade/lang';
import {NumberWrapper, isPresent} from '../facade/lang';
export enum TokenType {
Character,
@ -274,42 +274,41 @@ class _Scanner {
}
this.advance();
}
var str: string = this.input.substring(start, this.index);
var value: number = simple ? NumberWrapper.parseIntAutoRadix(str) : parseFloat(str);
const str: string = this.input.substring(start, this.index);
const value: number = simple ? NumberWrapper.parseIntAutoRadix(str) : parseFloat(str);
return newNumberToken(start, value);
}
scanString(): Token {
var start: number = this.index;
var quote: number = this.peek;
const start: number = this.index;
const quote: number = this.peek;
this.advance(); // Skip initial quote.
var buffer: StringJoiner;
var marker: number = this.index;
var input: string = this.input;
let buffer: string = '';
let marker: number = this.index;
let input: string = this.input;
while (this.peek != quote) {
if (this.peek == chars.$BACKSLASH) {
if (buffer == null) buffer = new StringJoiner();
buffer.add(input.substring(marker, this.index));
buffer += input.substring(marker, this.index);
this.advance();
var unescapedCode: number;
let unescapedCode: number;
if (this.peek == chars.$u) {
// 4 character hex code for unicode character.
var hex: string = input.substring(this.index + 1, this.index + 5);
const hex: string = input.substring(this.index + 1, this.index + 5);
try {
unescapedCode = NumberWrapper.parseInt(hex, 16);
} catch (e) {
return this.error(`Invalid unicode escape [\\u${hex}]`, 0);
}
for (var i: number = 0; i < 5; i++) {
for (let i: number = 0; i < 5; i++) {
this.advance();
}
} else {
unescapedCode = unescape(this.peek);
this.advance();
}
buffer.add(String.fromCharCode(unescapedCode));
buffer += String.fromCharCode(unescapedCode);
marker = this.index;
} else if (this.peek == chars.$EOF) {
return this.error('Unterminated quote', 0);
@ -318,16 +317,10 @@ class _Scanner {
}
}
var last: string = input.substring(marker, this.index);
const last: string = input.substring(marker, this.index);
this.advance(); // Skip terminating quote.
// Compute the unescaped string value.
var unescaped: string = last;
if (buffer != null) {
buffer.add(last);
unescaped = buffer.toString();
}
return newStringToken(start, unescaped);
return newStringToken(start, buffer + last);
}
error(message: string, offset: number): Token {

View File

@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ElementRef, Injector, LOCALE_ID as LOCALE_ID_, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT as TRANSLATIONS_FORMAT_, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
import {ANALYZE_FOR_ENTRY_COMPONENTS, AnimationTransitionEvent, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ElementRef, Injector, LOCALE_ID, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AnimationTransition, AppElement, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, DebugAppView, DebugContext, EMPTY_ARRAY, EMPTY_MAP, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewType, ViewUtils, balanceAnimationKeyframes, castByValue, checkBinding, clearStyles, collectAndResolveStyles, devModeEqual, flattenNestedViewRenderNodes, interpolate, prepareFinalAnimationStyles, pureProxy1, pureProxy10, pureProxy2, pureProxy3, pureProxy4, pureProxy5, pureProxy6, pureProxy7, pureProxy8, pureProxy9, reflector, registerModuleFactory, renderStyles} from './private_import_core';
import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AnimationTransition, AppElement, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, DebugAppView, DebugContext, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewType, balanceAnimationKeyframes, clearStyles, collectAndResolveStyles, devModeEqual, prepareFinalAnimationStyles, reflector, registerModuleFactory, renderStyles, view_utils} from './private_import_core';
import {assetUrl} from './util';
var APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view');
@ -33,7 +33,7 @@ export class Identifiers {
static ViewUtils: IdentifierSpec = {
name: 'ViewUtils',
moduleUrl: assetUrl('core', 'linker/view_utils'),
runtime: ViewUtils
runtime: view_utils.ViewUtils
};
static AppView:
IdentifierSpec = {name: 'AppView', moduleUrl: APP_VIEW_MODULE_URL, runtime: AppView};
@ -161,45 +161,48 @@ export class Identifiers {
static checkBinding: IdentifierSpec = {
name: 'checkBinding',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: checkBinding
runtime: view_utils.checkBinding
};
static flattenNestedViewRenderNodes: IdentifierSpec = {
name: 'flattenNestedViewRenderNodes',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: flattenNestedViewRenderNodes
runtime: view_utils.flattenNestedViewRenderNodes
};
static devModeEqual:
IdentifierSpec = {name: 'devModeEqual', moduleUrl: CD_MODULE_URL, runtime: devModeEqual};
static interpolate: IdentifierSpec = {
name: 'interpolate',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: interpolate
runtime: view_utils.interpolate
};
static castByValue: IdentifierSpec = {
name: 'castByValue',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: castByValue
runtime: view_utils.castByValue
};
static EMPTY_ARRAY: IdentifierSpec = {
name: 'EMPTY_ARRAY',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: EMPTY_ARRAY
runtime: view_utils.EMPTY_ARRAY
};
static EMPTY_MAP: IdentifierSpec = {
name: 'EMPTY_MAP',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: view_utils.EMPTY_MAP
};
static EMPTY_MAP:
IdentifierSpec = {name: 'EMPTY_MAP', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: EMPTY_MAP};
static pureProxies = [
null,
{name: 'pureProxy1', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy1},
{name: 'pureProxy2', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy2},
{name: 'pureProxy3', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy3},
{name: 'pureProxy4', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy4},
{name: 'pureProxy5', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy5},
{name: 'pureProxy6', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy6},
{name: 'pureProxy7', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy7},
{name: 'pureProxy8', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy8},
{name: 'pureProxy9', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy9},
{name: 'pureProxy10', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy10},
{name: 'pureProxy1', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy1},
{name: 'pureProxy2', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy2},
{name: 'pureProxy3', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy3},
{name: 'pureProxy4', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy4},
{name: 'pureProxy5', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy5},
{name: 'pureProxy6', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy6},
{name: 'pureProxy7', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy7},
{name: 'pureProxy8', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy8},
{name: 'pureProxy9', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy9},
{name: 'pureProxy10', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy10},
];
static SecurityContext: IdentifierSpec = {
name: 'SecurityContext',
@ -259,12 +262,22 @@ export class Identifiers {
static LOCALE_ID: IdentifierSpec = {
name: 'LOCALE_ID',
moduleUrl: assetUrl('core', 'i18n/tokens'),
runtime: LOCALE_ID_
runtime: LOCALE_ID
};
static TRANSLATIONS_FORMAT: IdentifierSpec = {
name: 'TRANSLATIONS_FORMAT',
moduleUrl: assetUrl('core', 'i18n/tokens'),
runtime: TRANSLATIONS_FORMAT_
runtime: TRANSLATIONS_FORMAT
};
static setBindingDebugInfo: IdentifierSpec = {
name: 'setBindingDebugInfo',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: view_utils.setBindingDebugInfo
};
static setBindingDebugInfoForChanges: IdentifierSpec = {
name: 'setBindingDebugInfoForChanges',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: view_utils.setBindingDebugInfoForChanges
};
static AnimationTransition: IdentifierSpec = {
name: 'AnimationTransition',

View File

@ -1,20 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* @module
* @description
* Starting point to import all compiler APIs.
*/
export {COMPILER_PROVIDERS, CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileFactoryMetadata, CompileIdentifierMetadata, CompileMetadataWithIdentifier, CompilePipeMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTemplateMetadata, CompileTokenMetadata, CompileTypeMetadata, CompilerConfig, DEFAULT_PACKAGE_URL_PROVIDER, DirectiveResolver, NgModuleResolver, OfflineCompiler, PipeResolver, RenderTypes, ResourceLoader, RuntimeCompiler, SourceModule, TEMPLATE_TRANSFORMS, UrlResolver, createOfflineCompileUrlResolver, platformCoreDynamic} from './compiler';
export {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from './ml_parser/interpolation_config';
export {ElementSchemaRegistry} from './schema/element_schema_registry';
export * from './i18n/index';
export * from './template_parser/template_ast';
export * from './private_export';

View File

@ -11,7 +11,7 @@ import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata
import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
import * as cpl from './compile_metadata';
import {DirectiveResolver} from './directive_resolver';
import {isBlank, isPresent, isString, stringify} from './facade/lang';
import {isBlank, isPresent, stringify} from './facade/lang';
import {Identifiers, resolveIdentifierToken} from './identifiers';
import {hasLifecycleHook} from './lifecycle_reflector';
import {NgModuleResolver} from './ng_module_resolver';
@ -116,8 +116,7 @@ export class CompileMetadataResolver {
return null;
}
getDirectiveMetadata(directiveType: Type<any>, throwIfNotFound = true):
cpl.CompileDirectiveMetadata {
getDirectiveMetadata(directiveType: any, throwIfNotFound = true): cpl.CompileDirectiveMetadata {
directiveType = resolveForwardRef(directiveType);
let meta = this._directiveCache.get(directiveType);
if (!meta) {
@ -569,7 +568,7 @@ export class CompileMetadataResolver {
getTokenMetadata(token: any): cpl.CompileTokenMetadata {
token = resolveForwardRef(token);
let compileToken: cpl.CompileTokenMetadata;
if (isString(token)) {
if (typeof token === 'string') {
compileToken = new cpl.CompileTokenMetadata({value: token});
} else {
compileToken = new cpl.CompileTokenMetadata({

View File

@ -12,6 +12,7 @@ import {AnimationCompiler} from './animation/animation_compiler';
import {AnimationParser} from './animation/animation_parser';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, StaticSymbol, createHostComponentMeta} from './compile_metadata';
import {DirectiveNormalizer} from './directive_normalizer';
import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from './directive_wrapper_compiler';
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from './identifiers';
import {CompileMetadataResolver} from './metadata_resolver';
import {NgModuleCompiler} from './ng_module_compiler';
@ -19,7 +20,7 @@ import {OutputEmitter} from './output/abstract_emitter';
import * as o from './output/output_ast';
import {CompiledStylesheet, StyleCompiler} from './style_compiler';
import {TemplateParser} from './template_parser/template_parser';
import {ComponentFactoryDependency, ViewCompileResult, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewCompileResult, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler';
export class SourceModule {
constructor(public moduleUrl: string, public source: string) {}
@ -27,25 +28,23 @@ export class SourceModule {
export class NgModulesSummary {
constructor(
public ngModuleByComponent: Map<StaticSymbol, CompileNgModuleMetadata>,
public ngModuleByDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
public ngModules: CompileNgModuleMetadata[]) {}
}
export function analyzeModules(
ngModules: StaticSymbol[], metadataResolver: CompileMetadataResolver) {
const ngModuleByComponent = new Map<StaticSymbol, CompileNgModuleMetadata>();
const ngModuleByDirective = new Map<StaticSymbol, CompileNgModuleMetadata>();
const modules: CompileNgModuleMetadata[] = [];
ngModules.forEach((ngModule) => {
const ngModuleMeta = metadataResolver.getNgModuleMetadata(<any>ngModule);
modules.push(ngModuleMeta);
ngModuleMeta.declaredDirectives.forEach((dirMeta: CompileDirectiveMetadata) => {
if (dirMeta.isComponent) {
ngModuleByComponent.set(dirMeta.type.reference, ngModuleMeta);
}
ngModuleByDirective.set(dirMeta.type.reference, ngModuleMeta);
});
});
return new NgModulesSummary(ngModuleByComponent, modules);
return new NgModulesSummary(ngModuleByDirective, modules);
}
export class OfflineCompiler {
@ -56,6 +55,7 @@ export class OfflineCompiler {
private _metadataResolver: CompileMetadataResolver,
private _directiveNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
private _dirWrapperCompiler: DirectiveWrapperCompiler,
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
private _localeId: string, private _translationFormat: string) {}
@ -69,7 +69,7 @@ export class OfflineCompiler {
}
compile(
moduleUrl: string, ngModulesSummary: NgModulesSummary, components: StaticSymbol[],
moduleUrl: string, ngModulesSummary: NgModulesSummary, directives: StaticSymbol[],
ngModules: StaticSymbol[]): Promise<SourceModule[]> {
const fileSuffix = _splitTypescriptSuffix(moduleUrl)[1];
const statements: o.Statement[] = [];
@ -80,11 +80,18 @@ export class OfflineCompiler {
exportedVars.push(
...ngModules.map((ngModuleType) => this._compileModule(ngModuleType, statements)));
// compile directive wrappers
exportedVars.push(...directives.map(
(directiveType) => this._compileDirectiveWrapper(directiveType, statements)));
// compile components
return Promise
.all(components.map((compType) => {
const compMeta = this._metadataResolver.getDirectiveMetadata(<any>compType);
const ngModule = ngModulesSummary.ngModuleByComponent.get(compType);
.all(directives.map((dirType) => {
const compMeta = this._metadataResolver.getDirectiveMetadata(<any>dirType);
if (!compMeta.isComponent) {
return Promise.resolve(null);
}
const ngModule = ngModulesSummary.ngModuleByDirective.get(dirType);
if (!ngModule) {
throw new Error(`Cannot determine the module for component ${compMeta.type.name}!`);
}
@ -148,6 +155,15 @@ export class OfflineCompiler {
return appCompileResult.ngModuleFactoryVar;
}
private _compileDirectiveWrapper(directiveType: StaticSymbol, targetStatements: o.Statement[]):
string {
const dirMeta = this._metadataResolver.getDirectiveMetadata(directiveType);
const dirCompileResult = this._dirWrapperCompiler.compile(dirMeta);
targetStatements.push(...dirCompileResult.statements);
return dirCompileResult.dirWrapperClassVar;
}
private _compileComponentFactory(
compMeta: CompileDirectiveMetadata, fileSuffix: string,
targetStatements: o.Statement[]): string {
@ -217,6 +233,9 @@ function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[]
const cfd = <ComponentFactoryDependency>dep;
cfd.placeholder.name = _componentFactoryName(cfd.comp);
cfd.placeholder.moduleUrl = _ngfactoryModuleUrl(cfd.comp.moduleUrl);
} else if (dep instanceof DirectiveWrapperDependency) {
const dwd = <DirectiveWrapperDependency>dep;
dwd.placeholder.moduleUrl = _ngfactoryModuleUrl(dwd.dir.moduleUrl);
}
});
return compileResult.statements;
@ -231,8 +250,8 @@ function _resolveStyleStatements(
return compileResult.statements;
}
function _ngfactoryModuleUrl(compUrl: string): string {
const urlWithSuffix = _splitTypescriptSuffix(compUrl);
function _ngfactoryModuleUrl(dirUrl: string): string {
const urlWithSuffix = _splitTypescriptSuffix(dirUrl);
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
}

View File

@ -6,14 +6,14 @@
* found in the LICENSE file at https://angular.io/license
*/
import {isBlank, isPresent, isString} from '../facade/lang';
import {isBlank, isPresent} from '../facade/lang';
import * as o from './output_ast';
var _SINGLE_QUOTE_ESCAPE_STRING_RE = /'|\\|\n|\r|\$/g;
var _LEGAL_IDENTIFIER_RE = /^[$A-Z_][0-9A-Z_$]*$/i;
export var CATCH_ERROR_VAR = o.variable('error');
export var CATCH_STACK_VAR = o.variable('stack');
const _SINGLE_QUOTE_ESCAPE_STRING_RE = /'|\\|\n|\r|\$/g;
const _LEGAL_IDENTIFIER_RE = /^[$A-Z_][0-9A-Z_$]*$/i;
export const CATCH_ERROR_VAR = o.variable('error');
export const CATCH_STACK_VAR = o.variable('stack');
export abstract class OutputEmitter {
abstract emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string;
@ -253,7 +253,7 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
visitLiteralExpr(ast: o.LiteralExpr, ctx: EmitterVisitorContext, absentValue: string = 'null'):
any {
var value = ast.value;
if (isString(value)) {
if (typeof value === 'string') {
ctx.print(escapeIdentifier(value, this._escapeDollarInStrings));
} else if (isBlank(value)) {
ctx.print(absentValue);

View File

@ -8,9 +8,7 @@
import {CompileIdentifierMetadata} from '../compile_metadata';
import {isPresent, isString} from '../facade/lang';
import {isPresent} from '../facade/lang';
//// Types
export enum TypeModifier {
@ -196,7 +194,7 @@ export class ReadVarExpr extends Expression {
constructor(name: string|BuiltinVar, type: Type = null) {
super(type);
if (isString(name)) {
if (typeof name === 'string') {
this.name = name;
this.builtin = null;
} else {
@ -267,7 +265,7 @@ export class InvokeMethodExpr extends Expression {
public receiver: Expression, method: string|BuiltinMethod, public args: Expression[],
type: Type = null) {
super(type);
if (isString(method)) {
if (typeof method === 'string') {
this.name = method;
this.builtin = null;
} else {

View File

@ -81,7 +81,7 @@ function createDynamicClass(
_executeFunctionStatements(
ctorParamNames, args, _classStmt.constructorMethod.body, instanceCtx, _visitor);
};
var superClass = _classStmt.parent.visitExpression(_visitor, _ctx);
var superClass = _classStmt.parent ? _classStmt.parent.visitExpression(_visitor, _ctx) : Object;
ctor.prototype = Object.create(superClass.prototype, propertyDescriptors);
return ctor;
}

View File

@ -6,13 +6,26 @@
* found in the LICENSE file at https://angular.io/license
*/
import {evalExpression, isPresent} from '../facade/lang';
import {isPresent} from '../facade/lang';
import {sanitizeIdentifier} from '../util';
import {EmitterVisitorContext} from './abstract_emitter';
import {AbstractJsEmitterVisitor} from './abstract_js_emitter';
import * as o from './output_ast';
function evalExpression(
sourceUrl: string, expr: string, declarations: string, vars: {[key: string]: any}): any {
const fnBody = `${declarations}\nreturn ${expr}\n//# sourceURL=${sourceUrl}`;
const fnArgNames: string[] = [];
const fnArgValues: any[] = [];
for (const argName in vars) {
fnArgNames.push(argName);
fnArgValues.push(vars[argName]);
}
return new Function(...fnArgNames.concat(fnBody))(...fnArgValues);
}
export function jitStatements(
sourceUrl: string, statements: o.Statement[], resultVar: string): any {
var converter = new JitEmitterVisitor();

View File

@ -8,24 +8,20 @@
import {CompileIdentifierMetadata} from '../compile_metadata';
import {isArray, isBlank, isPresent} from '../facade/lang';
import {isBlank, isPresent} from '../facade/lang';
import {AbstractEmitterVisitor, CATCH_ERROR_VAR, CATCH_STACK_VAR, EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
import * as o from './output_ast';
import {ImportGenerator} from './path_util';
var _debugModuleUrl = 'asset://debug/lib';
const _debugModuleUrl = 'asset://debug/lib';
export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.Type | any[]):
string {
var converter = new _TsEmitterVisitor(_debugModuleUrl);
var ctx = EmitterVisitorContext.createRoot([]);
var asts: any[];
if (isArray(ast)) {
asts = <any[]>ast;
} else {
asts = [ast];
}
const converter = new _TsEmitterVisitor(_debugModuleUrl);
const ctx = EmitterVisitorContext.createRoot([]);
const asts: any[] = Array.isArray(ast) ? ast : [ast];
asts.forEach((ast) => {
if (ast instanceof o.Statement) {
ast.visitStatement(converter, ctx);

View File

@ -1,112 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as directive_normalizer from './directive_normalizer';
import * as lexer from './expression_parser/lexer';
import * as parser from './expression_parser/parser';
import * as metadata_resolver from './metadata_resolver';
import * as html_parser from './ml_parser/html_parser';
import * as interpolation_config from './ml_parser/interpolation_config';
import * as ng_module_compiler from './ng_module_compiler';
import * as path_util from './output/path_util';
import * as ts_emitter from './output/ts_emitter';
import * as parse_util from './parse_util';
import * as dom_element_schema_registry from './schema/dom_element_schema_registry';
import * as selector from './selector';
import * as style_compiler from './style_compiler';
import * as template_parser from './template_parser/template_parser';
import * as view_compiler from './view_compiler/view_compiler';
export const __compiler_private__: {
_SelectorMatcher?: selector.SelectorMatcher; SelectorMatcher: typeof selector.SelectorMatcher;
_CssSelector?: selector.CssSelector;
CssSelector: typeof selector.CssSelector;
_AssetUrl?: path_util.AssetUrl;
AssetUrl: typeof path_util.AssetUrl;
_ImportGenerator?: path_util.ImportGenerator;
ImportGenerator: typeof path_util.ImportGenerator;
_CompileMetadataResolver?: metadata_resolver.CompileMetadataResolver;
CompileMetadataResolver: typeof metadata_resolver.CompileMetadataResolver;
_HtmlParser?: html_parser.HtmlParser;
HtmlParser: typeof html_parser.HtmlParser;
_InterpolationConfig?: interpolation_config.InterpolationConfig;
InterpolationConfig: typeof interpolation_config.InterpolationConfig;
_DirectiveNormalizer?: directive_normalizer.DirectiveNormalizer;
DirectiveNormalizer: typeof directive_normalizer.DirectiveNormalizer;
_Lexer?: lexer.Lexer;
Lexer: typeof lexer.Lexer;
_Parser?: parser.Parser;
Parser: typeof parser.Parser;
_ParseLocation?: parse_util.ParseLocation;
ParseLocation: typeof parse_util.ParseLocation;
_ParseError?: parse_util.ParseError;
ParseError: typeof parse_util.ParseError;
_ParseErrorLevel?: parse_util.ParseErrorLevel;
ParseErrorLevel: typeof parse_util.ParseErrorLevel;
_ParseSourceFile?: parse_util.ParseSourceFile;
ParseSourceFile: typeof parse_util.ParseSourceFile;
_ParseSourceSpan?: parse_util.ParseSourceSpan;
ParseSourceSpan: typeof parse_util.ParseSourceSpan;
_TemplateParser?: template_parser.TemplateParser;
TemplateParser: typeof template_parser.TemplateParser;
_TemplateParseResult?: template_parser.TemplateParseResult;
_DomElementSchemaRegistry?: dom_element_schema_registry.DomElementSchemaRegistry;
DomElementSchemaRegistry: typeof dom_element_schema_registry.DomElementSchemaRegistry;
_StyleCompiler?: style_compiler.StyleCompiler;
StyleCompiler: typeof style_compiler.StyleCompiler;
_ViewCompiler?: view_compiler.ViewCompiler;
ViewCompiler: typeof view_compiler.ViewCompiler;
_NgModuleCompiler?: ng_module_compiler.NgModuleCompiler;
NgModuleCompiler: typeof ng_module_compiler.NgModuleCompiler;
_TypeScriptEmitter?: ts_emitter.TypeScriptEmitter;
TypeScriptEmitter: typeof ts_emitter.TypeScriptEmitter;
} = {
SelectorMatcher: selector.SelectorMatcher,
CssSelector: selector.CssSelector,
AssetUrl: path_util.AssetUrl,
ImportGenerator: path_util.ImportGenerator,
CompileMetadataResolver: metadata_resolver.CompileMetadataResolver,
HtmlParser: html_parser.HtmlParser,
InterpolationConfig: interpolation_config.InterpolationConfig,
DirectiveNormalizer: directive_normalizer.DirectiveNormalizer,
Lexer: lexer.Lexer,
Parser: parser.Parser,
ParseLocation: parse_util.ParseLocation,
ParseError: parse_util.ParseError,
ParseErrorLevel: parse_util.ParseErrorLevel,
ParseSourceFile: parse_util.ParseSourceFile,
ParseSourceSpan: parse_util.ParseSourceSpan,
TemplateParser: template_parser.TemplateParser,
DomElementSchemaRegistry: dom_element_schema_registry.DomElementSchemaRegistry,
StyleCompiler: style_compiler.StyleCompiler,
ViewCompiler: view_compiler.ViewCompiler,
NgModuleCompiler: ng_module_compiler.NgModuleCompiler,
TypeScriptEmitter: ts_emitter.TypeScriptEmitter
};

View File

@ -27,13 +27,7 @@ export const NgModuleInjector: typeof r.NgModuleInjector = r.NgModuleInjector;
export const registerModuleFactory: typeof r.registerModuleFactory = r.registerModuleFactory;
export type ViewType = typeof r._ViewType;
export const ViewType: typeof r.ViewType = r.ViewType;
export const MAX_INTERPOLATION_VALUES: typeof r.MAX_INTERPOLATION_VALUES =
r.MAX_INTERPOLATION_VALUES;
export const checkBinding: typeof r.checkBinding = r.checkBinding;
export const flattenNestedViewRenderNodes: typeof r.flattenNestedViewRenderNodes =
r.flattenNestedViewRenderNodes;
export const interpolate: typeof r.interpolate = r.interpolate;
export const ViewUtils: typeof r.ViewUtils = r.ViewUtils;
export const view_utils: typeof r.view_utils = r.view_utils;
export const DebugContext: typeof r.DebugContext = r.DebugContext;
export const StaticNodeDebugInfo: typeof r.StaticNodeDebugInfo = r.StaticNodeDebugInfo;
export const devModeEqual: typeof r.devModeEqual = r.devModeEqual;
@ -42,19 +36,6 @@ export const ValueUnwrapper: typeof r.ValueUnwrapper = r.ValueUnwrapper;
export const TemplateRef_: typeof r.TemplateRef_ = r.TemplateRef_;
export type RenderDebugInfo = typeof r._RenderDebugInfo;
export const RenderDebugInfo: typeof r.RenderDebugInfo = r.RenderDebugInfo;
export const EMPTY_ARRAY: typeof r.EMPTY_ARRAY = r.EMPTY_ARRAY;
export const EMPTY_MAP: typeof r.EMPTY_MAP = r.EMPTY_MAP;
export const pureProxy1: typeof r.pureProxy1 = r.pureProxy1;
export const pureProxy2: typeof r.pureProxy2 = r.pureProxy2;
export const pureProxy3: typeof r.pureProxy3 = r.pureProxy3;
export const pureProxy4: typeof r.pureProxy4 = r.pureProxy4;
export const pureProxy5: typeof r.pureProxy5 = r.pureProxy5;
export const pureProxy6: typeof r.pureProxy6 = r.pureProxy6;
export const pureProxy7: typeof r.pureProxy7 = r.pureProxy7;
export const pureProxy8: typeof r.pureProxy8 = r.pureProxy8;
export const pureProxy9: typeof r.pureProxy9 = r.pureProxy9;
export const pureProxy10: typeof r.pureProxy10 = r.pureProxy10;
export const castByValue: typeof r.castByValue = r.castByValue;
export type Console = typeof r._Console;
export const Console: typeof r.Console = r.Console;
export const reflector: typeof r.reflector = r.reflector;

View File

@ -9,7 +9,7 @@
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata, CompileTypeMetadata} from './compile_metadata';
import {ListWrapper, MapWrapper} from './facade/collection';
import {isArray, isBlank, isPresent, normalizeBlank} from './facade/lang';
import {isBlank, isPresent, normalizeBlank} from './facade/lang';
import {Identifiers, resolveIdentifierToken} from './identifiers';
import {ParseError, ParseSourceSpan} from './parse_util';
import {AttrAst, DirectiveAst, ProviderAst, ProviderAstType, ReferenceAst} from './template_parser/template_ast';
@ -418,7 +418,7 @@ function _normalizeProviders(
}
if (isPresent(providers)) {
providers.forEach((provider) => {
if (isArray(provider)) {
if (Array.isArray(provider)) {
_normalizeProviders(<any[]>provider, sourceSpan, targetErrors, targetProviders);
} else {
let normalizeProvider: CompileProviderMetadata;

View File

@ -7,11 +7,13 @@
*/
import {Compiler, ComponentFactory, Injectable, Injector, ModuleWithComponentFactories, NgModuleFactory, SchemaMetadata, Type} from '@angular/core';
import {AnimationCompiler} from './animation/animation_compiler';
import {AnimationParser} from './animation/animation_parser';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, ProviderMeta, createHostComponentMeta} from './compile_metadata';
import {CompilerConfig} from './config';
import {DirectiveNormalizer} from './directive_normalizer';
import {DirectiveWrapperCompiler} from './directive_wrapper_compiler';
import {stringify} from './facade/lang';
import {CompileMetadataResolver} from './metadata_resolver';
import {NgModuleCompiler} from './ng_module_compiler';
@ -22,7 +24,8 @@ import {ComponentStillLoadingError} from './private_import_core';
import {CompiledStylesheet, StyleCompiler} from './style_compiler';
import {TemplateParser} from './template_parser/template_parser';
import {SyncAsyncResult} from './util';
import {ComponentFactoryDependency, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler';
/**
* An internal module of the Angular compiler that begins with component types,
@ -37,6 +40,7 @@ import {ComponentFactoryDependency, ViewCompiler, ViewFactoryDependency} from '.
export class RuntimeCompiler implements Compiler {
private _compiledTemplateCache = new Map<Type<any>, CompiledTemplate>();
private _compiledHostTemplateCache = new Map<Type<any>, CompiledTemplate>();
private _compiledDirectiveWrapperCache = new Map<Type<any>, Type<any>>();
private _compiledNgModuleCache = new Map<Type<any>, NgModuleFactory<any>>();
private _animationParser = new AnimationParser();
private _animationCompiler = new AnimationCompiler();
@ -45,7 +49,9 @@ export class RuntimeCompiler implements Compiler {
private _injector: Injector, private _metadataResolver: CompileMetadataResolver,
private _templateNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
private _ngModuleCompiler: NgModuleCompiler, private _compilerConfig: CompilerConfig) {}
private _ngModuleCompiler: NgModuleCompiler,
private _directiveWrapperCompiler: DirectiveWrapperCompiler,
private _compilerConfig: CompilerConfig) {}
get injector(): Injector { return this._injector; }
@ -80,10 +86,11 @@ export class RuntimeCompiler implements Compiler {
const moduleMeta = this._metadataResolver.getNgModuleMetadata(moduleType);
const componentFactories: ComponentFactory<any>[] = [];
const templates = new Set<CompiledTemplate>();
moduleMeta.transitiveModule.modules.forEach((moduleMeta) => {
moduleMeta.declaredDirectives.forEach((dirMeta) => {
moduleMeta.transitiveModule.modules.forEach((localModuleMeta) => {
localModuleMeta.declaredDirectives.forEach((dirMeta) => {
if (dirMeta.isComponent) {
const template = this._createCompiledHostTemplate(dirMeta.type.reference);
const template =
this._createCompiledHostTemplate(dirMeta.type.reference, localModuleMeta);
templates.add(template);
componentFactories.push(template.proxyComponentFactory);
}
@ -119,7 +126,7 @@ export class RuntimeCompiler implements Compiler {
interpretStatements(compileResult.statements, compileResult.ngModuleFactoryVar);
} else {
ngModuleFactory = jitStatements(
`${moduleMeta.type.name}.ngfactory.js`, compileResult.statements,
`/${moduleMeta.type.name}/module.ngfactory.js`, compileResult.statements,
compileResult.ngModuleFactoryVar);
}
this._compiledNgModuleCache.set(moduleMeta.type.reference, ngModuleFactory);
@ -135,22 +142,32 @@ export class RuntimeCompiler implements Compiler {
var loadingPromises: Promise<any>[] = [];
const ngModule = this._metadataResolver.getNgModuleMetadata(mainModule);
const moduleByDirective = new Map<any, CompileNgModuleMetadata>();
ngModule.transitiveModule.modules.forEach((localModuleMeta) => {
localModuleMeta.declaredDirectives.forEach((dirMeta) => {
moduleByDirective.set(dirMeta.type.reference, localModuleMeta);
this._compileDirectiveWrapper(dirMeta, localModuleMeta);
if (dirMeta.isComponent) {
templates.add(this._createCompiledTemplate(dirMeta, localModuleMeta));
}
});
});
ngModule.transitiveModule.modules.forEach((localModuleMeta) => {
localModuleMeta.declaredDirectives.forEach((dirMeta) => {
if (dirMeta.isComponent) {
templates.add(this._createCompiledTemplate(dirMeta, localModuleMeta));
dirMeta.entryComponents.forEach((entryComponentType) => {
templates.add(this._createCompiledHostTemplate(entryComponentType.reference));
const moduleMeta = moduleByDirective.get(entryComponentType.reference);
templates.add(
this._createCompiledHostTemplate(entryComponentType.reference, moduleMeta));
});
// TODO: what about entryComponents of entryComponents? maybe skip here and just do the
// below?
}
});
localModuleMeta.entryComponents.forEach((entryComponentType) => {
templates.add(this._createCompiledHostTemplate(entryComponentType.reference));
// TODO: what about entryComponents of entryComponents?
const moduleMeta = moduleByDirective.get(entryComponentType.reference);
templates.add(this._createCompiledHostTemplate(entryComponentType.reference, moduleMeta));
});
});
templates.forEach((template) => {
if (template.loading) {
if (isSync) {
@ -189,14 +206,19 @@ export class RuntimeCompiler implements Compiler {
this._compiledNgModuleCache.clear();
}
private _createCompiledHostTemplate(compType: Type<any>): CompiledTemplate {
private _createCompiledHostTemplate(compType: Type<any>, ngModule: CompileNgModuleMetadata):
CompiledTemplate {
if (!ngModule) {
throw new Error(
`Component ${stringify(compType)} is not part of any NgModule or the module has not been imported into your module.`);
}
var compiledTemplate = this._compiledHostTemplateCache.get(compType);
if (!compiledTemplate) {
var compMeta = this._metadataResolver.getDirectiveMetadata(compType);
assertComponent(compMeta);
var hostMeta = createHostComponentMeta(compMeta);
compiledTemplate = new CompiledTemplate(
true, compMeta.selector, compMeta.type, [compMeta], [], [],
true, compMeta.selector, compMeta.type, ngModule, [compMeta],
this._templateNormalizer.normalizeDirective(hostMeta));
this._compiledHostTemplateCache.set(compType, compiledTemplate);
}
@ -209,8 +231,7 @@ export class RuntimeCompiler implements Compiler {
if (!compiledTemplate) {
assertComponent(compMeta);
compiledTemplate = new CompiledTemplate(
false, compMeta.selector, compMeta.type, ngModule.transitiveModule.directives,
ngModule.transitiveModule.pipes, ngModule.schemas,
false, compMeta.selector, compMeta.type, ngModule, ngModule.transitiveModule.directives,
this._templateNormalizer.normalizeDirective(compMeta));
this._compiledTemplateCache.set(compMeta.type.reference, compiledTemplate);
}
@ -221,13 +242,8 @@ export class RuntimeCompiler implements Compiler {
const compiledTemplate = isHost ? this._compiledHostTemplateCache.get(compType) :
this._compiledTemplateCache.get(compType);
if (!compiledTemplate) {
if (isHost) {
throw new Error(
`Illegal state: Compiled view for component ${stringify(compType)} does not exist!`);
} else {
throw new Error(
`Component ${stringify(compType)} is not part of any NgModule or the module has not been imported into your module.`);
}
throw new Error(
`Illegal state: Compiled view for component ${stringify(compType)} does not exist!`);
}
return compiledTemplate;
}
@ -241,6 +257,30 @@ export class RuntimeCompiler implements Compiler {
return compiledTemplate;
}
private _assertDirectiveWrapper(dirType: any): Type<any> {
const dirWrapper = this._compiledDirectiveWrapperCache.get(dirType);
if (!dirWrapper) {
throw new Error(
`Illegal state: Directive wrapper for ${stringify(dirType)} has not been compiled!`);
}
return dirWrapper;
}
private _compileDirectiveWrapper(
dirMeta: CompileDirectiveMetadata, moduleMeta: CompileNgModuleMetadata): void {
const compileResult = this._directiveWrapperCompiler.compile(dirMeta);
const statements = compileResult.statements;
let directiveWrapperClass: any;
if (!this._compilerConfig.useJit) {
directiveWrapperClass = interpretStatements(statements, compileResult.dirWrapperClassVar);
} else {
directiveWrapperClass = jitStatements(
`/${moduleMeta.type.name}/${dirMeta.type.name}/wrapper.ngfactory.js`, statements,
compileResult.dirWrapperClassVar);
}
this._compiledDirectiveWrapperCache.set(dirMeta.type.reference, directiveWrapperClass);
}
private _compileTemplate(template: CompiledTemplate) {
if (template.isCompiled) {
return;
@ -275,6 +315,9 @@ export class RuntimeCompiler implements Compiler {
depTemplate = this._assertComponentLoaded(cfd.comp.reference, true);
cfd.placeholder.reference = depTemplate.proxyComponentFactory;
cfd.placeholder.name = `compFactory_${cfd.comp.name}`;
} else if (dep instanceof DirectiveWrapperDependency) {
let dwd = <DirectiveWrapperDependency>dep;
dwd.placeholder.reference = this._assertDirectiveWrapper(dwd.dir.reference);
}
});
const statements =
@ -286,8 +329,8 @@ export class RuntimeCompiler implements Compiler {
factory = interpretStatements(statements, compileResult.viewFactoryVar);
} else {
factory = jitStatements(
`${template.compType.name}${template.isHost?'_Host':''}.ngfactory.js`, statements,
compileResult.viewFactoryVar);
`/${template.ngModule.type.name}/${template.compType.name}/${template.isHost?'host':'component'}.ngfactory.js`,
statements, compileResult.viewFactoryVar);
}
template.compiled(factory);
}
@ -310,7 +353,7 @@ export class RuntimeCompiler implements Compiler {
if (!this._compilerConfig.useJit) {
return interpretStatements(result.statements, result.stylesVar);
} else {
return jitStatements(`${result.meta.moduleUrl}.css.js`, result.statements, result.stylesVar);
return jitStatements(`/${result.meta.moduleUrl}.css.js`, result.statements, result.stylesVar);
}
}
}
@ -325,13 +368,17 @@ class CompiledTemplate {
isCompiledWithDeps = false;
viewComponentTypes: Type<any>[] = [];
viewDirectives: CompileDirectiveMetadata[] = [];
viewPipes: CompilePipeMetadata[];
schemas: SchemaMetadata[];
constructor(
public isHost: boolean, selector: string, public compType: CompileIdentifierMetadata,
viewDirectivesAndComponents: CompileDirectiveMetadata[],
public viewPipes: CompilePipeMetadata[], public schemas: SchemaMetadata[],
public ngModule: CompileNgModuleMetadata,
viewDirectiveAndComponents: CompileDirectiveMetadata[],
_normalizeResult: SyncAsyncResult<CompileDirectiveMetadata>) {
viewDirectivesAndComponents.forEach((dirMeta) => {
this.viewPipes = ngModule.transitiveModule.pipes;
this.schemas = ngModule.schemas;
viewDirectiveAndComponents.forEach((dirMeta) => {
if (dirMeta.isComponent) {
this.viewComponentTypes.push(dirMeta.type.reference);
} else {

View File

@ -11,7 +11,7 @@ import {Inject, Injectable, OpaqueToken, Optional, SchemaMetadata, SecurityConte
import {CompileDirectiveMetadata, CompilePipeMetadata, CompileTemplateMetadata, CompileTokenMetadata, removeIdentifierDuplicates} from '../compile_metadata';
import {AST, ASTWithSource, BindingPipe, EmptyExpr, Interpolation, ParserError, RecursiveAstVisitor, TemplateBinding} from '../expression_parser/ast';
import {Parser} from '../expression_parser/parser';
import {isPresent, isString} from '../facade/lang';
import {isPresent} from '../facade/lang';
import {I18NHtmlParser} from '../i18n/i18n_html_parser';
import {Identifiers, identifierToken, resolveIdentifierToken} from '../identifiers';
import * as html from '../ml_parser/ast';
@ -20,7 +20,7 @@ import {expandNodes} from '../ml_parser/icu_ast_expander';
import {InterpolationConfig} from '../ml_parser/interpolation_config';
import {mergeNsAndName, splitNsName} from '../ml_parser/tags';
import {ParseError, ParseErrorLevel, ParseSourceSpan} from '../parse_util';
import {Console, MAX_INTERPOLATION_VALUES} from '../private_import_core';
import {Console, view_utils} from '../private_import_core';
import {ProviderElementContext, ProviderViewContext} from '../provider_analyzer';
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {CssSelector, SelectorMatcher} from '../selector';
@ -246,8 +246,9 @@ class TemplateParseVisitor implements html.Visitor {
if (ast) this._reportParserErrors(ast.errors, sourceSpan);
this._checkPipes(ast, sourceSpan);
if (isPresent(ast) &&
(<Interpolation>ast.ast).expressions.length > MAX_INTERPOLATION_VALUES) {
throw new Error(`Only support at most ${MAX_INTERPOLATION_VALUES} interpolation values!`);
(<Interpolation>ast.ast).expressions.length > view_utils.MAX_INTERPOLATION_VALUES) {
throw new Error(
`Only support at most ${view_utils.MAX_INTERPOLATION_VALUES} interpolation values!`);
}
return ast;
} catch (e) {
@ -844,7 +845,7 @@ class TemplateParseVisitor implements html.Visitor {
if (hostProps) {
Object.keys(hostProps).forEach(propName => {
const expression = hostProps[propName];
if (isString(expression)) {
if (typeof expression === 'string') {
const exprAst = this._parseBinding(expression, sourceSpan);
targetPropertyAsts.push(
this._createElementPropertyAst(elementName, propName, exprAst, sourceSpan));
@ -863,7 +864,7 @@ class TemplateParseVisitor implements html.Visitor {
if (hostListeners) {
Object.keys(hostListeners).forEach(propName => {
const expression = hostListeners[propName];
if (isString(expression)) {
if (typeof expression === 'string') {
this._parseEventOrAnimationEvent(propName, expression, sourceSpan, [], targetEventAsts);
} else {
this._reportError(

View File

@ -7,12 +7,12 @@
*/
import {CompileTokenMetadata} from './compile_metadata';
import {isArray, isBlank, isPresent, isPrimitive, isStrictStringMap} from './facade/lang';
import {isBlank, isPresent, isPrimitive, isStrictStringMap} from './facade/lang';
import * as o from './output/output_ast';
export const MODULE_SUFFIX = '';
var CAMEL_CASE_REGEXP = /([A-Z])/g;
const CAMEL_CASE_REGEXP = /([A-Z])/g;
export function camelCaseToDashCase(input: string): string {
return input.replace(CAMEL_CASE_REGEXP, (...m: any[]) => '-' + m[1].toLowerCase());
@ -37,15 +37,19 @@ export function sanitizeIdentifier(name: string): string {
}
export function visitValue(value: any, visitor: ValueVisitor, context: any): any {
if (isArray(value)) {
if (Array.isArray(value)) {
return visitor.visitArray(<any[]>value, context);
} else if (isStrictStringMap(value)) {
return visitor.visitStringMap(<{[key: string]: any}>value, context);
} else if (isBlank(value) || isPrimitive(value)) {
return visitor.visitPrimitive(value, context);
} else {
return visitor.visitOther(value, context);
}
if (isStrictStringMap(value)) {
return visitor.visitStringMap(<{[key: string]: any}>value, context);
}
if (isBlank(value) || isPrimitive(value)) {
return visitor.visitPrimitive(value, context);
}
return visitor.visitOther(value, context);
}
export interface ValueVisitor {

View File

@ -8,6 +8,7 @@
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileIdentifierMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata} from '../compile_metadata';
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
import {ListWrapper, MapWrapper} from '../facade/collection';
import {isPresent} from '../facade/lang';
import {Identifiers, identifierToken, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
@ -19,7 +20,8 @@ import {createDiTokenExpression} from '../util';
import {CompileMethod} from './compile_method';
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
import {CompileView} from './compile_view';
import {InjectMethodVars} from './constants';
import {InjectMethodVars, ViewProperties} from './constants';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewFactoryDependency} from './deps';
import {getPropertyInView, injectFromViewParentInjector} from './util';
export class CompileNode {
@ -34,7 +36,7 @@ export class CompileNode {
export class CompileElement extends CompileNode {
static createNull(): CompileElement {
return new CompileElement(null, null, null, null, null, null, [], [], false, false, []);
return new CompileElement(null, null, null, null, null, null, [], [], false, false, [], []);
}
private _compViewExpr: o.Expression = null;
@ -42,6 +44,7 @@ export class CompileElement extends CompileNode {
public elementRef: o.Expression;
public injector: o.Expression;
public instances = new Map<any, o.Expression>();
public directiveWrapperInstance = new Map<any, o.Expression>();
private _resolvedProviders: Map<any, ProviderAst>;
private _queryCount = 0;
@ -57,7 +60,9 @@ export class CompileElement extends CompileNode {
sourceAst: TemplateAst, public component: CompileDirectiveMetadata,
private _directives: CompileDirectiveMetadata[],
private _resolvedProvidersArray: ProviderAst[], public hasViewContainer: boolean,
public hasEmbeddedView: boolean, references: ReferenceAst[]) {
public hasEmbeddedView: boolean, references: ReferenceAst[],
private _targetDependencies:
Array<ViewFactoryDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {
super(parent, view, nodeIndex, renderNode, sourceAst);
this.referenceTokens = {};
references.forEach(ref => this.referenceTokens[ref.name] = ref.value);
@ -72,6 +77,9 @@ export class CompileElement extends CompileNode {
if (this.hasViewContainer || this.hasEmbeddedView || isPresent(this.component)) {
this._createAppElement();
}
if (this.component) {
this._createComponentFactoryResolver();
}
}
private _createAppElement() {
@ -92,7 +100,13 @@ export class CompileElement extends CompileNode {
this.instances.set(resolveIdentifierToken(Identifiers.AppElement).reference, this.appElement);
}
public createComponentFactoryResolver(entryComponents: CompileIdentifierMetadata[]) {
private _createComponentFactoryResolver() {
let entryComponents =
this.component.entryComponents.map((entryComponent: CompileIdentifierMetadata) => {
var id = new CompileIdentifierMetadata({name: entryComponent.name});
this._targetDependencies.push(new ComponentFactoryDependency(entryComponent, id));
return id;
});
if (!entryComponents || entryComponents.length === 0) {
return;
}
@ -155,6 +169,8 @@ export class CompileElement extends CompileNode {
// create all the provider instances, some in the view constructor,
// some as getters. We rely on the fact that they are already sorted topologically.
MapWrapper.values(this._resolvedProviders).forEach((resolvedProvider) => {
const isDirectiveWrapper = resolvedProvider.providerType === ProviderAstType.Component ||
resolvedProvider.providerType === ProviderAstType.Directive;
var providerValueExpressions = resolvedProvider.providers.map((provider) => {
if (isPresent(provider.useExisting)) {
return this._getDependency(
@ -167,8 +183,17 @@ export class CompileElement extends CompileNode {
} else if (isPresent(provider.useClass)) {
var deps = provider.deps || provider.useClass.diDeps;
var depsExpr = deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep));
return o.importExpr(provider.useClass)
.instantiate(depsExpr, o.importType(provider.useClass));
if (isDirectiveWrapper) {
const directiveWrapperIdentifier = new CompileIdentifierMetadata(
{name: DirectiveWrapperCompiler.dirWrapperClassName(provider.useClass)});
this._targetDependencies.push(
new DirectiveWrapperDependency(provider.useClass, directiveWrapperIdentifier));
return o.importExpr(directiveWrapperIdentifier)
.instantiate(depsExpr, o.importType(directiveWrapperIdentifier));
} else {
return o.importExpr(provider.useClass)
.instantiate(depsExpr, o.importType(provider.useClass));
}
} else {
return convertValueToOutputAst(provider.useValue);
}
@ -177,7 +202,12 @@ export class CompileElement extends CompileNode {
var instance = createProviderProperty(
propName, resolvedProvider, providerValueExpressions, resolvedProvider.multiProvider,
resolvedProvider.eager, this);
this.instances.set(resolvedProvider.token.reference, instance);
if (isDirectiveWrapper) {
this.directiveWrapperInstance.set(resolvedProvider.token.reference, instance);
this.instances.set(resolvedProvider.token.reference, instance.prop('context'));
} else {
this.instances.set(resolvedProvider.token.reference, instance);
}
});
for (var i = 0; i < this._directives.length; i++) {

View File

@ -0,0 +1,24 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {CompileIdentifierMetadata} from '../compile_metadata';
export class ViewFactoryDependency {
constructor(
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
}
export class ComponentFactoryDependency {
constructor(
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
}
export class DirectiveWrapperDependency {
constructor(
public dir: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
}

View File

@ -8,7 +8,7 @@
import * as cdAst from '../expression_parser/ast';
import {isArray, isBlank, isPresent} from '../facade/lang';
import {isBlank, isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers';
import * as o from '../output/output_ast';
@ -471,7 +471,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
}
function flattenStatements(arg: any, output: o.Statement[]) {
if (isArray(arg)) {
if (Array.isArray(arg)) {
(<any[]>arg).forEach((entry) => flattenStatements(entry, output));
} else {
output.push(arg);

View File

@ -20,27 +20,6 @@ import {DetectChangesVars} from './constants';
var STATE_IS_NEVER_CHECKED = o.THIS_EXPR.prop('numberOfChecks').identical(new o.LiteralExpr(0));
var NOT_THROW_ON_CHANGES = o.not(DetectChangesVars.throwOnChange);
export function bindDirectiveDetectChangesLifecycleCallbacks(
directiveAst: DirectiveAst, directiveInstance: o.Expression, compileElement: CompileElement) {
var view = compileElement.view;
var detectChangesInInputsMethod = view.detectChangesInInputsMethod;
var lifecycleHooks = directiveAst.directive.type.lifecycleHooks;
if (lifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1 && directiveAst.inputs.length > 0) {
detectChangesInInputsMethod.addStmt(new o.IfStmt(
DetectChangesVars.changes.notIdentical(o.NULL_EXPR),
[directiveInstance.callMethod('ngOnChanges', [DetectChangesVars.changes]).toStmt()]));
}
if (lifecycleHooks.indexOf(LifecycleHooks.OnInit) !== -1) {
detectChangesInInputsMethod.addStmt(new o.IfStmt(
STATE_IS_NEVER_CHECKED.and(NOT_THROW_ON_CHANGES),
[directiveInstance.callMethod('ngOnInit', []).toStmt()]));
}
if (lifecycleHooks.indexOf(LifecycleHooks.DoCheck) !== -1) {
detectChangesInInputsMethod.addStmt(new o.IfStmt(
NOT_THROW_ON_CHANGES, [directiveInstance.callMethod('ngDoCheck', []).toStmt()]));
}
}
export function bindDirectiveAfterContentLifecycleCallbacks(
directiveMeta: CompileDirectiveMetadata, directiveInstance: o.Expression,
compileElement: CompileElement) {

View File

@ -32,15 +32,18 @@ function createCurrValueExpr(exprIndex: number): o.ReadVarExpr {
return o.variable(`currVal_${exprIndex}`); // fix syntax highlighting: `
}
function bind(
view: CompileView, currValExpr: o.ReadVarExpr, fieldExpr: o.ReadPropExpr,
parsedExpression: cdAst.AST, context: o.Expression, actions: o.Statement[],
method: CompileMethod, bindingIndex: number) {
class EvalResult {
constructor(public forceUpdate: o.Expression) {}
}
function evalCdAst(
view: CompileView, currValExpr: o.ReadVarExpr, parsedExpression: cdAst.AST,
context: o.Expression, method: CompileMethod, bindingIndex: number): EvalResult {
var checkExpression = convertCdExpressionToIr(
view, context, parsedExpression, DetectChangesVars.valUnwrapper, bindingIndex);
if (!checkExpression.expression) {
// e.g. an empty expression was given
return;
return null;
}
if (checkExpression.temporaryCount) {
@ -49,24 +52,39 @@ function bind(
}
}
// private is fine here as no child view will reference the cached value...
view.fields.push(new o.ClassField(fieldExpr.name, null, [o.StmtModifier.Private]));
view.createMethod.addStmt(o.THIS_EXPR.prop(fieldExpr.name)
.set(o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED)))
.toStmt());
if (checkExpression.needsValueUnwrapper) {
var initValueUnwrapperStmt = DetectChangesVars.valUnwrapper.callMethod('reset', []).toStmt();
method.addStmt(initValueUnwrapperStmt);
}
method.addStmt(
currValExpr.set(checkExpression.expression).toDeclStmt(null, [o.StmtModifier.Final]));
if (checkExpression.needsValueUnwrapper) {
return new EvalResult(DetectChangesVars.valUnwrapper.prop('hasWrappedValue'));
} else {
return new EvalResult(null);
}
}
function bind(
view: CompileView, currValExpr: o.ReadVarExpr, fieldExpr: o.ReadPropExpr,
parsedExpression: cdAst.AST, context: o.Expression, actions: o.Statement[],
method: CompileMethod, bindingIndex: number) {
const evalResult = evalCdAst(view, currValExpr, parsedExpression, context, method, bindingIndex);
if (!evalResult) {
return;
}
// private is fine here as no child view will reference the cached value...
view.fields.push(new o.ClassField(fieldExpr.name, null, [o.StmtModifier.Private]));
view.createMethod.addStmt(o.THIS_EXPR.prop(fieldExpr.name)
.set(o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED)))
.toStmt());
var condition: o.Expression = o.importExpr(resolveIdentifier(Identifiers.checkBinding)).callFn([
DetectChangesVars.throwOnChange, fieldExpr, currValExpr
]);
if (checkExpression.needsValueUnwrapper) {
condition = DetectChangesVars.valUnwrapper.prop('hasWrappedValue').or(condition);
if (evalResult.forceUpdate) {
condition = evalResult.forceUpdate.or(condition);
}
method.addStmt(new o.IfStmt(
condition,
@ -237,82 +255,48 @@ export function bindDirectiveHostProps(
}
export function bindDirectiveInputs(
directiveAst: DirectiveAst, directiveInstance: o.Expression, compileElement: CompileElement) {
if (directiveAst.inputs.length === 0) {
return;
}
directiveAst: DirectiveAst, directiveWrapperInstance: o.Expression,
compileElement: CompileElement) {
var view = compileElement.view;
var detectChangesInInputsMethod = view.detectChangesInInputsMethod;
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
var lifecycleHooks = directiveAst.directive.type.lifecycleHooks;
var calcChangesMap = lifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1;
var isOnPushComp = directiveAst.directive.isComponent &&
!isDefaultChangeDetectionStrategy(directiveAst.directive.changeDetection);
if (calcChangesMap) {
detectChangesInInputsMethod.addStmt(DetectChangesVars.changes.set(o.NULL_EXPR).toStmt());
}
if (isOnPushComp) {
detectChangesInInputsMethod.addStmt(DetectChangesVars.changed.set(o.literal(false)).toStmt());
}
directiveAst.inputs.forEach((input) => {
var bindingIndex = view.bindings.length;
view.bindings.push(new CompileBinding(compileElement, input));
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, input);
var fieldExpr = createBindFieldExpr(bindingIndex);
var currValExpr = createCurrValueExpr(bindingIndex);
var statements: o.Statement[] =
[directiveInstance.prop(input.directiveName).set(currValExpr).toStmt()];
if (calcChangesMap) {
statements.push(new o.IfStmt(
DetectChangesVars.changes.identical(o.NULL_EXPR),
[DetectChangesVars.changes
.set(o.literalMap(
[], new o.MapType(o.importType(resolveIdentifier(Identifiers.SimpleChange)))))
.toStmt()]));
statements.push(DetectChangesVars.changes.key(o.literal(input.directiveName))
.set(o.importExpr(resolveIdentifier(Identifiers.SimpleChange))
.instantiate([fieldExpr, currValExpr]))
.toStmt());
const evalResult = evalCdAst(
view, currValExpr, input.value, view.componentContext, detectChangesInInputsMethod,
bindingIndex);
if (!evalResult) {
return;
}
if (isOnPushComp) {
statements.push(DetectChangesVars.changed.set(o.literal(true)).toStmt());
}
if (view.genConfig.logBindingUpdate) {
statements.push(
logBindingUpdateStmt(compileElement.renderNode, input.directiveName, currValExpr));
}
bind(
view, currValExpr, fieldExpr, input.value, view.componentContext, statements,
detectChangesInInputsMethod, bindingIndex);
detectChangesInInputsMethod.addStmt(directiveWrapperInstance
.callMethod(
`check_${input.directiveName}`,
[
currValExpr, DetectChangesVars.throwOnChange,
evalResult.forceUpdate || o.literal(false)
])
.toStmt());
});
if (isOnPushComp) {
detectChangesInInputsMethod.addStmt(new o.IfStmt(DetectChangesVars.changed, [
compileElement.appElement.prop('componentView').callMethod('markAsCheckOnce', []).toStmt()
]));
}
var isOnPushComp = directiveAst.directive.isComponent &&
!isDefaultChangeDetectionStrategy(directiveAst.directive.changeDetection);
let directiveDetectChangesExpr = directiveWrapperInstance.callMethod(
'detectChangesInternal',
[o.THIS_EXPR, compileElement.renderNode, DetectChangesVars.throwOnChange]);
const directiveDetectChangesStmt = isOnPushComp ?
new o.IfStmt(directiveDetectChangesExpr, [compileElement.appElement.prop('componentView')
.callMethod('markAsCheckOnce', [])
.toStmt()]) :
directiveDetectChangesExpr.toStmt();
detectChangesInInputsMethod.addStmt(directiveDetectChangesStmt);
}
function logBindingUpdateStmt(
renderNode: o.Expression, propName: string, value: o.Expression): o.Statement {
const tryStmt =
o.THIS_EXPR.prop('renderer')
.callMethod(
'setBindingDebugInfo',
[
renderNode, o.literal(`ng-reflect-${camelCaseToDashCase(propName)}`),
value.isBlank().conditional(o.NULL_EXPR, value.callMethod('toString', []))
])
.toStmt();
const catchStmt = o.THIS_EXPR.prop('renderer')
.callMethod(
'setBindingDebugInfo',
[
renderNode, o.literal(`ng-reflect-${camelCaseToDashCase(propName)}`),
o.literal('[ERROR] Exception while trying to serialize the value')
])
.toStmt();
return new o.TryCatchStmt([tryStmt], [catchStmt]);
return o.importExpr(resolveIdentifier(Identifiers.setBindingDebugInfo))
.callFn([o.THIS_EXPR.prop('renderer'), renderNode, o.literal(propName), value])
.toStmt();
}

View File

@ -7,7 +7,7 @@
*/
import {CompileDirectiveMetadata, CompileTokenMetadata} from '../compile_metadata';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileTokenMetadata} from '../compile_metadata';
import {isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers';
import * as o from '../output/output_ast';
@ -30,15 +30,28 @@ export function getPropertyInView(
throw new Error(
`Internal error: Could not calculate a property in a parent view: ${property}`);
}
if (property instanceof o.ReadPropExpr) {
let readPropExpr: o.ReadPropExpr = property;
return property.visitExpression(new _ReplaceViewTransformer(viewProp, definedView), null);
}
}
class _ReplaceViewTransformer extends o.ExpressionTransformer {
constructor(private _viewExpr: o.Expression, private _view: CompileView) { super(); }
private _isThis(expr: o.Expression): boolean {
return expr instanceof o.ReadVarExpr && expr.builtin === o.BuiltinVar.This;
}
visitReadVarExpr(ast: o.ReadVarExpr, context: any): any {
return this._isThis(ast) ? this._viewExpr : ast;
}
visitReadPropExpr(ast: o.ReadPropExpr, context: any): any {
if (this._isThis(ast.receiver)) {
// Note: Don't cast for members of the AppView base class...
if (definedView.fields.some((field) => field.name == readPropExpr.name) ||
definedView.getters.some((field) => field.name == readPropExpr.name)) {
viewProp = viewProp.cast(definedView.classType);
if (this._view.fields.some((field) => field.name == ast.name) ||
this._view.getters.some((field) => field.name == ast.name)) {
return this._viewExpr.cast(this._view.classType).prop(ast.name);
}
}
return o.replaceVarInExpression(o.THIS_EXPR.name, viewProp, property);
return super.visitReadPropExpr(ast, context);
}
}

View File

@ -11,7 +11,7 @@ import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventA
import {CompileElement} from './compile_element';
import {CompileView} from './compile_view';
import {CompileEventListener, bindDirectiveOutputs, bindRenderOutputs, collectEventListeners} from './event_binder';
import {bindDirectiveAfterContentLifecycleCallbacks, bindDirectiveAfterViewLifecycleCallbacks, bindDirectiveDetectChangesLifecycleCallbacks, bindInjectableDestroyLifecycleCallbacks, bindPipeDestroyLifecycleCallbacks} from './lifecycle_binder';
import {bindDirectiveAfterContentLifecycleCallbacks, bindDirectiveAfterViewLifecycleCallbacks, bindInjectableDestroyLifecycleCallbacks, bindPipeDestroyLifecycleCallbacks} from './lifecycle_binder';
import {bindDirectiveHostProps, bindDirectiveInputs, bindRenderInputs, bindRenderText} from './property_binder';
export function bindView(view: CompileView, parsedTemplate: TemplateAst[]): void {
@ -48,8 +48,9 @@ class ViewBinderVisitor implements TemplateAstVisitor {
bindRenderOutputs(eventListeners);
ast.directives.forEach((directiveAst) => {
var directiveInstance = compileElement.instances.get(directiveAst.directive.type.reference);
bindDirectiveInputs(directiveAst, directiveInstance, compileElement);
bindDirectiveDetectChangesLifecycleCallbacks(directiveAst, directiveInstance, compileElement);
var directiveWrapperInstance =
compileElement.directiveWrapperInstance.get(directiveAst.directive.type.reference);
bindDirectiveInputs(directiveAst, directiveWrapperInstance, compileElement);
bindDirectiveHostProps(directiveAst, directiveInstance, compileElement, eventListeners);
bindDirectiveOutputs(directiveAst, directiveInstance, eventListeners);
@ -76,8 +77,10 @@ class ViewBinderVisitor implements TemplateAstVisitor {
var eventListeners = collectEventListeners(ast.outputs, ast.directives, compileElement);
ast.directives.forEach((directiveAst) => {
var directiveInstance = compileElement.instances.get(directiveAst.directive.type.reference);
bindDirectiveInputs(directiveAst, directiveInstance, compileElement);
bindDirectiveDetectChangesLifecycleCallbacks(directiveAst, directiveInstance, compileElement);
var directiveWrapperInstance =
compileElement.directiveWrapperInstance.get(directiveAst.directive.type.reference);
bindDirectiveInputs(directiveAst, directiveWrapperInstance, compileElement);
bindDirectiveOutputs(directiveAst, directiveInstance, eventListeners);
bindDirectiveAfterContentLifecycleCallbacks(
directiveAst.directive, directiveInstance, compileElement);

View File

@ -20,6 +20,7 @@ import {createDiTokenExpression} from '../util';
import {CompileElement, CompileNode} from './compile_element';
import {CompileView} from './compile_view';
import {ChangeDetectorStatusEnum, DetectChangesVars, InjectMethodVars, ViewConstructorVars, ViewEncapsulationEnum, ViewProperties, ViewTypeEnum} from './constants';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewFactoryDependency} from './deps';
import {createFlatArray, getViewFactoryName} from './util';
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
@ -30,20 +31,11 @@ const NG_CONTAINER_TAG = 'ng-container';
var parentRenderNodeVar = o.variable('parentRenderNode');
var rootSelectorVar = o.variable('rootSelector');
export class ViewFactoryDependency {
constructor(
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
}
export class ComponentFactoryDependency {
constructor(
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
}
export function buildView(
view: CompileView, template: TemplateAst[],
targetDependencies: Array<ViewFactoryDependency|ComponentFactoryDependency>): number {
targetDependencies:
Array<ViewFactoryDependency|ComponentFactoryDependency|DirectiveWrapperDependency>):
number {
var builderVisitor = new ViewBuilderVisitor(view, targetDependencies);
templateVisitAll(
builderVisitor, template,
@ -66,7 +58,8 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
constructor(
public view: CompileView,
public targetDependencies: Array<ViewFactoryDependency|ComponentFactoryDependency>) {}
public targetDependencies:
Array<ViewFactoryDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {}
private _isRootNode(parent: CompileElement): boolean { return parent.view !== this.view; }
@ -204,7 +197,7 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
}
var compileElement = new CompileElement(
parent, this.view, nodeIndex, renderNode, ast, component, directives, ast.providers,
ast.hasViewContainer, false, ast.references);
ast.hasViewContainer, false, ast.references, this.targetDependencies);
this.view.nodes.push(compileElement);
var compViewExpr: o.ReadVarExpr = null;
if (isPresent(component)) {
@ -212,13 +205,6 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
new CompileIdentifierMetadata({name: getViewFactoryName(component, 0)});
this.targetDependencies.push(
new ViewFactoryDependency(component.type, nestedComponentIdentifier));
let entryComponentIdentifiers =
component.entryComponents.map((entryComponent: CompileIdentifierMetadata) => {
var id = new CompileIdentifierMetadata({name: entryComponent.name});
this.targetDependencies.push(new ComponentFactoryDependency(entryComponent, id));
return id;
});
compileElement.createComponentFactoryResolver(entryComponentIdentifiers);
compViewExpr = o.variable(`compView_${nodeIndex}`); // fix highlighting: `
compileElement.setComponentView(compViewExpr);
@ -273,7 +259,7 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
var directives = ast.directives.map(directiveAst => directiveAst.directive);
var compileElement = new CompileElement(
parent, this.view, nodeIndex, renderNode, ast, null, directives, ast.providers,
ast.hasViewContainer, true, ast.references);
ast.hasViewContainer, true, ast.references, this.targetDependencies);
this.view.nodes.push(compileElement);
this.nestedViewCount++;

View File

@ -16,15 +16,17 @@ import {TemplateAst} from '../template_parser/template_ast';
import {CompileElement} from './compile_element';
import {CompileView} from './compile_view';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewFactoryDependency} from './deps';
import {bindView} from './view_binder';
import {ComponentFactoryDependency, ViewFactoryDependency, buildView, finishView} from './view_builder';
import {buildView, finishView} from './view_builder';
export {ComponentFactoryDependency, ViewFactoryDependency} from './view_builder';
export {ComponentFactoryDependency, DirectiveWrapperDependency, ViewFactoryDependency} from './deps';
export class ViewCompileResult {
constructor(
public statements: o.Statement[], public viewFactoryVar: string,
public dependencies: Array<ViewFactoryDependency|ComponentFactoryDependency>) {}
public dependencies:
Array<ViewFactoryDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {}
}
@Injectable()
@ -35,7 +37,8 @@ export class ViewCompiler {
component: CompileDirectiveMetadata, template: TemplateAst[], styles: o.Expression,
pipes: CompilePipeMetadata[],
compiledAnimations: AnimationEntryCompileResult[]): ViewCompileResult {
const dependencies: Array<ViewFactoryDependency|ComponentFactoryDependency> = [];
const dependencies:
Array<ViewFactoryDependency|ComponentFactoryDependency|DirectiveWrapperDependency> = [];
const view = new CompileView(
component, this._genConfig, pipes, styles, compiledAnimations, 0,
CompileElement.createNull(), []);

View File

@ -7,7 +7,6 @@
*/
import {AST, AstVisitor, Binary, BindingPipe, Chain, Conditional, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead} from '../../src/expression_parser/ast';
import {isString} from '../../src/facade/lang';
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../src/ml_parser/interpolation_config';
class Unparser implements AstVisitor {
@ -133,7 +132,7 @@ class Unparser implements AstVisitor {
}
visitLiteralPrimitive(ast: LiteralPrimitive, context: any) {
if (isString(ast.value)) {
if (typeof ast.value === 'string') {
this._expression += `"${ast.value.replace( Unparser._quoteRegExp, '\"')}"`;
} else {
this._expression += `${ast.value}`;

View File

@ -0,0 +1,65 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {CompileIdentifierMetadata} from '../../src/compile_metadata';
import * as o from '../../src/output/output_ast';
import {CompileView} from '../../src/view_compiler/compile_view';
import {getPropertyInView} from '../../src/view_compiler/util';
export function main() {
describe('getPropertyInView', () => {
it('should return the expression if it is the same view', () => {
const expr = o.THIS_EXPR.prop('someProp');
const callingView = createCompileView({className: 'view'});
expect(getPropertyInView(expr, callingView, callingView)).toBe(expr);
});
it('should access an unknown property in a parent view', () => {
const expr = o.THIS_EXPR.prop('someProp');
const definedView = createCompileView({className: 'parentView'});
const callingView = createCompileView({className: 'childView', parent: definedView});
expect(getPropertyInView(expr, callingView, definedView))
.toEqual(o.THIS_EXPR.prop('parent').prop('someProp'));
});
it('should access a known property in a parent view with cast', () => {
const expr = o.THIS_EXPR.prop('someProp');
const definedView = createCompileView({className: 'parentView', fields: ['someProp']});
const callingView = createCompileView({className: 'childView', parent: definedView});
expect(getPropertyInView(expr, callingView, definedView))
.toEqual(o.THIS_EXPR.prop('parent').cast(definedView.classType).prop('someProp'));
});
it('should access a known property in a parent view with cast also for property chain expressions',
() => {
const expr = o.THIS_EXPR.prop('someProp').prop('context');
const definedView = createCompileView({className: 'parentView', fields: ['someProp']});
const callingView = createCompileView({className: 'childView', parent: definedView});
expect(getPropertyInView(expr, callingView, definedView))
.toEqual(o.THIS_EXPR.prop('parent')
.cast(definedView.classType)
.prop('someProp')
.prop('context'));
});
});
}
function createCompileView(config: {className: string, parent?: CompileView, fields?: string[]}):
CompileView {
const declarationElement: any = config.parent ? {view: config.parent} : {view: null};
const fields: o.ClassField[] = [];
if (config.fields) {
config.fields.forEach((fieldName) => { fields.push(new o.ClassField(fieldName)); });
}
return <any>{
classType: o.importType(new CompileIdentifierMetadata({name: config.className})),
fields: fields,
getters: [],
declarationElement: declarationElement
};
}

View File

@ -7,7 +7,6 @@
*/
import {isPresent, scheduleMicroTask} from '../facade/lang';
import {Math} from '../facade/math';
import {AnimationPlayer} from './animation_player';

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {isArray, isPresent, isString} from '../facade/lang';
import {isPresent} from '../facade/lang';
/**
* @experimental Animation support is experimental.
@ -328,10 +328,10 @@ export function style(
Array<string|{[key: string]: string | number}>): AnimationStyleMetadata {
var input: Array<{[key: string]: string | number}|string>;
var offset: number = null;
if (isString(tokens)) {
if (typeof tokens === 'string') {
input = [<string>tokens];
} else {
if (isArray(tokens)) {
if (Array.isArray(tokens)) {
input = <Array<{[key: string]: string | number}>>tokens;
} else {
input = [<{[key: string]: string | number}>tokens];
@ -564,8 +564,7 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe
*/
export function transition(stateChangeExpr: string, steps: AnimationMetadata | AnimationMetadata[]):
AnimationStateTransitionMetadata {
var animationData = isArray(steps) ? new AnimationSequenceMetadata(<AnimationMetadata[]>steps) :
<AnimationMetadata>steps;
var animationData = Array.isArray(steps) ? new AnimationSequenceMetadata(steps) : steps;
return new AnimationStateTransitionMetadata(stateChangeExpr, animationData);
}

View File

@ -7,7 +7,7 @@
*/
import {isListLikeIterable, iterateListLike} from '../../facade/collection';
import {getMapKey, isArray, isBlank, isPresent, looseIdentical, stringify} from '../../facade/lang';
import {isBlank, looseIdentical, stringify} from '../../facade/lang';
import {ChangeDetectorRef} from '../change_detector_ref';
import {IterableDiffer, IterableDifferFactory, TrackByFn} from './iterable_differs';
@ -168,13 +168,13 @@ export class DefaultIterableDiffer implements IterableDiffer {
var record: CollectionChangeRecord = this._itHead;
var mayBeDirty: boolean = false;
var index: number;
var item: any /** TODO #9100 */;
var itemTrackBy: any /** TODO #9100 */;
if (isArray(collection)) {
var list = collection;
var item: any;
var itemTrackBy: any;
if (Array.isArray(collection)) {
const list = collection;
this._length = collection.length;
for (index = 0; index < this._length; index++) {
for (let index = 0; index < this._length; index++) {
item = list[index];
itemTrackBy = this._trackByFn(index, item);
if (record === null || !looseIdentical(record.trackById, itemTrackBy)) {
@ -700,11 +700,10 @@ class _DuplicateMap {
map = new Map<any, _DuplicateItemRecordList>();
put(record: CollectionChangeRecord) {
// todo(vicb) handle corner cases
var key = getMapKey(record.trackById);
const key = record.trackById;
var duplicates = this.map.get(key);
if (!isPresent(duplicates)) {
let duplicates = this.map.get(key);
if (!duplicates) {
duplicates = new _DuplicateItemRecordList();
this.map.set(key, duplicates);
}
@ -719,9 +718,8 @@ class _DuplicateMap {
* have any more `a`s needs to return the last `a` not the first or second.
*/
get(trackById: any, afterIndex: number = null): CollectionChangeRecord {
var key = getMapKey(trackById);
var recordList = this.map.get(key);
const key = trackById;
const recordList = this.map.get(key);
return recordList ? recordList.get(trackById, afterIndex) : null;
}
@ -731,10 +729,8 @@ class _DuplicateMap {
* The list of duplicates also is removed from the map if it gets empty.
*/
remove(record: CollectionChangeRecord): CollectionChangeRecord {
var key = getMapKey(record.trackById);
// todo(vicb)
// assert(this.map.containsKey(key));
var recordList: _DuplicateItemRecordList = this.map.get(key);
const key = record.trackById;
const recordList: _DuplicateItemRecordList = this.map.get(key);
// Remove the list of duplicates when it gets empty
if (recordList.remove(record)) {
this.map.delete(key);

View File

@ -64,11 +64,6 @@ export var __core_private__: {
_NgModuleInjector?: ng_module_factory.NgModuleInjector<any>,
registerModuleFactory: typeof ng_module_factory_loader.registerModuleFactory,
ViewType: typeof view_type.ViewType, _ViewType?: view_type.ViewType,
MAX_INTERPOLATION_VALUES: typeof view_utils.MAX_INTERPOLATION_VALUES,
checkBinding: typeof view_utils.checkBinding,
flattenNestedViewRenderNodes: typeof view_utils.flattenNestedViewRenderNodes,
interpolate: typeof view_utils.interpolate,
ViewUtils: typeof view_utils.ViewUtils, _ViewUtils?: view_utils.ViewUtils,
ViewMetadata: typeof metadata_view.ViewMetadata, _ViewMetadata?: metadata_view.ViewMetadata,
DebugContext: typeof debug_context.DebugContext, _DebugContext?: debug_context.DebugContext,
StaticNodeDebugInfo: typeof debug_context.StaticNodeDebugInfo,
@ -84,19 +79,6 @@ export var __core_private__: {
makeDecorator: typeof decorators.makeDecorator,
DebugDomRootRenderer: typeof debug.DebugDomRootRenderer,
_DebugDomRootRenderer?: debug.DebugDomRootRenderer,
EMPTY_ARRAY: typeof view_utils.EMPTY_ARRAY,
EMPTY_MAP: typeof view_utils.EMPTY_MAP,
pureProxy1: typeof view_utils.pureProxy1,
pureProxy2: typeof view_utils.pureProxy2,
pureProxy3: typeof view_utils.pureProxy3,
pureProxy4: typeof view_utils.pureProxy4,
pureProxy5: typeof view_utils.pureProxy5,
pureProxy6: typeof view_utils.pureProxy6,
pureProxy7: typeof view_utils.pureProxy7,
pureProxy8: typeof view_utils.pureProxy8,
pureProxy9: typeof view_utils.pureProxy9,
pureProxy10: typeof view_utils.pureProxy10,
castByValue: typeof view_utils.castByValue,
Console: typeof console.Console, _Console?: console.Console,
reflector: typeof reflection.reflector,
Reflector: typeof reflection.Reflector, _Reflector?: reflection.Reflector,
@ -121,6 +103,7 @@ export var __core_private__: {
ComponentStillLoadingError: typeof ComponentStillLoadingError,
isPromise: typeof isPromise,
AnimationTransition: typeof AnimationTransition
view_utils: typeof view_utils,
} = {
isDefaultChangeDetectionStrategy: constants.isDefaultChangeDetectionStrategy,
ChangeDetectorStatus: constants.ChangeDetectorStatus,
@ -135,11 +118,7 @@ export var __core_private__: {
NgModuleInjector: ng_module_factory.NgModuleInjector,
registerModuleFactory: ng_module_factory_loader.registerModuleFactory,
ViewType: view_type.ViewType,
MAX_INTERPOLATION_VALUES: view_utils.MAX_INTERPOLATION_VALUES,
checkBinding: view_utils.checkBinding,
flattenNestedViewRenderNodes: view_utils.flattenNestedViewRenderNodes,
interpolate: view_utils.interpolate,
ViewUtils: view_utils.ViewUtils,
view_utils: view_utils,
ViewMetadata: metadata_view.ViewMetadata,
DebugContext: debug_context.DebugContext,
StaticNodeDebugInfo: debug_context.StaticNodeDebugInfo,
@ -151,19 +130,6 @@ export var __core_private__: {
ReflectionCapabilities: reflection_capabilities.ReflectionCapabilities,
makeDecorator: decorators.makeDecorator,
DebugDomRootRenderer: debug.DebugDomRootRenderer,
EMPTY_ARRAY: view_utils.EMPTY_ARRAY,
EMPTY_MAP: view_utils.EMPTY_MAP,
pureProxy1: view_utils.pureProxy1,
pureProxy2: view_utils.pureProxy2,
pureProxy3: view_utils.pureProxy3,
pureProxy4: view_utils.pureProxy4,
pureProxy5: view_utils.pureProxy5,
pureProxy6: view_utils.pureProxy6,
pureProxy7: view_utils.pureProxy7,
pureProxy8: view_utils.pureProxy8,
pureProxy9: view_utils.pureProxy9,
pureProxy10: view_utils.pureProxy10,
castByValue: view_utils.castByValue,
Console: console.Console,
reflector: reflection.reflector,
Reflector: reflection.Reflector,

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {isFunction, stringify} from '../facade/lang';
import {stringify} from '../facade/lang';
import {Type} from '../type';
@ -51,7 +51,7 @@ export function forwardRef(forwardRefFn: ForwardRefFn): Type<any> {
* @experimental
*/
export function resolveForwardRef(type: any): any {
if (isFunction(type) && type.hasOwnProperty('__forward_ref__') &&
if (typeof type === 'function' && type.hasOwnProperty('__forward_ref__') &&
type.__forward_ref__ === forwardRef) {
return (<ForwardRefFn>type)();
} else {

View File

@ -7,7 +7,7 @@
*/
import {ListWrapper, MapWrapper} from '../facade/collection';
import {isArray, isBlank, isPresent} from '../facade/lang';
import {isBlank, isPresent} from '../facade/lang';
import {reflector} from '../reflection/reflection';
import {Type} from '../type';
@ -225,7 +225,7 @@ function _extractToken(
var token: any /** TODO #9100 */ = null;
var optional = false;
if (!isArray(metadata)) {
if (!Array.isArray(metadata)) {
if (metadata instanceof Inject) {
return _createDependency(metadata.token, optional, null, null, depProps);
} else {

View File

@ -7,13 +7,14 @@
*/
import {APP_ID} from '../application_tokens';
import {devModeEqual} from '../change_detection/change_detection';
import {SimpleChange, devModeEqual} from '../change_detection/change_detection';
import {UNINITIALIZED} from '../change_detection/change_detection_util';
import {Inject, Injectable} from '../di';
import {isPresent, looseIdentical} from '../facade/lang';
import {ViewEncapsulation} from '../metadata/view';
import {RenderComponentType, Renderer, RootRenderer} from '../render/api';
import {Sanitizer} from '../security';
import {AppElement} from './element';
import {ExpressionChangedAfterItHasBeenCheckedError} from './errors';
@ -352,3 +353,27 @@ export function pureProxy10<P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, R>(
return result;
};
}
export function setBindingDebugInfoForChanges(
renderer: Renderer, el: any, changes: {[key: string]: SimpleChange}) {
Object.keys(changes).forEach((propName) => {
setBindingDebugInfo(renderer, el, propName, changes[propName].currentValue);
});
}
export function setBindingDebugInfo(renderer: Renderer, el: any, propName: string, value: any) {
try {
renderer.setBindingDebugInfo(
el, `ng-reflect-${camelCaseToDashCase(propName)}`, value ? value.toString() : null);
} catch (e) {
renderer.setBindingDebugInfo(
el, `ng-reflect-${camelCaseToDashCase(propName)}`,
'[ERROR] Exception while trying to serialize the value');
}
}
const CAMEL_CASE_REGEXP = /([A-Z])/g;
function camelCaseToDashCase(input: string): string {
return input.replace(CAMEL_CASE_REGEXP, (...m: any[]) => '-' + m[1].toLowerCase());
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {global, isFunction, isPresent, stringify} from '../facade/lang';
import {global, isPresent, stringify} from '../facade/lang';
import {Type} from '../type';
import {PlatformReflectionCapabilities} from './platform_reflection_capabilities';
@ -81,7 +81,7 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
// Prefer the direct API.
if ((<any>typeOrFunc).annotations) {
let annotations = (<any>typeOrFunc).annotations;
if (isFunction(annotations) && annotations.annotations) {
if (typeof annotations === 'function' && annotations.annotations) {
annotations = annotations.annotations;
}
return annotations;
@ -104,7 +104,7 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
// Prefer the direct API.
if ((<any>typeOrFunc).propMetadata) {
let propMetadata = (<any>typeOrFunc).propMetadata;
if (isFunction(propMetadata) && propMetadata.propMetadata) {
if (typeof propMetadata === 'function' && propMetadata.propMetadata) {
propMetadata = propMetadata.propMetadata;
}
return propMetadata;

View File

@ -10,7 +10,6 @@ import {DefaultIterableDiffer, DefaultIterableDifferFactory} from '@angular/core
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
import {ListWrapper} from '../../../src/facade/collection';
import {NumberWrapper} from '../../../src/facade/lang';
import {TestIterable} from '../../change_detection/iterable';
import {iterableChangesAsString} from '../../change_detection/util';
@ -207,17 +206,15 @@ export function main() {
});
it('should ignore [NaN] != [NaN] (JS)', () => {
let l = [NumberWrapper.NaN];
let l = [NaN];
differ.check(l);
differ.check(l);
expect(differ.toString()).toEqual(iterableChangesAsString({
collection: [NumberWrapper.NaN],
previous: [NumberWrapper.NaN]
}));
expect(differ.toString())
.toEqual(iterableChangesAsString({collection: [NaN], previous: [NaN]}));
});
it('should detect [NaN] moves', () => {
let l = [NumberWrapper.NaN, NumberWrapper.NaN];
let l = [NaN, NaN];
differ.check(l);
ListWrapper.insert<any>(l, 0, 'foo');

View File

@ -17,7 +17,6 @@ import {DomRootRenderer} from '@angular/platform-browser/src/dom/dom_renderer';
import {MockSchemaRegistry} from '../../../compiler/testing/index';
import {EventEmitter} from '../../src/facade/async';
import {NumberWrapper} from '../../src/facade/lang';
export function main() {
let elSchema: MockSchemaRegistry;
@ -338,7 +337,7 @@ export function main() {
it('should support NaN', fakeAsync(() => {
var ctx = _bindSimpleValue('age', Person);
ctx.componentInstance.age = NumberWrapper.NaN;
ctx.componentInstance.age = NaN;
ctx.detectChanges(false);
expect(renderLog.log).toEqual(['someProp=NaN']);

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {getSymbolIterator, isArray, isBlank, isJsObject, isPresent} from './lang';
import {getSymbolIterator, isBlank, isJsObject, isPresent} from './lang';
// Safari doesn't implement MapIterator.next(), which is used is Traceur's polyfill of Array.from
// TODO(mlaval): remove the work around once we have a working polyfill of Array.from
@ -193,9 +193,9 @@ export class ListWrapper {
function _flattenArray(source: any[], target: any[]): any[] {
if (isPresent(source)) {
for (var i = 0; i < source.length; i++) {
var item = source[i];
if (isArray(item)) {
for (let i = 0; i < source.length; i++) {
const item = source[i];
if (Array.isArray(item)) {
_flattenArray(item, target);
} else {
target.push(item);
@ -208,14 +208,14 @@ function _flattenArray(source: any[], target: any[]): any[] {
export function isListLikeIterable(obj: any): boolean {
if (!isJsObject(obj)) return false;
return isArray(obj) ||
return Array.isArray(obj) ||
(!(obj instanceof Map) && // JS Map are iterables but return entries as [k, v]
getSymbolIterator() in obj); // JS Iterable have a Symbol.iterator prop
}
export function areIterablesEqual(a: any, b: any, comparator: Function): boolean {
var iterator1 = a[getSymbolIterator()]();
var iterator2 = b[getSymbolIterator()]();
const iterator1 = a[getSymbolIterator()]();
const iterator2 = b[getSymbolIterator()]();
while (true) {
let item1 = iterator1.next();
@ -227,13 +227,13 @@ export function areIterablesEqual(a: any, b: any, comparator: Function): boolean
}
export function iterateListLike(obj: any, fn: Function) {
if (isArray(obj)) {
if (Array.isArray(obj)) {
for (var i = 0; i < obj.length; i++) {
fn(obj[i]);
}
} else {
var iterator = obj[getSymbolIterator()]();
var item: any /** TODO #???? */;
const iterator = obj[getSymbolIterator()]();
let item: any;
while (!((item = iterator.next()).done)) {
fn(item.value);
}

View File

@ -77,6 +77,7 @@ var DATE_FORMATS = {
MM: datePartGetterFactory(digitCondition('month', 2)),
M: datePartGetterFactory(digitCondition('month', 1)),
LLLL: datePartGetterFactory(nameCondition('month', 4)),
L: datePartGetterFactory(nameCondition('month', 1)),
dd: datePartGetterFactory(digitCondition('day', 2)),
d: datePartGetterFactory(digitCondition('day', 1)),
HH: digitModifier(
@ -161,13 +162,19 @@ function hour12Modify(
}
function digitCondition(prop: string, len: number): Intl.DateTimeFormatOptions {
var result: {[k: string]: string} = {};
result[prop] = len == 2 ? '2-digit' : 'numeric';
const result: {[k: string]: string} = {};
result[prop] = len === 2 ? '2-digit' : 'numeric';
return result;
}
function nameCondition(prop: string, len: number): Intl.DateTimeFormatOptions {
var result: {[k: string]: string} = {};
result[prop] = len < 4 ? 'short' : 'long';
const result: {[k: string]: string} = {};
if (len < 4) {
result[prop] = len > 1 ? 'short' : 'narrow';
} else {
result[prop] = 'long';
}
return result;
}

View File

@ -77,37 +77,9 @@ export function isBlank(obj: any): boolean {
return obj === undefined || obj === null;
}
export function isBoolean(obj: any): boolean {
return typeof obj === 'boolean';
}
export function isNumber(obj: any): boolean {
return typeof obj === 'number';
}
export function isString(obj: any): obj is string {
return typeof obj === 'string';
}
export function isFunction(obj: any): boolean {
return typeof obj === 'function';
}
export function isType(obj: any): boolean {
return isFunction(obj);
}
export function isStringMap(obj: any): obj is Object {
return typeof obj === 'object' && obj !== null;
}
const STRING_MAP_PROTO = Object.getPrototypeOf({});
export function isStrictStringMap(obj: any): boolean {
return isStringMap(obj) && Object.getPrototypeOf(obj) === STRING_MAP_PROTO;
}
export function isArray(obj: any): boolean {
return Array.isArray(obj);
return typeof obj === 'object' && obj !== null && Object.getPrototypeOf(obj) === STRING_MAP_PROTO;
}
export function isDate(obj: any): obj is Date {
@ -137,22 +109,9 @@ export function stringify(token: any): string {
return newLineIndex === -1 ? res : res.substring(0, newLineIndex);
}
export class StringJoiner {
constructor(public parts: string[] = []) {}
add(part: string): void { this.parts.push(part); }
toString(): string { return this.parts.join(''); }
}
export class NumberWrapper {
static toFixed(n: number, fractionDigits: number): string { return n.toFixed(fractionDigits); }
static equal(a: number, b: number): boolean { return a === b; }
static parseIntAutoRadix(text: string): number {
var result: number = parseInt(text);
const result: number = parseInt(text);
if (isNaN(result)) {
throw new Error('Invalid integer literal when parsing ' + text);
}
@ -169,7 +128,7 @@ export class NumberWrapper {
return parseInt(text, radix);
}
} else {
var result: number = parseInt(text, radix);
const result = parseInt(text, radix);
if (!isNaN(result)) {
return result;
}
@ -177,21 +136,7 @@ export class NumberWrapper {
throw new Error('Invalid integer literal when parsing ' + text + ' in base ' + radix);
}
static get NaN(): number { return NaN; }
static isNumeric(value: any): boolean { return !isNaN(value - parseFloat(value)); }
static isNaN(value: any): boolean { return isNaN(value); }
static isInteger(value: any): boolean { return Number.isInteger(value); }
}
export var RegExp = _global.RegExp;
export class FunctionWrapper {
static apply(fn: Function, posArgs: any): any { return fn.apply(null, posArgs); }
static bind(fn: Function, scope: any): Function { return fn.bind(scope); }
}
// JS has NaN !== NaN
@ -199,12 +144,6 @@ export function looseIdentical(a: any, b: any): boolean {
return a === b || typeof a === 'number' && typeof b === 'number' && isNaN(a) && isNaN(b);
}
// JS considers NaN is the same as NaN for map Key (while NaN !== NaN otherwise)
// see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
export function getMapKey<T>(value: T): T {
return value;
}
export function normalizeBlank(obj: Object): any {
return isBlank(obj) ? null : obj;
}
@ -225,15 +164,6 @@ export function warn(obj: Error | Object) {
console.warn(obj);
}
// Can't be all uppercase as our transpiler would think it is a special directive...
export class Json {
static parse(s: string): Object { return _global.JSON.parse(s); }
static stringify(data: Object): string {
// Dart doesn't take 3 arguments
return _global.JSON.stringify(data, null, 2);
}
}
export function setValueOnPath(global: any, path: string, value: any) {
var parts = path.split('.');
var obj: any = global;
@ -273,30 +203,10 @@ export function getSymbolIterator(): string|symbol {
return _symbolIterator;
}
export function evalExpression(
sourceUrl: string, expr: string, declarations: string, vars: {[key: string]: any}): any {
var fnBody = `${declarations}\nreturn ${expr}\n//# sourceURL=${sourceUrl}`;
var fnArgNames: string[] = [];
var fnArgValues: any[] = [];
for (var argName in vars) {
fnArgNames.push(argName);
fnArgValues.push(vars[argName]);
}
return new Function(...fnArgNames.concat(fnBody))(...fnArgValues);
}
export function isPrimitive(obj: any): boolean {
return !isJsObject(obj);
}
export function hasConstructor(value: Object, type: any): boolean {
return value.constructor === type;
}
export function escape(s: string): string {
return _global.encodeURI(s);
}
export function escapeRegExp(s: string): string {
return s.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
}

View File

@ -1,12 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {global} from './lang';
export var Math = global.Math;
export var NaN: any /** TODO #???? */ = typeof NaN;

View File

@ -6,10 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {NumberWrapper, escapeRegExp, hasConstructor} from '../src/lang';
class MySuperclass {}
class MySubclass extends MySuperclass {}
import {NumberWrapper, escapeRegExp} from '../src/lang';
export function main() {
describe('RegExp', () => {
@ -22,13 +19,6 @@ export function main() {
});
describe('const', () => {
it('should support const expressions both in TS and Dart', () => {
const numbers = [1, 2, 3];
expect(numbers).toEqual([1, 2, 3]);
});
});
describe('Number', () => {
describe('isNumeric', () => {
it('should return true when passing correct numeric string',

View File

@ -16,6 +16,7 @@ import {NgModel} from './directives/ng_model';
import {NgModelGroup} from './directives/ng_model_group';
import {NumberValueAccessor} from './directives/number_value_accessor';
import {RadioControlValueAccessor} from './directives/radio_control_value_accessor';
import {RangeValueAccessor} from './directives/range_value_accessor';
import {FormControlDirective} from './directives/reactive_directives/form_control_directive';
import {FormControlName} from './directives/reactive_directives/form_control_name';
import {FormGroupDirective} from './directives/reactive_directives/form_group_directive';
@ -34,6 +35,7 @@ export {NgModel} from './directives/ng_model';
export {NgModelGroup} from './directives/ng_model_group';
export {NumberValueAccessor} from './directives/number_value_accessor';
export {RadioControlValueAccessor} from './directives/radio_control_value_accessor';
export {RangeValueAccessor} from './directives/range_value_accessor';
export {FormControlDirective} from './directives/reactive_directives/form_control_directive';
export {FormControlName} from './directives/reactive_directives/form_control_name';
export {FormGroupDirective} from './directives/reactive_directives/form_group_directive';
@ -44,9 +46,9 @@ export {MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValida
export const SHARED_FORM_DIRECTIVES: Type<any>[] = [
NgSelectOption, NgSelectMultipleOption, DefaultValueAccessor, NumberValueAccessor,
CheckboxControlValueAccessor, SelectControlValueAccessor, SelectMultipleControlValueAccessor,
RadioControlValueAccessor, NgControlStatus, NgControlStatusGroup, RequiredValidator,
MinLengthValidator, MaxLengthValidator, PatternValidator
RangeValueAccessor, CheckboxControlValueAccessor, SelectControlValueAccessor,
SelectMultipleControlValueAccessor, RadioControlValueAccessor, NgControlStatus,
NgControlStatusGroup, RequiredValidator, MinLengthValidator, MaxLengthValidator, PatternValidator
];
export const TEMPLATE_DRIVEN_DIRECTIVES: Type<any>[] = [NgModel, NgModelGroup, NgForm];

View File

@ -57,4 +57,12 @@ export abstract class AbstractControlDirective {
reset(value: any = undefined): void {
if (isPresent(this.control)) this.control.reset(value);
}
hasError(errorCode: string, path: string[] = null): boolean {
return isPresent(this.control) ? this.control.hasError(errorCode, path) : false;
}
getError(errorCode: string, path: string[] = null): any {
return isPresent(this.control) ? this.control.getError(errorCode, path) : null;
}
}

View File

@ -37,6 +37,9 @@ export class AbstractControlStatus {
get ngClassInvalid(): boolean {
return isPresent(this._cd.control) ? this._cd.control.invalid : false;
}
get ngClassPending(): boolean {
return isPresent(this._cd.control) ? this._cd.control.pending : false;
}
}
export const ngControlStatusHost = {
@ -45,7 +48,8 @@ export const ngControlStatusHost = {
'[class.ng-pristine]': 'ngClassPristine',
'[class.ng-dirty]': 'ngClassDirty',
'[class.ng-valid]': 'ngClassValid',
'[class.ng-invalid]': 'ngClassInvalid'
'[class.ng-invalid]': 'ngClassInvalid',
'[class.ng-pending]': 'ngClassPending'
};
/**

View File

@ -0,0 +1,57 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, ElementRef, Provider, Renderer, forwardRef} from '@angular/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
export const RANGE_VALUE_ACCESSOR: Provider = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => RangeValueAccessor),
multi: true
};
/**
* The accessor for writing a range value and listening to changes that is used by the
* {@link NgModel}, {@link FormControlDirective}, and {@link FormControlName} directives.
*
* ### Example
* ```
* <input type="range" [(ngModel)]="age" >
* ```
*/
@Directive({
selector:
'input[type=range][formControlName],input[type=range][formControl],input[type=range][ngModel]',
host: {
'(change)': 'onChange($event.target.value)',
'(input)': 'onChange($event.target.value)',
'(blur)': 'onTouched()'
},
providers: [RANGE_VALUE_ACCESSOR]
})
export class RangeValueAccessor implements ControlValueAccessor {
onChange = (_: any) => {};
onTouched = () => {};
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
writeValue(value: any): void {
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', parseFloat(value));
}
registerOnChange(fn: (_: number) => void): void {
this.onChange = (value) => { fn(value == '' ? null : parseFloat(value)); };
}
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
setDisabledState(isDisabled: boolean): void {
this._renderer.setElementProperty(this._elementRef.nativeElement, 'disabled', isDisabled);
}
}

View File

@ -9,7 +9,7 @@
import {Directive, ElementRef, Host, Input, OnDestroy, OpaqueToken, Optional, Renderer, Type, forwardRef} from '@angular/core';
import {MapWrapper} from '../facade/collection';
import {isBlank, isPresent, isPrimitive, isString, looseIdentical} from '../facade/lang';
import {isBlank, isPresent, isPrimitive, looseIdentical} from '../facade/lang';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
@ -21,7 +21,7 @@ export const SELECT_MULTIPLE_VALUE_ACCESSOR = {
function _buildValueString(id: string, value: any): string {
if (isBlank(id)) return `${value}`;
if (isString(value)) value = `'${value}'`;
if (typeof value === 'string') value = `'${value}'`;
if (!isPrimitive(value)) value = 'Object';
return `${id}: ${value}`.slice(0, 50);
}

View File

@ -6,9 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ListWrapper} from '../facade/collection';
import {hasConstructor, isBlank, isPresent, looseIdentical} from '../facade/lang';
import {isBlank, isPresent, looseIdentical} from '../facade/lang';
import {FormArray, FormControl, FormGroup} from '../model';
import {Validators} from '../validators';
@ -22,6 +20,7 @@ import {NgControl} from './ng_control';
import {normalizeAsyncValidator, normalizeValidator} from './normalize_validator';
import {NumberValueAccessor} from './number_value_accessor';
import {RadioControlValueAccessor} from './radio_control_value_accessor';
import {RangeValueAccessor} from './range_value_accessor';
import {FormArrayName} from './reactive_directives/form_group_name';
import {SelectControlValueAccessor} from './select_control_value_accessor';
import {SelectMultipleControlValueAccessor} from './select_multiple_control_value_accessor';
@ -29,9 +28,7 @@ import {AsyncValidatorFn, Validator, ValidatorFn} from './validators';
export function controlPath(name: string, parent: ControlContainer): string[] {
var p = ListWrapper.clone(parent.path);
p.push(name);
return p;
return [...parent.path, name];
}
export function setUpControl(control: FormControl, dir: NgControl): void {
@ -127,13 +124,17 @@ export function isPropertyUpdated(changes: {[key: string]: any}, viewModel: any)
return !looseIdentical(viewModel, change.currentValue);
}
const BUILTIN_ACCESSORS = [
CheckboxControlValueAccessor,
RangeValueAccessor,
NumberValueAccessor,
SelectControlValueAccessor,
SelectMultipleControlValueAccessor,
RadioControlValueAccessor,
];
export function isBuiltInAccessor(valueAccessor: ControlValueAccessor): boolean {
return (
hasConstructor(valueAccessor, CheckboxControlValueAccessor) ||
hasConstructor(valueAccessor, NumberValueAccessor) ||
hasConstructor(valueAccessor, SelectControlValueAccessor) ||
hasConstructor(valueAccessor, SelectMultipleControlValueAccessor) ||
hasConstructor(valueAccessor, RadioControlValueAccessor));
return BUILTIN_ACCESSORS.some(a => valueAccessor.constructor === a);
}
// TODO: vsavkin remove it once https://github.com/angular/angular/issues/3011 is implemented
@ -145,24 +146,24 @@ export function selectValueAccessor(
var builtinAccessor: ControlValueAccessor;
var customAccessor: ControlValueAccessor;
valueAccessors.forEach((v: ControlValueAccessor) => {
if (hasConstructor(v, DefaultValueAccessor)) {
if (v.constructor === DefaultValueAccessor) {
defaultAccessor = v;
} else if (isBuiltInAccessor(v)) {
if (isPresent(builtinAccessor))
if (builtinAccessor)
_throwError(dir, 'More than one built-in value accessor matches form control with');
builtinAccessor = v;
} else {
if (isPresent(customAccessor))
if (customAccessor)
_throwError(dir, 'More than one custom value accessor matches form control with');
customAccessor = v;
}
});
if (isPresent(customAccessor)) return customAccessor;
if (isPresent(builtinAccessor)) return builtinAccessor;
if (isPresent(defaultAccessor)) return defaultAccessor;
if (customAccessor) return customAccessor;
if (builtinAccessor) return builtinAccessor;
if (defaultAccessor) return defaultAccessor;
_throwError(dir, 'No valid value accessor for form control with');
return null;

View File

@ -9,7 +9,7 @@
import {Injectable} from '@angular/core';
import {AsyncValidatorFn, ValidatorFn} from './directives/validators';
import {isArray, isPresent} from './facade/lang';
import {isPresent} from './facade/lang';
import {AbstractControl, FormArray, FormControl, FormGroup} from './model';
/**
@ -86,10 +86,10 @@ export class FormBuilder {
controlConfig instanceof FormArray) {
return controlConfig;
} else if (isArray(controlConfig)) {
var value = controlConfig[0];
var validator: ValidatorFn = controlConfig.length > 1 ? controlConfig[1] : null;
var asyncValidator: AsyncValidatorFn = controlConfig.length > 2 ? controlConfig[2] : null;
} else if (Array.isArray(controlConfig)) {
const value = controlConfig[0];
const validator: ValidatorFn = controlConfig.length > 1 ? controlConfig[1] : null;
const asyncValidator: AsyncValidatorFn = controlConfig.length > 2 ? controlConfig[2] : null;
return this.control(value, validator, asyncValidator);
} else {

View File

@ -11,8 +11,7 @@ import {fromPromise} from 'rxjs/observable/fromPromise';
import {composeAsyncValidators, composeValidators} from './directives/shared';
import {AsyncValidatorFn, ValidatorFn} from './directives/validators';
import {EventEmitter, Observable} from './facade/async';
import {ListWrapper, StringMapWrapper} from './facade/collection';
import {isBlank, isPresent, isStringMap, normalizeBool} from './facade/lang';
import {isBlank, isPresent, normalizeBool} from './facade/lang';
import {isPromise} from './private_import_core';
@ -48,7 +47,7 @@ function _find(control: AbstractControl, path: Array<string|number>| string, del
if (!(path instanceof Array)) {
path = (<string>path).split(delimiter);
}
if (path instanceof Array && ListWrapper.isEmpty(path)) return null;
if (path instanceof Array && (path.length === 0)) return null;
return (<Array<string|number>>path).reduce((v, name) => {
if (v instanceof FormGroup) {
@ -109,6 +108,11 @@ export abstract class AbstractControl {
*/
get value(): any { return this._value; }
/**
* The parent control.
*/
get parent(): FormGroup|FormArray { return this._parent; }
/**
* The validation status of the control. There are four possible
* validation statuses:
@ -249,7 +253,7 @@ export abstract class AbstractControl {
this._touched = true;
if (isPresent(this._parent) && !onlySelf) {
this._parent.markAsTouched({onlySelf: onlySelf});
this._parent.markAsTouched({onlySelf});
}
}
@ -267,7 +271,7 @@ export abstract class AbstractControl {
(control: AbstractControl) => { control.markAsUntouched({onlySelf: true}); });
if (isPresent(this._parent) && !onlySelf) {
this._parent._updateTouched({onlySelf: onlySelf});
this._parent._updateTouched({onlySelf});
}
}
@ -282,7 +286,7 @@ export abstract class AbstractControl {
this._pristine = false;
if (isPresent(this._parent) && !onlySelf) {
this._parent.markAsDirty({onlySelf: onlySelf});
this._parent.markAsDirty({onlySelf});
}
}
@ -299,7 +303,7 @@ export abstract class AbstractControl {
this._forEachChild((control: AbstractControl) => { control.markAsPristine({onlySelf: true}); });
if (isPresent(this._parent) && !onlySelf) {
this._parent._updatePristine({onlySelf: onlySelf});
this._parent._updatePristine({onlySelf});
}
}
@ -311,7 +315,7 @@ export abstract class AbstractControl {
this._status = PENDING;
if (isPresent(this._parent) && !onlySelf) {
this._parent.markAsPending({onlySelf: onlySelf});
this._parent.markAsPending({onlySelf});
}
}
@ -348,7 +352,7 @@ export abstract class AbstractControl {
enable({onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
this._status = VALID;
this._forEachChild((control: AbstractControl) => { control.enable({onlySelf: true}); });
this.updateValueAndValidity({onlySelf: true, emitEvent: emitEvent});
this.updateValueAndValidity({onlySelf: true, emitEvent});
this._updateAncestors(onlySelf);
this._onDisabledChange.forEach((changeFn) => changeFn(false));
@ -407,7 +411,7 @@ export abstract class AbstractControl {
}
if (isPresent(this._parent) && !onlySelf) {
this._parent.updateValueAndValidity({onlySelf: onlySelf, emitEvent: emitEvent});
this._parent.updateValueAndValidity({onlySelf, emitEvent});
}
}
@ -428,8 +432,8 @@ export abstract class AbstractControl {
this._status = PENDING;
this._cancelExistingSubscription();
var obs = toObservable(this.asyncValidator(this));
this._asyncValidationSubscription = obs.subscribe(
{next: (res: {[key: string]: any}) => this.setErrors(res, {emitEvent: emitEvent})});
this._asyncValidationSubscription =
obs.subscribe({next: (res: {[key: string]: any}) => this.setErrors(res, {emitEvent})});
}
}
@ -491,7 +495,7 @@ export abstract class AbstractControl {
* If no path is given, it checks for the error on the present control.
*/
getError(errorCode: string, path: string[] = null): any {
var control = isPresent(path) && !ListWrapper.isEmpty(path) ? this.get(path) : this;
const control = isPresent(path) && (path.length > 0) ? this.get(path) : this;
if (isPresent(control) && isPresent(control._errors)) {
return control._errors[errorCode];
} else {
@ -564,7 +568,7 @@ export abstract class AbstractControl {
/** @internal */
_anyControlsHaveStatus(status: string): boolean {
return this._anyControls((control: AbstractControl) => control.status == status);
return this._anyControls((control: AbstractControl) => control.status === status);
}
/** @internal */
@ -582,7 +586,7 @@ export abstract class AbstractControl {
this._pristine = !this._anyControlsDirty();
if (isPresent(this._parent) && !onlySelf) {
this._parent._updatePristine({onlySelf: onlySelf});
this._parent._updatePristine({onlySelf});
}
}
@ -591,7 +595,7 @@ export abstract class AbstractControl {
this._touched = this._anyControlsTouched();
if (isPresent(this._parent) && !onlySelf) {
this._parent._updateTouched({onlySelf: onlySelf});
this._parent._updateTouched({onlySelf});
}
}
@ -600,8 +604,8 @@ export abstract class AbstractControl {
/** @internal */
_isBoxedValue(formState: any): boolean {
return isStringMap(formState) && Object.keys(formState).length === 2 && 'value' in formState &&
'disabled' in formState;
return typeof formState === 'object' && formState !== null &&
Object.keys(formState).length === 2 && 'value' in formState && 'disabled' in formState;
}
/** @internal */
@ -694,7 +698,7 @@ export class FormControl extends AbstractControl {
if (this._onChange.length && emitModelToViewChange) {
this._onChange.forEach((changeFn) => changeFn(this._value, emitViewToModelChange));
}
this.updateValueAndValidity({onlySelf: onlySelf, emitEvent: emitEvent});
this.updateValueAndValidity({onlySelf, emitEvent});
}
/**
@ -741,11 +745,12 @@ export class FormControl extends AbstractControl {
* console.log(this.control.status); // 'DISABLED'
* ```
*/
reset(formState: any = null, {onlySelf}: {onlySelf?: boolean} = {}): void {
reset(formState: any = null, {onlySelf, emitEvent}: {onlySelf?: boolean,
emitEvent?: boolean} = {}): void {
this._applyFormState(formState);
this.markAsPristine({onlySelf});
this.markAsUntouched({onlySelf});
this.setValue(this._value, {onlySelf});
this.setValue(this._value, {onlySelf, emitEvent});
}
/**
@ -938,13 +943,15 @@ export class FormGroup extends AbstractControl {
*
* ```
*/
setValue(value: {[key: string]: any}, {onlySelf}: {onlySelf?: boolean} = {}): void {
setValue(
value: {[key: string]: any},
{onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
this._checkAllValuesPresent(value);
Object.keys(value).forEach(name => {
this._throwIfControlMissing(name);
this.controls[name].setValue(value[name], {onlySelf: true});
this.controls[name].setValue(value[name], {onlySelf: true, emitEvent});
});
this.updateValueAndValidity({onlySelf: onlySelf});
this.updateValueAndValidity({onlySelf, emitEvent});
}
/**
@ -968,13 +975,15 @@ export class FormGroup extends AbstractControl {
*
* ```
*/
patchValue(value: {[key: string]: any}, {onlySelf}: {onlySelf?: boolean} = {}): void {
patchValue(
value: {[key: string]: any},
{onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
Object.keys(value).forEach(name => {
if (this.controls[name]) {
this.controls[name].patchValue(value[name], {onlySelf: true});
this.controls[name].patchValue(value[name], {onlySelf: true, emitEvent});
}
});
this.updateValueAndValidity({onlySelf: onlySelf});
this.updateValueAndValidity({onlySelf, emitEvent});
}
/**
@ -1009,13 +1018,14 @@ export class FormGroup extends AbstractControl {
* console.log(this.form.get('first').status); // 'DISABLED'
* ```
*/
reset(value: any = {}, {onlySelf}: {onlySelf?: boolean} = {}): void {
reset(value: any = {}, {onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}):
void {
this._forEachChild((control: AbstractControl, name: string) => {
control.reset(value[name], {onlySelf: true});
control.reset(value[name], {onlySelf: true, emitEvent});
});
this.updateValueAndValidity({onlySelf: onlySelf});
this._updatePristine({onlySelf: onlySelf});
this._updateTouched({onlySelf: onlySelf});
this.updateValueAndValidity({onlySelf, emitEvent});
this._updatePristine({onlySelf});
this._updateTouched({onlySelf});
}
/**
@ -1051,7 +1061,7 @@ export class FormGroup extends AbstractControl {
}
/** @internal */
_setUpControls() {
_setUpControls(): void {
this._forEachChild((control: AbstractControl) => {
control.setParent(this);
control._registerOnCollectionChange(this._onCollectionChange);
@ -1059,11 +1069,11 @@ export class FormGroup extends AbstractControl {
}
/** @internal */
_updateValue() { this._value = this._reduceValue(); }
_updateValue(): void { this._value = this._reduceValue(); }
/** @internal */
_anyControls(condition: Function): boolean {
var res = false;
let res = false;
this._forEachChild((control: AbstractControl, name: string) => {
res = res || (this.contains(name) && condition(control));
});
@ -1083,7 +1093,7 @@ export class FormGroup extends AbstractControl {
/** @internal */
_reduceChildren(initValue: any, fn: Function) {
var res = initValue;
let res = initValue;
this._forEachChild(
(control: AbstractControl, name: string) => { res = fn(res, control, name); });
return res;
@ -1182,7 +1192,8 @@ export class FormArray extends AbstractControl {
* Insert a new {@link AbstractControl} at the given `index` in the array.
*/
insert(index: number, control: AbstractControl): void {
ListWrapper.insert(this.controls, index, control);
this.controls.splice(index, 0, control);
this._registerControl(control);
this.updateValueAndValidity();
this._onCollectionChange();
@ -1193,7 +1204,7 @@ export class FormArray extends AbstractControl {
*/
removeAt(index: number): void {
if (this.controls[index]) this.controls[index]._registerOnCollectionChange(() => {});
ListWrapper.removeAt(this.controls, index);
this.controls.splice(index, 1);
this.updateValueAndValidity();
this._onCollectionChange();
}
@ -1203,10 +1214,10 @@ export class FormArray extends AbstractControl {
*/
setControl(index: number, control: AbstractControl): void {
if (this.controls[index]) this.controls[index]._registerOnCollectionChange(() => {});
ListWrapper.removeAt(this.controls, index);
this.controls.splice(index, 1);
if (control) {
ListWrapper.insert(this.controls, index, control);
this.controls.splice(index, 0, control);
this._registerControl(control);
}
@ -1240,13 +1251,14 @@ export class FormArray extends AbstractControl {
* console.log(arr.value); // ['Nancy', 'Drew']
* ```
*/
setValue(value: any[], {onlySelf}: {onlySelf?: boolean} = {}): void {
setValue(value: any[], {onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}):
void {
this._checkAllValuesPresent(value);
value.forEach((newValue: any, index: number) => {
this._throwIfControlMissing(index);
this.at(index).setValue(newValue, {onlySelf: true});
this.at(index).setValue(newValue, {onlySelf: true, emitEvent});
});
this.updateValueAndValidity({onlySelf: onlySelf});
this.updateValueAndValidity({onlySelf, emitEvent});
}
/**
@ -1269,13 +1281,14 @@ export class FormArray extends AbstractControl {
* console.log(arr.value); // ['Nancy', null]
* ```
*/
patchValue(value: any[], {onlySelf}: {onlySelf?: boolean} = {}): void {
patchValue(value: any[], {onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}):
void {
value.forEach((newValue: any, index: number) => {
if (this.at(index)) {
this.at(index).patchValue(newValue, {onlySelf: true});
this.at(index).patchValue(newValue, {onlySelf: true, emitEvent});
}
});
this.updateValueAndValidity({onlySelf: onlySelf});
this.updateValueAndValidity({onlySelf, emitEvent});
}
/**
@ -1309,13 +1322,14 @@ export class FormArray extends AbstractControl {
* console.log(this.arr.get(0).status); // 'DISABLED'
* ```
*/
reset(value: any = [], {onlySelf}: {onlySelf?: boolean} = {}): void {
reset(value: any = [], {onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}):
void {
this._forEachChild((control: AbstractControl, index: number) => {
control.reset(value[index], {onlySelf: true});
control.reset(value[index], {onlySelf: true, emitEvent});
});
this.updateValueAndValidity({onlySelf: onlySelf});
this._updatePristine({onlySelf: onlySelf});
this._updateTouched({onlySelf: onlySelf});
this.updateValueAndValidity({onlySelf, emitEvent});
this._updatePristine({onlySelf});
this._updateTouched({onlySelf});
}
/**

View File

@ -8,7 +8,6 @@
import {OpaqueToken} from '@angular/core';
import {toPromise} from 'rxjs/operator/toPromise';
import {AsyncValidatorFn, ValidatorFn} from './directives/validators';
import {StringMapWrapper} from './facade/collection';
import {isPresent} from './facade/lang';
@ -95,16 +94,24 @@ export class Validators {
/**
* Validator that requires a control to match a regex to its value.
*/
static pattern(pattern: string): ValidatorFn {
static pattern(pattern: string|RegExp): ValidatorFn {
if (!pattern) return Validators.nullValidator;
let regex: RegExp;
let regexStr: string;
if (typeof pattern === 'string') {
regexStr = `^${pattern}$`;
regex = new RegExp(regexStr);
} else {
regexStr = pattern.toString();
regex = pattern;
}
return (control: AbstractControl): {[key: string]: any} => {
if (isEmptyInputValue(control.value)) {
return null; // don't validate empty values to allow optional controls
}
const regex = new RegExp(`^${pattern}$`);
const value: string = control.value;
return regex.test(value) ?
null :
{'pattern': {'requiredPattern': `^${pattern}$`, 'actualValue': value}};
return regex.test(value) ? null :
{'pattern': {'requiredPattern': regexStr, 'actualValue': value}};
};
}
@ -119,7 +126,7 @@ export class Validators {
*/
static compose(validators: ValidatorFn[]): ValidatorFn {
if (!validators) return null;
var presentValidators = validators.filter(isPresent);
const presentValidators = validators.filter(isPresent);
if (presentValidators.length == 0) return null;
return function(control: AbstractControl) {
@ -129,7 +136,7 @@ export class Validators {
static composeAsync(validators: AsyncValidatorFn[]): AsyncValidatorFn {
if (!validators) return null;
var presentValidators = validators.filter(isPresent);
const presentValidators = validators.filter(isPresent);
if (presentValidators.length == 0) return null;
return function(control: AbstractControl) {
@ -152,7 +159,7 @@ function _executeAsyncValidators(control: AbstractControl, validators: AsyncVali
}
function _mergeErrors(arrayOfErrors: any[]): {[key: string]: any} {
var res: {[key: string]: any} =
const res: {[key: string]: any} =
arrayOfErrors.reduce((res: {[key: string]: any}, errors: {[key: string]: any}) => {
return isPresent(errors) ? StringMapWrapper.merge(res, errors) : res;
}, {});

View File

@ -158,6 +158,15 @@ export function main() {
expect(form.valueChanges).toBe(formModel.valueChanges);
});
it('should reexport control methods', () => {
expect(form.hasError('required')).toBe(formModel.hasError('required'));
expect(form.getError('required')).toBe(formModel.getError('required'));
formModel.setErrors({required: true});
expect(form.hasError('required')).toBe(formModel.hasError('required'));
expect(form.getError('required')).toBe(formModel.getError('required'));
});
describe('addControl', () => {
it('should throw when no control found', () => {
const dir = new FormControlName(form, null, null, [defaultAccessor]);
@ -329,6 +338,15 @@ export function main() {
expect(form.enabled).toBe(formModel.enabled);
});
it('should reexport control methods', () => {
expect(form.hasError('required')).toBe(formModel.hasError('required'));
expect(form.getError('required')).toBe(formModel.getError('required'));
formModel.setErrors({required: true});
expect(form.hasError('required')).toBe(formModel.hasError('required'));
expect(form.getError('required')).toBe(formModel.getError('required'));
});
describe('addControl & addFormGroup', () => {
it('should create a control with the given name', fakeAsync(() => {
form.addFormGroup(personControlGroupDir);
@ -406,6 +424,15 @@ export function main() {
expect(controlGroupDir.disabled).toBe(formModel.disabled);
expect(controlGroupDir.enabled).toBe(formModel.enabled);
});
it('should reexport control methods', () => {
expect(controlGroupDir.hasError('required')).toBe(formModel.hasError('required'));
expect(controlGroupDir.getError('required')).toBe(formModel.getError('required'));
formModel.setErrors({required: true});
expect(controlGroupDir.hasError('required')).toBe(formModel.hasError('required'));
expect(controlGroupDir.getError('required')).toBe(formModel.getError('required'));
});
});
describe('FormArrayName', () => {
@ -434,6 +461,15 @@ export function main() {
expect(formArrayDir.disabled).toBe(formModel.disabled);
expect(formArrayDir.enabled).toBe(formModel.enabled);
});
it('should reexport control methods', () => {
expect(formArrayDir.hasError('required')).toBe(formModel.hasError('required'));
expect(formArrayDir.getError('required')).toBe(formModel.getError('required'));
formModel.setErrors({required: true});
expect(formArrayDir.hasError('required')).toBe(formModel.hasError('required'));
expect(formArrayDir.getError('required')).toBe(formModel.getError('required'));
});
});
describe('FormControlDirective', () => {
@ -466,6 +502,15 @@ export function main() {
it('should reexport control properties', () => { checkProperties(control); });
it('should reexport control methods', () => {
expect(controlDir.hasError('required')).toBe(control.hasError('required'));
expect(controlDir.getError('required')).toBe(control.getError('required'));
control.setErrors({required: true});
expect(controlDir.hasError('required')).toBe(control.hasError('required'));
expect(controlDir.getError('required')).toBe(control.getError('required'));
});
it('should reexport new control properties', () => {
var newControl = new FormControl(null);
controlDir.form = newControl;
@ -486,15 +531,16 @@ export function main() {
describe('NgModel', () => {
let ngModel: NgModel;
let control: FormControl;
beforeEach(() => {
ngModel = new NgModel(
null, [Validators.required], [asyncValidator('expected')], [defaultAccessor]);
ngModel.valueAccessor = new DummyControlValueAccessor();
control = ngModel.control;
});
it('should reexport control properties', () => {
var control = ngModel.control;
expect(ngModel.control).toBe(control);
expect(ngModel.value).toBe(control.value);
expect(ngModel.valid).toBe(control.valid);
@ -511,6 +557,15 @@ export function main() {
expect(ngModel.enabled).toBe(control.enabled);
});
it('should reexport control methods', () => {
expect(ngModel.hasError('required')).toBe(control.hasError('required'));
expect(ngModel.getError('required')).toBe(control.getError('required'));
control.setErrors({required: true});
expect(ngModel.hasError('required')).toBe(control.hasError('required'));
expect(ngModel.getError('required')).toBe(control.getError('required'));
});
it('should throw when no value accessor with named control', () => {
const namedDir = new NgModel(null, null, null, null);
namedDir.name = 'one';
@ -610,6 +665,15 @@ export function main() {
expect(controlNameDir.disabled).toBe(formModel.disabled);
expect(controlNameDir.enabled).toBe(formModel.enabled);
});
it('should reexport control methods', () => {
expect(controlNameDir.hasError('required')).toBe(formModel.hasError('required'));
expect(controlNameDir.getError('required')).toBe(formModel.getError('required'));
formModel.setErrors({required: true});
expect(controlNameDir.hasError('required')).toBe(formModel.hasError('required'));
expect(controlNameDir.getError('required')).toBe(formModel.getError('required'));
});
});
});
}

View File

@ -172,6 +172,16 @@ export function main() {
expect(logger).toEqual(['control1', 'control2', 'array', 'form']);
});
it('should not fire an event when explicitly specified', fakeAsync(() => {
form.valueChanges.subscribe((value) => { throw 'Should not happen'; });
a.valueChanges.subscribe((value) => { throw 'Should not happen'; });
c.valueChanges.subscribe((value) => { throw 'Should not happen'; });
c2.valueChanges.subscribe((value) => { throw 'Should not happen'; });
a.setValue(['one', 'two'], {emitEvent: false});
tick();
}));
it('should emit one statusChange event per control', () => {
form.statusChanges.subscribe(() => logger.push('form'));
a.statusChanges.subscribe(() => logger.push('array'));
@ -277,6 +287,16 @@ export function main() {
expect(logger).toEqual(['control1', 'array', 'form']);
});
it('should not fire an event when explicitly specified', fakeAsync(() => {
form.valueChanges.subscribe((value) => { throw 'Should not happen'; });
a.valueChanges.subscribe((value) => { throw 'Should not happen'; });
c.valueChanges.subscribe((value) => { throw 'Should not happen'; });
c2.valueChanges.subscribe((value) => { throw 'Should not happen'; });
a.patchValue(['one', 'two'], {emitEvent: false});
tick();
}));
it('should emit one statusChange event per control', () => {
form.statusChanges.subscribe(() => logger.push('form'));
a.statusChanges.subscribe(() => logger.push('array'));
@ -478,6 +498,17 @@ export function main() {
expect(logger).toEqual(['control1', 'control2', 'array', 'form']);
});
it('should not fire an event when explicitly specified', fakeAsync(() => {
form.valueChanges.subscribe((value) => { throw 'Should not happen'; });
a.valueChanges.subscribe((value) => { throw 'Should not happen'; });
c.valueChanges.subscribe((value) => { throw 'Should not happen'; });
c2.valueChanges.subscribe((value) => { throw 'Should not happen'; });
c3.valueChanges.subscribe((value) => { throw 'Should not happen'; });
a.reset([], {emitEvent: false});
tick();
}));
it('should emit one statusChange event per reset control', () => {
form.statusChanges.subscribe(() => logger.push('form'));
a.statusChanges.subscribe(() => logger.push('array'));

View File

@ -555,6 +555,16 @@ export function main() {
expect(logger).toEqual(['control1', 'group']);
});
it('should not fire an event when explicitly specified', fakeAsync(() => {
g.valueChanges.subscribe((value) => { throw 'Should not happen'; });
c.valueChanges.subscribe((value) => { throw 'Should not happen'; });
c2.valueChanges.subscribe((value) => { throw 'Should not happen'; });
c.reset(null, {emitEvent: false});
tick();
}));
it('should emit one statusChange event per reset control', () => {
g.statusChanges.subscribe(() => logger.push('group'));
c.statusChanges.subscribe(() => logger.push('control1'));

View File

@ -236,6 +236,15 @@ export function main() {
expect(logger).toEqual(['control1', 'control2', 'group', 'form']);
});
it('should not fire an event when explicitly specified', fakeAsync(() => {
form.valueChanges.subscribe((value) => { throw 'Should not happen'; });
g.valueChanges.subscribe((value) => { throw 'Should not happen'; });
c.valueChanges.subscribe((value) => { throw 'Should not happen'; });
g.setValue({'one': 'one', 'two': 'two'}, {emitEvent: false});
tick();
}));
it('should emit one statusChange event per control', () => {
form.statusChanges.subscribe(() => logger.push('form'));
g.statusChanges.subscribe(() => logger.push('group'));
@ -341,6 +350,15 @@ export function main() {
expect(logger).toEqual(['control1', 'group', 'form']);
});
it('should not fire an event when explicitly specified', fakeAsync(() => {
form.valueChanges.subscribe((value) => { throw 'Should not happen'; });
g.valueChanges.subscribe((value) => { throw 'Should not happen'; });
c.valueChanges.subscribe((value) => { throw 'Should not happen'; });
g.patchValue({'one': 'one', 'two': 'two'}, {emitEvent: false});
tick();
}));
it('should emit one statusChange event per control', () => {
form.statusChanges.subscribe(() => logger.push('form'));
g.statusChanges.subscribe(() => logger.push('group'));
@ -541,6 +559,15 @@ export function main() {
expect(logger).toEqual(['control1', 'control2', 'group', 'form']);
});
it('should not fire an event when explicitly specified', fakeAsync(() => {
form.valueChanges.subscribe((value) => { throw 'Should not happen'; });
g.valueChanges.subscribe((value) => { throw 'Should not happen'; });
c.valueChanges.subscribe((value) => { throw 'Should not happen'; });
g.reset({}, {emitEvent: false});
tick();
}));
it('should emit one statusChange event per reset control', () => {
form.statusChanges.subscribe(() => logger.push('form'));
g.statusChanges.subscribe(() => logger.push('group'));

View File

@ -22,11 +22,26 @@ export function main() {
TestBed.configureTestingModule({
imports: [FormsModule, ReactiveFormsModule],
declarations: [
FormControlComp, FormGroupComp, FormArrayComp, FormArrayNestedGroup,
FormControlNameSelect, FormControlNumberInput, FormControlRadioButtons, WrappedValue,
WrappedValueForm, MyInput, MyInputForm, FormGroupNgModel, FormControlNgModel,
LoginIsEmptyValidator, LoginIsEmptyWrapper, ValidationBindingsForm, UniqLoginValidator,
UniqLoginWrapper, NestedFormGroupComp
FormControlComp,
FormGroupComp,
FormArrayComp,
FormArrayNestedGroup,
FormControlNameSelect,
FormControlNumberInput,
FormControlRangeInput,
FormControlRadioButtons,
WrappedValue,
WrappedValueForm,
MyInput,
MyInputForm,
FormGroupNgModel,
FormControlNgModel,
LoginIsEmptyValidator,
LoginIsEmptyWrapper,
ValidationBindingsForm,
UniqLoginValidator,
UniqLoginWrapper,
NestedFormGroupComp
]
});
});
@ -646,6 +661,60 @@ export function main() {
expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']);
});
it('should work with single fields and async validators', fakeAsync(() => {
const fixture = TestBed.createComponent(FormControlComp);
const control = new FormControl('', null, uniqLoginAsyncValidator('good'));
fixture.debugElement.componentInstance.control = control;
fixture.detectChanges();
const input = fixture.debugElement.query(By.css('input')).nativeElement;
expect(sortedClassList(input)).toEqual(['ng-pending', 'ng-pristine', 'ng-untouched']);
dispatchEvent(input, 'blur');
fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-pending', 'ng-pristine', 'ng-touched']);
input.value = 'good';
dispatchEvent(input, 'input');
tick();
fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']);
}));
it('should work with single fields that combines async and sync validators', fakeAsync(() => {
const fixture = TestBed.createComponent(FormControlComp);
const control =
new FormControl('', Validators.required, uniqLoginAsyncValidator('good'));
fixture.debugElement.componentInstance.control = control;
fixture.detectChanges();
const input = fixture.debugElement.query(By.css('input')).nativeElement;
expect(sortedClassList(input)).toEqual(['ng-invalid', 'ng-pristine', 'ng-untouched']);
dispatchEvent(input, 'blur');
fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-invalid', 'ng-pristine', 'ng-touched']);
input.value = 'bad';
dispatchEvent(input, 'input');
fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-pending', 'ng-touched']);
tick();
fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-invalid', 'ng-touched']);
input.value = 'good';
dispatchEvent(input, 'input');
tick();
fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']);
}));
it('should work with single fields in parent forms', () => {
const fixture = TestBed.createComponent(FormGroupComp);
const form = new FormGroup({'login': new FormControl('', Validators.required)});
@ -1072,6 +1141,57 @@ export function main() {
});
describe('should support <type=range>', () => {
it('with basic use case', () => {
const fixture = TestBed.createComponent(FormControlRangeInput);
const control = new FormControl(10);
fixture.componentInstance.control = control;
fixture.detectChanges();
// model -> view
const input = fixture.debugElement.query(By.css('input'));
expect(input.nativeElement.value).toEqual('10');
input.nativeElement.value = '20';
dispatchEvent(input.nativeElement, 'input');
// view -> model
expect(control.value).toEqual(20);
});
it('when value is cleared in the UI', () => {
const fixture = TestBed.createComponent(FormControlNumberInput);
const control = new FormControl(10, Validators.required);
fixture.componentInstance.control = control;
fixture.detectChanges();
const input = fixture.debugElement.query(By.css('input'));
input.nativeElement.value = '';
dispatchEvent(input.nativeElement, 'input');
expect(control.valid).toBe(false);
expect(control.value).toEqual(null);
input.nativeElement.value = '0';
dispatchEvent(input.nativeElement, 'input');
expect(control.valid).toBe(true);
expect(control.value).toEqual(0);
});
it('when value is cleared programmatically', () => {
const fixture = TestBed.createComponent(FormControlNumberInput);
const control = new FormControl(10);
fixture.componentInstance.control = control;
fixture.detectChanges();
control.setValue(null);
const input = fixture.debugElement.query(By.css('input'));
expect(input.nativeElement.value).toEqual('');
});
});
describe('custom value accessors', () => {
it('should support basic functionality', () => {
const fixture = TestBed.createComponent(WrappedValueForm);
@ -1736,7 +1856,7 @@ class LoginIsEmptyValidator {
}]
})
class UniqLoginValidator implements Validator {
@Input('uniq-login-validator') expected: any /** TODO #9100 */;
@Input('uniq-login-validator') expected: any;
validate(c: AbstractControl) { return uniqLoginAsyncValidator(this.expected)(c); }
}
@ -1798,6 +1918,16 @@ class FormControlNumberInput {
control: FormControl;
}
@Component({
selector: 'form-control-range-input',
template: `
<input type="range" [formControl]="control">
`
})
class FormControlRangeInput {
control: FormControl;
}
@Component({
selector: 'form-control-radio-buttons',
template: `

View File

@ -6,9 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Component, Input} from '@angular/core';
import {Component, Directive, Input, forwardRef} from '@angular/core';
import {TestBed, async, fakeAsync, tick} from '@angular/core/testing';
import {ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR, NgForm} from '@angular/forms';
import {AbstractControl, ControlValueAccessor, FormsModule, NG_ASYNC_VALIDATORS, NG_VALUE_ACCESSOR, NgForm, Validator} from '@angular/forms';
import {By} from '@angular/platform-browser/src/dom/debug/by';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {dispatchEvent} from '@angular/platform-browser/testing/browser_util';
@ -20,9 +20,10 @@ export function main() {
TestBed.configureTestingModule({
declarations: [
StandaloneNgModel, NgModelForm, NgModelGroupForm, NgModelValidBinding, NgModelNgIfForm,
NgModelRadioForm, NgModelSelectForm, NgNoFormComp, InvalidNgModelNoName,
NgModelRadioForm, NgModelRangeForm, NgModelSelectForm, NgNoFormComp, InvalidNgModelNoName,
NgModelOptionsStandalone, NgModelCustomComp, NgModelCustomWrapper,
NgModelValidationBindings, NgModelMultipleValidators
NgModelValidationBindings, NgModelMultipleValidators, NgAsyncValidator,
NgModelAsyncValidation
],
imports: [FormsModule]
});
@ -139,7 +140,6 @@ export function main() {
fixture.detectChanges();
const input = fixture.debugElement.query(By.css('input')).nativeElement;
const form = fixture.debugElement.children[0].injector.get(NgForm);
expect(sortedClassList(input)).toEqual(['ng-invalid', 'ng-pristine', 'ng-untouched']);
dispatchEvent(input, 'blur');
@ -154,6 +154,29 @@ export function main() {
});
}));
it('should set status classes with ngModel and async validators', fakeAsync(() => {
const fixture = TestBed.createComponent(NgModelAsyncValidation);
fixture.whenStable().then(() => {
fixture.detectChanges();
const input = fixture.debugElement.query(By.css('input')).nativeElement;
expect(sortedClassList(input)).toEqual(['ng-pending', 'ng-pristine', 'ng-untouched']);
dispatchEvent(input, 'blur');
fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-pending', 'ng-pristine', 'ng-touched']);
input.value = 'updatedValue';
dispatchEvent(input, 'input');
tick();
fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']);
});
}));
it('should set status classes with ngModelGroup and ngForm', async(() => {
const fixture = TestBed.createComponent(NgModelGroupForm);
fixture.componentInstance.first = '';
@ -480,6 +503,26 @@ export function main() {
});
describe('range control', () => {
it('should support <type=range>', fakeAsync(() => {
const fixture = TestBed.createComponent(NgModelRangeForm);
// model -> view
fixture.componentInstance.val = 4;
fixture.detectChanges();
tick();
let input = fixture.debugElement.query(By.css('input'));
expect(input.nativeElement.value).toBe('4');
fixture.detectChanges();
tick();
let newVal = '4';
input.triggerEventHandler('input', {target: {value: newVal}});
tick();
// view -> model
fixture.detectChanges();
expect(typeof(fixture.componentInstance.val)).toBe('number');
}));
});
describe('radio controls', () => {
it('should support <type=radio>', fakeAsync(() => {
const fixture = TestBed.createComponent(NgModelRadioForm);
@ -883,7 +926,7 @@ export function main() {
});
});
};
}
@Component({
selector: 'standalone-ng-model',
@ -1000,6 +1043,11 @@ class NgModelOptionsStandalone {
two: string;
}
@Component({selector: 'ng-model-range-form', template: '<input type="range" [(ngModel)]="val">'})
class NgModelRangeForm {
val: any;
}
@Component({
selector: 'ng-model-radio-form',
template: `
@ -1096,6 +1144,23 @@ class NgModelMultipleValidators {
pattern: string;
}
@Directive({
selector: '[ng-async-validator]',
providers: [
{provide: NG_ASYNC_VALIDATORS, useExisting: forwardRef(() => NgAsyncValidator), multi: true}
]
})
class NgAsyncValidator implements Validator {
validate(c: AbstractControl) { return Promise.resolve(null); }
}
@Component({
selector: 'ng-model-async-validation',
template: `<input name="async" ngModel ng-async-validator>`
})
class NgModelAsyncValidation {
}
function sortedClassList(el: HTMLElement) {
const l = getDOM().classList(el);
l.sort();

View File

@ -17,7 +17,7 @@ import {EventEmitter} from '../src/facade/async';
export function main() {
function validator(key: string, error: any) {
return function(c: AbstractControl) {
var r: {[k: string]: string} = {};
const r: {[k: string]: string} = {};
r[key] = error;
return r;
};
@ -101,13 +101,31 @@ export function main() {
() => { expect(Validators.pattern('null')(new FormControl(null))).toBeNull(); });
it('should not error on valid strings',
() => { expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaAA'))).toBeNull(); });
() => expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaAA'))).toBeNull());
it('should error on failure to match string', () => {
expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaa0'))).toEqual({
'pattern': {'requiredPattern': '^[a-zA-Z ]*$', 'actualValue': 'aaa0'}
});
});
it('should accept RegExp object', () => {
const pattern: RegExp = new RegExp('[a-zA-Z ]+');
expect(Validators.pattern(pattern)(new FormControl('aaAA'))).toBeNull();
});
it('should error on failure to match RegExp object', () => {
const pattern: RegExp = new RegExp('^[a-zA-Z ]*$');
expect(Validators.pattern(pattern)(new FormControl('aaa0'))).toEqual({
'pattern': {'requiredPattern': '/^[a-zA-Z ]*$/', 'actualValue': 'aaa0'}
});
});
it('should not error on "null" pattern',
() => expect(Validators.pattern(null)(new FormControl('aaAA'))).toBeNull());
it('should not error on "undefined" pattern',
() => expect(Validators.pattern(undefined)(new FormControl('aaAA'))).toBeNull());
});
describe('compose', () => {
@ -115,22 +133,22 @@ export function main() {
() => { expect(Validators.compose(null)).toBe(null); });
it('should collect errors from all the validators', () => {
var c = Validators.compose([validator('a', true), validator('b', true)]);
const c = Validators.compose([validator('a', true), validator('b', true)]);
expect(c(new FormControl(''))).toEqual({'a': true, 'b': true});
});
it('should run validators left to right', () => {
var c = Validators.compose([validator('a', 1), validator('a', 2)]);
const c = Validators.compose([validator('a', 1), validator('a', 2)]);
expect(c(new FormControl(''))).toEqual({'a': 2});
});
it('should return null when no errors', () => {
var c = Validators.compose([Validators.nullValidator, Validators.nullValidator]);
const c = Validators.compose([Validators.nullValidator, Validators.nullValidator]);
expect(c(new FormControl(''))).toBeNull();
});
it('should ignore nulls', () => {
var c = Validators.compose([null, Validators.required]);
const c = Validators.compose([null, Validators.required]);
expect(c(new FormControl(''))).toEqual({'required': true});
});
});

View File

@ -13,7 +13,7 @@ import {Observer} from 'rxjs/Observer';
import {ResponseOptions} from '../base_response_options';
import {ContentType, ReadyState, RequestMethod, ResponseContentType, ResponseType} from '../enums';
import {isPresent, isString} from '../facade/lang';
import {isPresent} from '../facade/lang';
import {Headers} from '../headers';
import {getResponseURL, isSuccess} from '../http_utils';
import {Connection, ConnectionBackend, XSRFStrategy} from '../interfaces';
@ -57,7 +57,7 @@ export class XHRConnection implements Connection {
// by IE10)
let body = _xhr.response === undefined ? _xhr.responseText : _xhr.response;
// Implicitly strip a potential XSSI prefix.
if (isString(body)) body = body.replace(XSSI_PREFIX, '');
if (typeof body === 'string') body = body.replace(XSSI_PREFIX, '');
let headers = Headers.fromResponseHeaderString(_xhr.getAllResponseHeaders());
let url = getResponseURL(_xhr);

View File

@ -8,7 +8,7 @@
import {Injectable} from '@angular/core';
import {isPresent, isString} from '../src/facade/lang';
import {isPresent} from '../src/facade/lang';
import {RequestMethod, ResponseContentType} from './enums';
import {Headers} from './headers';
@ -82,7 +82,8 @@ export class RequestOptions {
this.body = isPresent(body) ? body : null;
this.url = isPresent(url) ? url : null;
this.search = isPresent(search) ?
(isString(search) ? new URLSearchParams(<string>(search)) : <URLSearchParams>(search)) :
(typeof search === 'string' ? new URLSearchParams(<string>(search)) :
<URLSearchParams>(search)) :
null;
this.withCredentials = isPresent(withCredentials) ? withCredentials : null;
this.responseType = isPresent(responseType) ? responseType : null;
@ -115,19 +116,18 @@ export class RequestOptions {
*/
merge(options?: RequestOptionsArgs): RequestOptions {
return new RequestOptions({
method: isPresent(options) && isPresent(options.method) ? options.method : this.method,
headers: isPresent(options) && isPresent(options.headers) ? options.headers : this.headers,
body: isPresent(options) && isPresent(options.body) ? options.body : this.body,
url: isPresent(options) && isPresent(options.url) ? options.url : this.url,
search: isPresent(options) && isPresent(options.search) ?
(isString(options.search) ? new URLSearchParams(<string>(options.search)) :
(<URLSearchParams>(options.search)).clone()) :
method: options && isPresent(options.method) ? options.method : this.method,
headers: options && isPresent(options.headers) ? options.headers : this.headers,
body: options && isPresent(options.body) ? options.body : this.body,
url: options && isPresent(options.url) ? options.url : this.url,
search: options && isPresent(options.search) ?
(typeof options.search === 'string' ? new URLSearchParams(options.search) :
(<URLSearchParams>(options.search)).clone()) :
this.search,
withCredentials: isPresent(options) && isPresent(options.withCredentials) ?
options.withCredentials :
this.withCredentials,
responseType: isPresent(options) && isPresent(options.responseType) ? options.responseType :
this.responseType
withCredentials: options && isPresent(options.withCredentials) ? options.withCredentials :
this.withCredentials,
responseType: options && isPresent(options.responseType) ? options.responseType :
this.responseType
});
}
}

View File

@ -6,8 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Json, isString} from '../src/facade/lang';
import {isJsObject, stringToArrayBuffer} from './http_utils';
import {URLSearchParams} from './url_search_params';
@ -26,12 +24,12 @@ export abstract class Body {
* Attempts to return body as parsed `JSON` object, or raises an exception.
*/
json(): any {
if (isString(this._body)) {
return Json.parse(<string>this._body);
if (typeof this._body === 'string') {
return JSON.parse(<string>this._body);
}
if (this._body instanceof ArrayBuffer) {
return Json.parse(this.text());
return JSON.parse(this.text());
}
return this._body;
@ -54,7 +52,7 @@ export abstract class Body {
}
if (isJsObject(this._body)) {
return Json.stringify(this._body);
return JSON.stringify(this._body, null, 2);
}
return this._body.toString();

View File

@ -8,7 +8,7 @@
import {Injectable} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import {isPresent, isString} from '../src/facade/lang';
import {isPresent} from '../src/facade/lang';
import {BaseRequestOptions, RequestOptions} from './base_request_options';
import {RequestMethod} from './enums';
import {ConnectionBackend, RequestOptionsArgs} from './interfaces';
@ -114,7 +114,7 @@ export class Http {
*/
request(url: string|Request, options?: RequestOptionsArgs): Observable<Response> {
let responseObservable: any;
if (isString(url)) {
if (typeof url === 'string') {
responseObservable = httpRequest(
this._backend,
new Request(mergeOptions(this._defaultOptions, options, RequestMethod.Get, <string>url)));
@ -212,7 +212,7 @@ export class Jsonp extends Http {
*/
request(url: string|Request, options?: RequestOptionsArgs): Observable<Response> {
let responseObservable: any;
if (isString(url)) {
if (typeof url === 'string') {
url =
new Request(mergeOptions(this._defaultOptions, options, RequestMethod.Get, <string>url));
}

View File

@ -6,12 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/
import {isString} from '../src/facade/lang';
import {RequestMethod} from './enums';
export function normalizeMethodName(method: string | RequestMethod): RequestMethod {
if (!isString(method)) return method;
if (typeof method !== 'string') return method;
switch (method.toUpperCase()) {
case 'GET':
return RequestMethod.Get;

View File

@ -15,7 +15,6 @@ import {CookieXSRFStrategy, XHRBackend, XHRConnection} from '../../src/backends/
import {BaseRequestOptions, RequestOptions} from '../../src/base_request_options';
import {BaseResponseOptions, ResponseOptions} from '../../src/base_response_options';
import {ResponseContentType, ResponseType} from '../../src/enums';
import {Json} from '../../src/facade/lang';
import {Headers} from '../../src/headers';
import {XSRFStrategy} from '../../src/interfaces';
import {Request} from '../../src/static_request';
@ -257,7 +256,7 @@ export function main() {
var connection = new XHRConnection(
new Request(base.merge(new RequestOptions({body: body}))), new MockBrowserXHR());
connection.response.subscribe();
expect(sendSpy).toHaveBeenCalledWith(Json.stringify(body));
expect(sendSpy).toHaveBeenCalledWith(JSON.stringify(body, null, 2));
expect(setRequestHeaderSpy).toHaveBeenCalledWith('content-type', 'application/json');
});

View File

@ -14,7 +14,6 @@ import {Observable} from 'rxjs/Observable';
import {zip} from 'rxjs/observable/zip';
import {BaseRequestOptions, ConnectionBackend, Http, HttpModule, JSONPBackend, Jsonp, JsonpModule, Request, RequestMethod, RequestOptions, Response, ResponseContentType, ResponseOptions, URLSearchParams, XHRBackend} from '../index';
import {Json} from '../src/facade/lang';
import {stringToArrayBuffer} from '../src/http_utils';
import {MockBackend, MockConnection} from '../testing/mock_backend';
@ -460,7 +459,7 @@ export function main() {
c.mockRespond(new Response(new ResponseOptions({body: simpleObject}))));
http.get('https://www.google.com').subscribe((res: Response) => {
expect(res.arrayBuffer()).toBeAnInstanceOf(ArrayBuffer);
expect(res.text()).toEqual(Json.stringify(simpleObject));
expect(res.text()).toEqual(JSON.stringify(simpleObject, null, 2));
expect(res.json()).toBe(simpleObject);
async.done();
});
@ -500,11 +499,11 @@ export function main() {
let body = (): any => {
switch (c.request.responseType) {
case ResponseContentType.Text:
return Json.stringify(message);
return JSON.stringify(message, null, 2);
case ResponseContentType.Json:
return message;
case ResponseContentType.ArrayBuffer:
return stringToArrayBuffer(Json.stringify(message));
return stringToArrayBuffer(JSON.stringify(message, null, 2));
}
};
c.mockRespond(new Response(new ResponseOptions({body: body()})));

View File

@ -53,20 +53,20 @@ export class AngularProfiler {
* ```
*/
timeChangeDetection(config: any): ChangeDetectionPerfRecord {
var record = isPresent(config) && config['record'];
var record = config && config['record'];
var profileName = 'Change Detection';
// Profiler is not available in Android browsers, nor in IE 9 without dev tools opened
var isProfilerAvailable = isPresent(window.console.profile);
if (record && isProfilerAvailable) {
window.console.profile(profileName);
}
var start = getDOM().performanceNow();
const start = getDOM().performanceNow();
var numTicks = 0;
while (numTicks < 5 || (getDOM().performanceNow() - start) < 500) {
this.appRef.tick();
numTicks++;
}
var end = getDOM().performanceNow();
const end = getDOM().performanceNow();
if (record && isProfilerAvailable) {
// need to cast to <any> because type checker thinks there's no argument
// while in fact there is:
@ -74,9 +74,9 @@ export class AngularProfiler {
// https://developer.mozilla.org/en-US/docs/Web/API/Console/profileEnd
(<any>window.console.profileEnd)(profileName);
}
var msPerTick = (end - start) / numTicks;
const msPerTick = (end - start) / numTicks;
window.console.log(`ran ${numTicks} change detection cycles`);
window.console.log(`${NumberWrapper.toFixed(msPerTick, 2)} ms per check`);
window.console.log(`${msPerTick.toFixed(2)} ms per check`);
return new ChangeDetectionPerfRecord(msPerTick, numTicks);
}

View File

@ -7,7 +7,7 @@
*/
import {Inject, Injectable, RenderComponentType, Renderer, RootRenderer, ViewEncapsulation} from '@angular/core';
import {Json, isArray, isBlank, isPresent, isString, stringify} from '../facade/lang';
import {isBlank, isPresent, stringify} from '../facade/lang';
import {AnimationKeyframe, AnimationPlayer, AnimationStyles, RenderDebugInfo} from '../private_import_core';
import {AnimationDriver} from './animation_driver';
@ -74,7 +74,7 @@ export class DomRenderer implements Renderer {
selectRootElement(selectorOrNode: string|any, debugInfo: RenderDebugInfo): Element {
var el: any /** TODO #9100 */;
if (isString(selectorOrNode)) {
if (typeof selectorOrNode === 'string') {
el = getDOM().querySelector(this._rootRenderer.document, selectorOrNode);
if (isBlank(el)) {
throw new Error(`The selector "${selectorOrNode}" did not match any elements`);
@ -194,10 +194,11 @@ export class DomRenderer implements Renderer {
if (getDOM().isCommentNode(renderElement)) {
const existingBindings =
getDOM().getText(renderElement).replace(/\n/g, '').match(TEMPLATE_BINDINGS_EXP);
var parsedBindings = Json.parse(existingBindings[1]);
var parsedBindings = JSON.parse(existingBindings[1]);
(parsedBindings as any /** TODO #9100 */)[dashCasedPropertyName] = propertyValue;
getDOM().setText(
renderElement, TEMPLATE_COMMENT_TEXT.replace('{}', Json.stringify(parsedBindings)));
renderElement,
TEMPLATE_COMMENT_TEXT.replace('{}', JSON.stringify(parsedBindings, null, 2)));
} else {
this.setElementAttribute(renderElement, propertyName, propertyValue);
}
@ -279,9 +280,10 @@ function _shimHostAttribute(componentShortId: string): string {
}
function _flattenStyles(compId: string, styles: Array<any|any[]>, target: string[]): string[] {
for (var i = 0; i < styles.length; i++) {
var style = styles[i];
if (isArray(style)) {
for (let i = 0; i < styles.length; i++) {
let style = styles[i];
if (Array.isArray(style)) {
_flattenStyles(compId, style, target);
} else {
style = style.replace(COMPONENT_REGEX, compId);

View File

@ -8,7 +8,7 @@
import {AUTO_STYLE} from '@angular/core';
import {isNumber, isPresent} from '../facade/lang';
import {isPresent} from '../facade/lang';
import {AnimationKeyframe, AnimationStyles} from '../private_import_core';
import {AnimationDriver} from './animation_driver';
@ -83,7 +83,7 @@ function _resolveStyleUnit(
val: string | number, userProvidedProp: string, formattedProp: string): string {
var unit = '';
if (_isPixelDimensionStyle(formattedProp) && val != 0 && val != '0') {
if (isNumber(val)) {
if (typeof val === 'number') {
unit = 'px';
} else if (_findDimensionalSuffix(val.toString()).length == 0) {
throw new Error('Please provide a CSS unit value for ' + userProvidedProp + ':' + val);

View File

@ -9,7 +9,7 @@
import {NgZone} from '@angular/core';
import {ListWrapper} from './facade/collection';
import {global, isPresent, isString} from './facade/lang';
import {global, isPresent} from './facade/lang';
import {getDOM} from './private_import_platform-browser';
export class BrowserDetection {
@ -108,7 +108,7 @@ export function stringifyElement(el: any /** TODO #9100 */): string {
for (let i = 0; i < keys.length; i++) {
var key = keys[i];
var attValue = attributeMap.get(key);
if (!isString(attValue)) {
if (typeof attValue !== 'string') {
result += ` ${key}`;
} else {
result += ` ${key}="${attValue}"`;

View File

@ -7,7 +7,7 @@
*/
import {global, isString} from './facade/lang';
import {global} from './facade/lang';
import {getDOM} from './private_import_platform-browser';
@ -187,7 +187,7 @@ _global.beforeEach(function() {
return {
compare: function(actual: any, styles: {[k: string]: string}|string) {
let allPassed: boolean;
if (isString(styles)) {
if (typeof styles === 'string') {
allPassed = getDOM().hasStyle(actual, styles);
} else {
allPassed = Object.keys(styles).length !== 0;
@ -199,9 +199,9 @@ _global.beforeEach(function() {
return {
pass: allPassed,
get message() {
const expectedValueStr = isString(styles) ? styles : JSON.stringify(styles);
const expectedValueStr = typeof styles === 'string' ? styles : JSON.stringify(styles);
return `Expected ${actual.outerHTML} ${!allPassed ? ' ' : 'not '}to contain the
CSS ${isString(styles) ? 'property' : 'styles'} "${expectedValueStr}"`;
CSS ${typeof styles === 'string' ? 'property' : 'styles'} "${expectedValueStr}"`;
}
};
}

View File

@ -11,7 +11,7 @@ const parse5 = require('parse5');
import {ListWrapper} from '../src/facade/collection';
import {DomAdapter, setRootDomAdapter} from './private_import_platform-browser';
import {isPresent, isBlank, global, setValueOnPath} from '../src/facade/lang';
import {SelectorMatcher, CssSelector} from './private_import_compiler';
import {SelectorMatcher, CssSelector} from '@angular/compiler';
let treeAdapter: any;

View File

@ -1,15 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {__compiler_private__ as _c} from '@angular/compiler';
export type SelectorMatcher = typeof _c.SelectorMatcher;
export var SelectorMatcher: typeof _c.SelectorMatcher = _c.SelectorMatcher;
export type CssSelector = typeof _c.CssSelector;
export var CssSelector: typeof _c.CssSelector = _c.CssSelector;

View File

@ -8,7 +8,7 @@
import {Injectable, RenderComponentType, Type, ViewEncapsulation} from '@angular/core';
import {isArray, isPresent} from '../../facade/lang';
import {isPresent} from '../../facade/lang';
import {RenderStore} from './render_store';
import {LocationType} from './serialized_types';
@ -30,7 +30,7 @@ export class Serializer {
if (!isPresent(obj)) {
return null;
}
if (isArray(obj)) {
if (Array.isArray(obj)) {
return (<any[]>obj).map(v => this.serialize(v, type));
}
if (type == PRIMITIVE) {
@ -38,15 +38,17 @@ export class Serializer {
}
if (type == RenderStoreObject) {
return this._renderStore.serialize(obj);
} else if (type === RenderComponentType) {
return this._serializeRenderComponentType(obj);
} else if (type === ViewEncapsulation) {
return obj;
} else if (type === LocationType) {
return this._serializeLocation(obj);
} else {
throw new Error('No serializer for ' + type.toString());
}
if (type === RenderComponentType) {
return this._serializeRenderComponentType(obj);
}
if (type === ViewEncapsulation) {
return obj;
}
if (type === LocationType) {
return this._serializeLocation(obj);
}
throw new Error('No serializer for ' + type.toString());
}
deserialize(map: any, type: any, data?: any): any {

View File

@ -9,7 +9,7 @@
import {Injectable, Type} from '@angular/core';
import {EventEmitter} from '../../facade/async';
import {FunctionWrapper, isPresent} from '../../facade/lang';
import {isPresent} from '../../facade/lang';
import {MessageBus} from '../shared/message_bus';
import {Serializer} from '../shared/serializer';
@ -69,16 +69,16 @@ export class ServiceMessageBroker_ extends ServiceMessageBroker {
methodName: string, signature: Type<any>[], method: (..._: any[]) => Promise<any>| void,
returnType?: Type<any>): void {
this._methods.set(methodName, (message: ReceivedMessage) => {
var serializedArgs = message.args;
const serializedArgs = message.args;
let numArgs = signature === null ? 0 : signature.length;
var deserializedArgs: any[] = new Array(numArgs);
for (var i = 0; i < numArgs; i++) {
var serializedArg = serializedArgs[i];
const deserializedArgs: any[] = new Array(numArgs);
for (let i = 0; i < numArgs; i++) {
const serializedArg = serializedArgs[i];
deserializedArgs[i] = this._serializer.deserialize(serializedArg, signature[i]);
}
var promise = FunctionWrapper.apply(method, deserializedArgs);
if (isPresent(returnType) && isPresent(promise)) {
const promise = method(...deserializedArgs);
if (isPresent(returnType) && promise) {
this._wrapWebWorkerPromise(message.id, promise, returnType);
}
});

View File

@ -10,7 +10,6 @@ import {LocationChangeListener} from '@angular/common';
import {Injectable} from '@angular/core';
import {EventEmitter} from '../../facade/async';
import {FunctionWrapper} from '../../facade/lang';
import {BrowserPlatformLocation} from '../../private_import_platform-browser';
import {MessageBus} from '../shared/message_bus';
import {ROUTER_CHANNEL} from '../shared/messaging_api';
@ -27,30 +26,26 @@ export class MessageBasedPlatformLocation {
private _brokerFactory: ServiceMessageBrokerFactory,
private _platformLocation: BrowserPlatformLocation, bus: MessageBus,
private _serializer: Serializer) {
this._platformLocation.onPopState(
<LocationChangeListener>FunctionWrapper.bind(this._sendUrlChangeEvent, this));
this._platformLocation.onPopState(<LocationChangeListener>this._sendUrlChangeEvent.bind(this));
this._platformLocation.onHashChange(
<LocationChangeListener>FunctionWrapper.bind(this._sendUrlChangeEvent, this));
<LocationChangeListener>this._sendUrlChangeEvent.bind(this));
this._broker = this._brokerFactory.createMessageBroker(ROUTER_CHANNEL);
this._channelSink = bus.to(ROUTER_CHANNEL);
}
start(): void {
this._broker.registerMethod(
'getLocation', null, FunctionWrapper.bind(this._getLocation, this), LocationType);
this._broker.registerMethod(
'setPathname', [PRIMITIVE], FunctionWrapper.bind(this._setPathname, this));
this._broker.registerMethod('getLocation', null, this._getLocation.bind(this), LocationType);
this._broker.registerMethod('setPathname', [PRIMITIVE], this._setPathname.bind(this));
this._broker.registerMethod(
'pushState', [PRIMITIVE, PRIMITIVE, PRIMITIVE],
FunctionWrapper.bind(this._platformLocation.pushState, this._platformLocation));
this._platformLocation.pushState.bind(this._platformLocation));
this._broker.registerMethod(
'replaceState', [PRIMITIVE, PRIMITIVE, PRIMITIVE],
FunctionWrapper.bind(this._platformLocation.replaceState, this._platformLocation));
this._platformLocation.replaceState.bind(this._platformLocation));
this._broker.registerMethod(
'forward', null,
FunctionWrapper.bind(this._platformLocation.forward, this._platformLocation));
'forward', null, this._platformLocation.forward.bind(this._platformLocation));
this._broker.registerMethod(
'back', null, FunctionWrapper.bind(this._platformLocation.back, this._platformLocation));
'back', null, this._platformLocation.back.bind(this._platformLocation));
}
private _getLocation(): Promise<Location> {

Some files were not shown because too many files have changed in this diff Show More