Compare commits
26 Commits
Author | SHA1 | Date | |
---|---|---|---|
0e95a20576 | |||
9c7428e7ad | |||
d3044271dc | |||
4348c472d6 | |||
2ec05415e2 | |||
188e9ceb1f | |||
de7b554a14 | |||
1d252a559b | |||
bff5c24ed2 | |||
53197de80c | |||
12757474e6 | |||
057423e3e7 | |||
7a1b8763b2 | |||
cbd420656d | |||
f33307badb | |||
b00588a240 | |||
fb0d93d2c1 | |||
cc0fd6036d | |||
682034fcc0 | |||
0475b8d686 | |||
6be6125c5c | |||
1b7b64ae18 | |||
d284fa6bd2 | |||
8e4dca0ddf | |||
006a43f951 | |||
5469b6f64b |
4
.bazelrc
4
.bazelrc
@ -24,6 +24,10 @@ build --symlink_prefix=/
|
||||
# Performance: avoid stat'ing input files
|
||||
build --watchfs
|
||||
|
||||
# Turn off legacy external runfiles
|
||||
run --nolegacy_external_runfiles
|
||||
test --nolegacy_external_runfiles
|
||||
|
||||
###############################
|
||||
# Release support #
|
||||
###############################
|
||||
|
@ -108,8 +108,9 @@ groups:
|
||||
- "*.lock"
|
||||
- "tools/*"
|
||||
exclude:
|
||||
- "tools/public_api_guard/*"
|
||||
- "aio/*"
|
||||
- "packages/core/test/bundling/*"
|
||||
- "tools/public_api_guard/*"
|
||||
users:
|
||||
- IgorMinar #primary
|
||||
- alexeagle
|
||||
|
12
CHANGELOG.md
12
CHANGELOG.md
@ -1,3 +1,15 @@
|
||||
<a name="7.0.4"></a>
|
||||
## [7.0.4](https://github.com/angular/angular/compare/7.0.3...7.0.4) (2018-11-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-cli:** add missing tslib dependency ([#27063](https://github.com/angular/angular/issues/27063)) ([4348c47](https://github.com/angular/angular/commit/4348c47))
|
||||
* **compiler-cli:** only pass canonical genfile paths to compiler host ([#27062](https://github.com/angular/angular/issues/27062)) ([188e9ce](https://github.com/angular/angular/commit/188e9ce))
|
||||
* **router:** add `relativeLinkResolution` to `recognize` operator ([#26990](https://github.com/angular/angular/issues/26990)) ([d304427](https://github.com/angular/angular/commit/d304427)), closes [#26983](https://github.com/angular/angular/issues/26983)
|
||||
|
||||
|
||||
|
||||
<a name="7.0.3"></a>
|
||||
## [7.0.3](https://github.com/angular/angular/compare/7.0.2...7.0.3) (2018-11-07)
|
||||
|
||||
|
@ -72,15 +72,15 @@
|
||||
<h2>You submitted the following:</h2>
|
||||
<div class="row">
|
||||
<div class="col-xs-3">Name</div>
|
||||
<div class="col-xs-9 pull-left">{{ model.name }}</div>
|
||||
<div class="col-xs-9">{{ model.name }}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-3">Alter Ego</div>
|
||||
<div class="col-xs-9 pull-left">{{ model.alterEgo }}</div>
|
||||
<div class="col-xs-9">{{ model.alterEgo }}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-3">Power</div>
|
||||
<div class="col-xs-9 pull-left">{{ model.power }}</div>
|
||||
<div class="col-xs-9">{{ model.power }}</div>
|
||||
</div>
|
||||
<br>
|
||||
<button class="btn btn-primary" (click)="submitted=false">Edit</button>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<h4>Hero Search</h4>
|
||||
|
||||
<!-- #docregion input -->
|
||||
<input #searchBox id="search-box" (keyup)="search(searchBox.value)" />
|
||||
<input #searchBox id="search-box" (input)="search(searchBox.value)" />
|
||||
<!-- #enddocregion input -->
|
||||
|
||||
<ul class="search-result">
|
||||
|
@ -1,7 +1,11 @@
|
||||
// #docregion , init
|
||||
import { InMemoryDbService } from 'angular-in-memory-web-api';
|
||||
import { Hero } from './hero';
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class InMemoryDataService implements InMemoryDbService {
|
||||
createDb() {
|
||||
const heroes = [
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div id="search-component">
|
||||
<h4>Hero Search</h4>
|
||||
|
||||
<input #searchBox id="search-box" (keyup)="search(searchBox.value)" />
|
||||
<input #searchBox id="search-box" (input)="search(searchBox.value)" />
|
||||
|
||||
<ul class="search-result">
|
||||
<li *ngFor="let hero of heroes | async" >
|
||||
|
@ -48,7 +48,7 @@ Similarly, use the `@Injectable()` decorator to indicate that a component or oth
|
||||
|
||||
* An injector creates dependencies, and maintains a *container* of dependency instances that it reuses if possible.
|
||||
|
||||
* A *provider* is an object that tell an injector how to obtain or create a dependency.
|
||||
* A *provider* is an object that tells an injector how to obtain or create a dependency.
|
||||
|
||||
For any dependency that you need in your app, you must register a provider with the app's injector,
|
||||
so that the injector can use the provider to create new instances.
|
||||
|
@ -41,7 +41,7 @@ When the workspace file structure is in place, you can use the `ng generate` com
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
Besides using the CLI on the command line, you can also use an interactive development environment like [Angular Console](https://angular.console.com), or manipulate files directly in the app's source folder and configuration files.
|
||||
Besides using the CLI on the command line, you can also use an interactive development environment like [Angular Console](https://angularconsole.com/), or manipulate files directly in the app's source folder and configuration files.
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -81,7 +81,7 @@ The following support packages are included as dependencies in the default `pack
|
||||
|
||||
Package name | Description
|
||||
---------------------------------------- | --------------------------------------------------
|
||||
[**rxjs**](https://github.com/ReactiveX/rxjs) | Many Angular APIs return [_observables_](guide/glossary#observable). RxJS is an implementation of the proposed [Observables specification](https://github.com/zenparsing/es-observable) currently before the [TC39](http://www.ecma-international.org/memento/TC39.htm) committee, which determines standards for the JavaScript language.
|
||||
[**rxjs**](https://github.com/ReactiveX/rxjs) | Many Angular APIs return [_observables_](guide/glossary#observable). RxJS is an implementation of the proposed [Observables specification](https://github.com/tc39/proposal-observable) currently before the [TC39](https://www.ecma-international.org/memento/tc39-m.htm) committee, which determines standards for the JavaScript language.
|
||||
[**zone.js**](https://github.com/angular/zone.js) | Angular relies on zone.js to run Angular's change detection processes when native JavaScript operations raise events. Zone.js is an implementation of a [specification](https://gist.github.com/mhevery/63fdcdf7c65886051d55) currently before the [TC39](http://www.ecma-international.org/memento/TC39.htm) committee that determines standards for the JavaScript language.
|
||||
|
||||
|
||||
|
@ -62,7 +62,7 @@ Your app may have to launch faster to engage these users before they decide to d
|
||||
|
||||
With Angular Universal, you can generate landing pages for the app that look like the complete app.
|
||||
The pages are pure HTML, and can display even if JavaScript is disabled.
|
||||
The pages don't handle browser events, but they _do_ support navigation through the site using `[routerLink](guide/router#router-link)`.
|
||||
The pages don't handle browser events, but they _do_ support navigation through the site using [`routerLink`](guide/router#router-link).
|
||||
|
||||
In practice, you'll serve a static version of the landing page to hold the user's attention.
|
||||
At the same time, you'll load the full Angular app behind it.
|
||||
|
@ -78,7 +78,7 @@ Each target object specifies the `builder` for that target, which is the npm pac
|
||||
}
|
||||
</code-example>
|
||||
|
||||
* The `architect/build` section configures defaults for options of the `ng build` command. See [Build target]{#build-target} below for more information.
|
||||
* The `architect/build` section configures defaults for options of the `ng build` command. See [Build target](#build-target) below for more information.
|
||||
|
||||
* The `architect/serve` section overrides build defaults and supplies additional serve defaults for the `ng serve` command. In addition to the options available for the `ng build` command, it adds options related to serving the app.
|
||||
|
||||
|
@ -22,6 +22,12 @@
|
||||
"rev": true,
|
||||
"title": "Made with Angular",
|
||||
"url": "https://www.madewithangular.com/"
|
||||
},
|
||||
"angular-subreddit": {
|
||||
"desc": "An Angular-dedicated subreddit.",
|
||||
"rev": true,
|
||||
"title": "Angular Subreddit",
|
||||
"url": "https://www.reddit.com/r/Angular2/"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -149,6 +155,13 @@
|
||||
"rev": true,
|
||||
"title": "AngularCommerce",
|
||||
"url": "https://github.com/NodeArt/angular-commerce"
|
||||
},
|
||||
"ngx-api-utils": {
|
||||
"desc": "ngx-api-utils is a lean library of utilities and helpers to quickly integrate any HTTP API (REST, Ajax, and any other) with Angular.",
|
||||
"logo": "",
|
||||
"rev": true,
|
||||
"title": "ngx-api-utils",
|
||||
"url": "https://github.com/ngx-api-utils/ngx-api-utils"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -19,7 +19,7 @@ To set up your development environment, follow these instructions in [Getting St
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
**Note:**: You do not need to complete the entire Getting Started. After you complete the above two sections of Getting Started, your environment is set up. Continue below to create the Tour of Heroes workspace and an initial app project.
|
||||
**Note:** You do not need to complete the entire Getting Started. After you complete the above two sections of Getting Started, your environment is set up. Continue below to create the Tour of Heroes workspace and an initial app project.
|
||||
</div>
|
||||
|
||||
|
||||
|
@ -77,8 +77,13 @@ _after importing the `HttpClientModule`_,
|
||||
The `forRoot()` configuration method takes an `InMemoryDataService` class
|
||||
that primes the in-memory database.
|
||||
|
||||
The _Tour of Heroes_ sample creates such a class
|
||||
`src/app/in-memory-data.service.ts` which has the following content:
|
||||
The class `src/app/in-memory-data.service.ts` is generated by the following command:
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
ng generate service InMemoryData
|
||||
</code-example>
|
||||
|
||||
This class has the following content:
|
||||
|
||||
<code-example path="toh-pt6/src/app/in-memory-data.service.ts" region="init" header="src/app/in-memory-data.service.ts" linenums="false"></code-example>
|
||||
|
||||
@ -461,7 +466,7 @@ Replace the generated `HeroSearchComponent` _template_ with a text box and a lis
|
||||
Add private CSS styles to `hero-search.component.css`
|
||||
as listed in the [final code review](#herosearchcomponent) below.
|
||||
|
||||
As the user types in the search box, a *keyup* event binding calls the component's `search()`
|
||||
As the user types in the search box, an *input* event binding calls the component's `search()`
|
||||
method with the new search box value.
|
||||
|
||||
{@a asyncpipe}
|
||||
@ -511,7 +516,7 @@ You can also push values into that `Observable` by calling its `next(value)` met
|
||||
as the `search()` method does.
|
||||
|
||||
The `search()` method is called via an _event binding_ to the
|
||||
textbox's `keystroke` event.
|
||||
textbox's `input` event.
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero-search/hero-search.component.html" region="input"></code-example>
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
.cli-name {
|
||||
font-weight: bold;
|
||||
|
||||
.kwd { color: initial } /* override code format */
|
||||
}
|
||||
|
||||
.cli-option-syntax {
|
||||
|
@ -1,7 +1,7 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("//tools:defaults.bzl", "ng_rollup_bundle", "ts_library")
|
||||
load("//packages/bazel:index.bzl", "protractor_web_test")
|
||||
load("//packages/bazel:index.bzl", "protractor_web_test_suite")
|
||||
load("@build_bazel_rules_typescript//:defs.bzl", "ts_devserver")
|
||||
|
||||
ts_library(
|
||||
@ -46,7 +46,7 @@ ts_devserver(
|
||||
],
|
||||
)
|
||||
|
||||
protractor_web_test(
|
||||
protractor_web_test_suite(
|
||||
name = "perf",
|
||||
configuration = "//:protractor-perf.conf.js",
|
||||
data = [
|
||||
@ -60,8 +60,6 @@ protractor_web_test(
|
||||
on_prepare = ":protractor.on-prepare.js",
|
||||
server = ":devserver",
|
||||
tags = [
|
||||
"fixme-ivy-aot",
|
||||
"fixme-ivy-jit",
|
||||
"ivy-only",
|
||||
],
|
||||
deps = [
|
||||
|
@ -1,7 +1,7 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("//tools:defaults.bzl", "ng_module", "ng_rollup_bundle")
|
||||
load("//packages/bazel:index.bzl", "protractor_web_test")
|
||||
load("//packages/bazel:index.bzl", "protractor_web_test_suite")
|
||||
load("@build_bazel_rules_typescript//:defs.bzl", "ts_devserver")
|
||||
|
||||
ng_module(
|
||||
@ -46,7 +46,7 @@ ts_devserver(
|
||||
tags = ["ivy-only"],
|
||||
)
|
||||
|
||||
protractor_web_test(
|
||||
protractor_web_test_suite(
|
||||
name = "perf",
|
||||
configuration = "//:protractor-perf.conf.js",
|
||||
data = [
|
||||
@ -56,8 +56,6 @@ protractor_web_test(
|
||||
on_prepare = ":protractor.on_prepare.js",
|
||||
server = ":devserver",
|
||||
tags = [
|
||||
"fixme-ivy-aot",
|
||||
"fixme-ivy-jit",
|
||||
"ivy-only",
|
||||
],
|
||||
deps = [
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "7.0.3",
|
||||
"version": "7.0.4",
|
||||
"private": true,
|
||||
"branchPattern": "2.0.*",
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
@ -30,6 +30,7 @@
|
||||
"test-fixme-ivy-jit": "bazel test --define=compile=jit --build_tag_filters=-no-ivy-jit --test_tag_filters=-no-ivy-jit",
|
||||
"test-ivy-aot": "bazel test --define=compile=aot --build_tag_filters=-no-ivy-aot,-fixme-ivy-aot --test_tag_filters=-no-ivy-aot,-fixme-ivy-aot",
|
||||
"test-fixme-ivy-aot": "bazel test --define=compile=aot --build_tag_filters=-no-ivy-aot --test_tag_filters=-no-ivy-aot",
|
||||
"list-fixme-ivy-targets": "bazel query --output=label 'attr(\"tags\", \"\\[.*fixme-ivy.*\\]\", //...) except kind(\"sh_binary\", //...) except kind(\"devmode_js_sources\", //...)' | sort",
|
||||
"bazel": "bazel"
|
||||
},
|
||||
"// 1": "dependencies are used locally and by bazel",
|
||||
|
@ -5,9 +5,6 @@
|
||||
* 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
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
|
||||
const DEBUG = false;
|
||||
|
||||
const configPath = 'TMPL_config';
|
||||
@ -32,15 +29,44 @@ function setConf(conf, name, value, msg) {
|
||||
conf[name] = value;
|
||||
}
|
||||
|
||||
function mergeCapabilities(conf, capabilities) {
|
||||
if (conf.capabilities) {
|
||||
if (conf.capabilities.browserName === capabilities.browserName) {
|
||||
// there are capabilities to merge
|
||||
if (capabilities.browserName === 'chrome') {
|
||||
conf.capabilities.chromeOptions = conf.capabilities.chromeOptions || {};
|
||||
conf.capabilities.chromeOptions.binary = capabilities.chromeOptions.binary;
|
||||
conf.capabilities.chromeOptions.args = conf.capabilities.chromeOptions.args || [];
|
||||
conf.capabilities.chromeOptions.args.push(...capabilities.chromeOptions.args);
|
||||
console.warn(
|
||||
`Your protractor configuration specifies capabilities for browser '${conf.capabilities.browserName}'
|
||||
which will be merged with capabilities provided by Bazel resulting in:`,
|
||||
JSON.stringify(conf.capabilities, null, 2));
|
||||
} else {
|
||||
// TODO(gmagolan): implement firefox support for protractor
|
||||
throw new Error(
|
||||
`Unexpected browserName ${capabilities.browserName} for capabilities merging`);
|
||||
}
|
||||
} else {
|
||||
console.warn(
|
||||
`Your protractor configuration specifies capabilities for browser '${conf.capabilities.browserName}' which will be overwritten by Bazel`);
|
||||
conf.capabilities = capabilities;
|
||||
}
|
||||
} else {
|
||||
conf.capabilities = capabilities;
|
||||
}
|
||||
}
|
||||
|
||||
let conf = {};
|
||||
|
||||
// Import the user's base protractor configuration if specified
|
||||
if (configPath) {
|
||||
const baseConf = require(configPath);
|
||||
if (!baseConf.config) {
|
||||
throw new Error('Invalid base protractor configration. Expected config to be exported.');
|
||||
throw new Error('Invalid base protractor configuration. Expected config to be exported.');
|
||||
}
|
||||
conf = baseConf.config;
|
||||
if (DEBUG) console.info(`Base protractor configuration: ${JSON.stringify(conf, null, 2)}`);
|
||||
}
|
||||
|
||||
// Import the user's on prepare function if specified
|
||||
@ -109,8 +135,8 @@ if (process.env['WEB_TEST_METADATA']) {
|
||||
}
|
||||
setConf(conf, 'directConnect', true, 'is set to true for chrome');
|
||||
setConf(conf, 'chromeDriver', chromeDriver, 'is determined by the browsers attribute');
|
||||
setConf(
|
||||
conf, 'capabilities', {
|
||||
mergeCapabilities(
|
||||
conf, {
|
||||
browserName: 'chrome',
|
||||
chromeOptions: {
|
||||
binary: chromeBin,
|
||||
@ -131,7 +157,7 @@ if (process.env['WEB_TEST_METADATA']) {
|
||||
// }
|
||||
// setConf(conf, 'seleniumAddress', process.env.WEB_TEST_HTTP_SERVER.trim() + "/wd/hub", 'is
|
||||
// configured by Bazel for firefox browser')
|
||||
// setConf(conf, 'capabilities', {
|
||||
// mergeCapabilities(conf, {
|
||||
// browserName: "firefox",
|
||||
// 'moz:firefoxOptions': {
|
||||
// binary: firefoxBin,
|
||||
|
@ -70,7 +70,7 @@ export function createTsConfig(options: TsConfigOptions) {
|
||||
'tsickleExternsPath': '',
|
||||
// we don't copy the node_modules into our tmp dir, so we should look in
|
||||
// the original workspace directory for it
|
||||
'nodeModulesPrefix': '../angular/external/ngdeps/node_modules',
|
||||
'nodeModulesPrefix': '../ngdeps/node_modules',
|
||||
},
|
||||
'files': options.files,
|
||||
'angularCompilerOptions': {
|
||||
|
@ -19,6 +19,7 @@
|
||||
"magic-string": "^0.25.0",
|
||||
"shelljs": "^0.8.1",
|
||||
"source-map": "^0.6.1",
|
||||
"tslib": "^1.9.0",
|
||||
"yargs": "9.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
@ -974,10 +974,8 @@ function normalizeSeparators(path: string): string {
|
||||
* TODO(tbosch): talk to the TypeScript team to expose their logic for calculating the `rootDir`
|
||||
* if none was specified.
|
||||
*
|
||||
* Note: This function works on normalized paths from typescript.
|
||||
*
|
||||
* @param outDir
|
||||
* @param outSrcMappings
|
||||
* Note: This function works on normalized paths from typescript but should always return
|
||||
* POSIX normalized paths for output paths.
|
||||
*/
|
||||
export function createSrcToOutPathMapper(
|
||||
outDir: string | undefined, sampleSrcFileName: string | undefined,
|
||||
@ -986,7 +984,6 @@ export function createSrcToOutPathMapper(
|
||||
resolve: typeof path.resolve,
|
||||
relative: typeof path.relative
|
||||
} = path): (srcFileName: string) => string {
|
||||
let srcToOutPath: (srcFileName: string) => string;
|
||||
if (outDir) {
|
||||
let path: {} = {}; // Ensure we error if we use `path` instead of `host`.
|
||||
if (sampleSrcFileName == null || sampleOutFileName == null) {
|
||||
@ -1006,11 +1003,18 @@ export function createSrcToOutPathMapper(
|
||||
srcDirParts[srcDirParts.length - 1 - i] === outDirParts[outDirParts.length - 1 - i])
|
||||
i++;
|
||||
const rootDir = srcDirParts.slice(0, srcDirParts.length - i).join('/');
|
||||
srcToOutPath = (srcFileName) => host.resolve(outDir, host.relative(rootDir, srcFileName));
|
||||
return (srcFileName) => {
|
||||
// Note: Before we return the mapped output path, we need to normalize the path delimiters
|
||||
// because the output path is usually passed to TypeScript which sometimes only expects
|
||||
// posix normalized paths (e.g. if a custom compiler host is used)
|
||||
return normalizeSeparators(host.resolve(outDir, host.relative(rootDir, srcFileName)));
|
||||
};
|
||||
} else {
|
||||
srcToOutPath = (srcFileName) => srcFileName;
|
||||
// Note: Before we return the output path, we need to normalize the path delimiters because
|
||||
// the output path is usually passed to TypeScript which only passes around posix
|
||||
// normalized paths (e.g. if a custom compiler host is used)
|
||||
return (srcFileName) => normalizeSeparators(srcFileName);
|
||||
}
|
||||
return srcToOutPath;
|
||||
}
|
||||
|
||||
export function i18nExtract(
|
||||
|
@ -136,7 +136,7 @@ export function setupBazelTo(basePath: string) {
|
||||
}
|
||||
|
||||
// Link typescript
|
||||
const typescriptSource = path.join(sources, 'angular/external/ngdeps/node_modules/typescript');
|
||||
const typescriptSource = path.join(sources, 'ngdeps/node_modules/typescript');
|
||||
const typescriptDest = path.join(nodeModulesPath, 'typescript');
|
||||
if (fs.existsSync(typescriptSource)) {
|
||||
fs.symlinkSync(typescriptSource, typescriptDest);
|
||||
|
@ -604,13 +604,13 @@ describe('ng program', () => {
|
||||
it('should work on windows with normalized paths', () => {
|
||||
const mapper =
|
||||
createSrcToOutPathMapper('c:/tmp/out', 'c:/tmp/a/x.ts', 'c:/tmp/out/a/x.js', path.win32);
|
||||
expect(mapper('c:/tmp/b/y.js')).toBe('c:\\tmp\\out\\b\\y.js');
|
||||
expect(mapper('c:/tmp/b/y.js')).toBe('c:/tmp/out/b/y.js');
|
||||
});
|
||||
|
||||
it('should work on windows with non-normalized paths', () => {
|
||||
const mapper = createSrcToOutPathMapper(
|
||||
'c:\\tmp\\out', 'c:\\tmp\\a\\x.ts', 'c:\\tmp\\out\\a\\x.js', path.win32);
|
||||
expect(mapper('c:\\tmp\\b\\y.js')).toBe('c:\\tmp\\out\\b\\y.js');
|
||||
expect(mapper('c:\\tmp\\b\\y.js')).toBe('c:/tmp/out/b/y.js');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -134,6 +134,7 @@ export interface Directive {
|
||||
* id: string;
|
||||
*
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
inputs?: string[];
|
||||
|
||||
@ -168,6 +169,7 @@ export interface Directive {
|
||||
* class MainComponent {
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
outputs?: string[];
|
||||
|
||||
@ -199,6 +201,7 @@ export interface Directive {
|
||||
* class MainComponent {
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
exportAs?: string;
|
||||
|
||||
@ -434,6 +437,67 @@ export interface ComponentDecorator {
|
||||
*
|
||||
* ```
|
||||
*
|
||||
* ### Preserving whitespace
|
||||
*
|
||||
* Removing whitespace can greatly reduce AOT-generated code size and speed up view creation.
|
||||
* As of Angular 6, the default for `preserveWhitespaces` is false (whitespace is removed).
|
||||
* To change the default setting for all components in your application, set
|
||||
* the `preserveWhitespaces` option of the AOT compiler.
|
||||
*
|
||||
* By default, the AOT compiler removes whitespace characters as follows:
|
||||
* * Trims all whitespaces at the beginning and the end of a template.
|
||||
* * Removes whitespace-only text nodes. For example,
|
||||
*
|
||||
* ```
|
||||
* <button>Action 1</button> <button>Action 2</button>
|
||||
* ```
|
||||
*
|
||||
* becomes:
|
||||
*
|
||||
* ```
|
||||
* <button>Action 1</button><button>Action 2</button>
|
||||
* ```
|
||||
*
|
||||
* * Replaces a series of whitespace characters in text nodes with a single space.
|
||||
* For example, `<span>\n some text\n</span>` becomes `<span> some text </span>`.
|
||||
* * Does NOT alter text nodes inside HTML tags such as `<pre>` or `<textarea>`,
|
||||
* where whitespace characters are significant.
|
||||
*
|
||||
* Note that these transformations can influence DOM nodes layout, although impact
|
||||
* should be minimal.
|
||||
*
|
||||
* You can override the default behavior to preserve whitespace characters
|
||||
* in certain fragments of a template. For example, you can exclude an entire
|
||||
* DOM sub-tree by using the `ngPreserveWhitespaces` attribute:
|
||||
*
|
||||
* ```html
|
||||
* <div ngPreserveWhitespaces>
|
||||
* whitespaces are preserved here
|
||||
* <span> and here </span>
|
||||
* </div>
|
||||
* ```
|
||||
*
|
||||
* You can force a single space to be preserved in a text node by using `&ngsp;`,
|
||||
* which is replaced with a space character by Angular's template
|
||||
* compiler:
|
||||
*
|
||||
* ```html
|
||||
* <a>Spaces</a>&ngsp;<a>between</a>&ngsp;<a>links.</a>
|
||||
* <!-->compiled to be equivalent to:</>
|
||||
* <a>Spaces</a> <a>between</a> <a>links.</a>
|
||||
* ```
|
||||
*
|
||||
* Note that sequences of `&ngsp;` are still collapsed to just one space character when
|
||||
* the `preserveWhitespaces` option is set to `false`.
|
||||
*
|
||||
* ```html
|
||||
* <a>before</a>&ngsp;&ngsp;&ngsp;<a>after</a>
|
||||
* <!-->compiled to be equivalent to:</>
|
||||
* <a>Spaces</a> <a>between</a> <a>links.</a>
|
||||
* ```
|
||||
*
|
||||
* To preserve sequences of whitespace characters, use the
|
||||
* `ngPreserveWhitespaces` attribute.
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
@ -551,89 +615,6 @@ export interface Component extends Directive {
|
||||
/**
|
||||
* Component decorator and metadata.
|
||||
*
|
||||
* @usageNotes
|
||||
*
|
||||
* ### Using animations
|
||||
*
|
||||
* The following snippet shows an animation trigger in a component's
|
||||
* metadata. The trigger is attached to an element in the component's
|
||||
* template, using "@_trigger_name_", and a state expression that is evaluated
|
||||
* at run time to determine whether the animation should start.
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: 'animation-cmp',
|
||||
* templateUrl: 'animation-cmp.html',
|
||||
* animations: [
|
||||
* trigger('myTriggerName', [
|
||||
* state('on', style({ opacity: 1 }),
|
||||
* state('off', style({ opacity: 0 }),
|
||||
* transition('on => off', [
|
||||
* animate("1s")
|
||||
* ])
|
||||
* ])
|
||||
* ]
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* ```html
|
||||
* <!-- animation-cmp.html -->
|
||||
* <div @myTriggerName="expression">...</div>
|
||||
* ```
|
||||
*
|
||||
* ### Preserving whitespace
|
||||
*
|
||||
* Removing whitespace can greatly reduce AOT-generated code size, and speed up view creation.
|
||||
* As of Angular 6, default for `preserveWhitespaces` is false (whitespace is removed).
|
||||
* To change the default setting for all components in your application, set
|
||||
* the `preserveWhitespaces` option of the AOT compiler.
|
||||
*
|
||||
* Current implementation removes whitespace characters as follows:
|
||||
* - Trims all whitespaces at the beginning and the end of a template.
|
||||
* - Removes whitespace-only text nodes. For example,
|
||||
* `<button>Action 1</button> <button>Action 2</button>` becomes
|
||||
* `<button>Action 1</button><button>Action 2</button>`.
|
||||
* - Replaces a series of whitespace characters in text nodes with a single space.
|
||||
* For example, `<span>\n some text\n</span>` becomes `<span> some text </span>`.
|
||||
* - Does NOT alter text nodes inside HTML tags such as `<pre>` or `<textarea>`,
|
||||
* where whitespace characters are significant.
|
||||
*
|
||||
* Note that these transformations can influence DOM nodes layout, although impact
|
||||
* should be minimal.
|
||||
*
|
||||
* You can override the default behavior to preserve whitespace characters
|
||||
* in certain fragments of a template. For example, you can exclude an entire
|
||||
* DOM sub-tree by using the `ngPreserveWhitespaces` attribute:
|
||||
*
|
||||
* ```html
|
||||
* <div ngPreserveWhitespaces>
|
||||
* whitespaces are preserved here
|
||||
* <span> and here </span>
|
||||
* </div>
|
||||
* ```
|
||||
*
|
||||
* You can force a single space to be preserved in a text node by using `&ngsp;`,
|
||||
* which is replaced with a space character by Angular's template
|
||||
* compiler:
|
||||
*
|
||||
* ```html
|
||||
* <a>Spaces</a>&ngsp;<a>between</a>&ngsp;<a>links.</a>
|
||||
* <!-->compiled to be equivalent to:</>
|
||||
* <a>Spaces</a> <a>between</a> <a>links.</a>
|
||||
* ```
|
||||
*
|
||||
* Note that sequences of `&ngsp;` are still collapsed to just one space character when
|
||||
* the `preserveWhitespaces` option is set to `false`.
|
||||
*
|
||||
* ```html
|
||||
* <a>before</a>&ngsp;&ngsp;&ngsp;<a>after</a>
|
||||
* <!-->compiled to be equivalent to:</>
|
||||
* <a>Spaces</a> <a>between</a> <a>links.</a>
|
||||
* ```
|
||||
*
|
||||
* To preserve sequences of whitespace characters, use the
|
||||
* `ngPreserveWhitespaces` attribute.
|
||||
*
|
||||
* @Annotation
|
||||
* @publicApi
|
||||
*/
|
||||
@ -766,6 +747,7 @@ export interface Input {
|
||||
*
|
||||
* class App {}
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
bindingPropertyName?: string;
|
||||
}
|
||||
@ -885,6 +867,7 @@ export interface HostBindingDecorator {
|
||||
* prop;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
(hostPropertyName?: string): any;
|
||||
new (hostPropertyName?: string): any;
|
||||
|
@ -15,7 +15,6 @@ class ChildDirective {
|
||||
|
||||
@Directive({selector: 'someDir'})
|
||||
class SomeDir implements AfterContentInit {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChild(ChildDirective) contentChild !: ChildDirective;
|
||||
|
||||
ngAfterContentInit() {
|
||||
|
@ -23,9 +23,7 @@ export class Pane {
|
||||
`
|
||||
})
|
||||
export class Tab {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChildren(Pane) topLevelPanes !: QueryList<Pane>;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChildren(Pane, {descendants: true}) arbitraryNestedPanes !: QueryList<Pane>;
|
||||
|
||||
get serializedPanes(): string {
|
||||
|
@ -17,17 +17,26 @@ export const CHECKBOX_VALUE_ACCESSOR: any = {
|
||||
};
|
||||
|
||||
/**
|
||||
* The accessor for writing a value and listening to changes on a checkbox input element.
|
||||
* @description
|
||||
* A `ControlValueAccessor` for writing a value and listening to changes on a checkbox input
|
||||
* element.
|
||||
*
|
||||
* @usageNotes
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* <input type="checkbox" name="rememberLogin" ngModel>
|
||||
* ### Using a checkbox with a reactive form.
|
||||
*
|
||||
* The following example shows how to use a checkbox with a reactive form.
|
||||
*
|
||||
* ```ts
|
||||
* const rememberLoginControl = new FormControl();
|
||||
* ```
|
||||
*
|
||||
* ```
|
||||
* <input type="checkbox" [formControl]="rememberLoginControl">
|
||||
* ```
|
||||
*
|
||||
* @ngModule FormsModule
|
||||
* @ngModule ReactiveFormsModule
|
||||
* @ngModule FormsModule
|
||||
* @publicApi
|
||||
*/
|
||||
@Directive({
|
||||
@ -37,17 +46,50 @@ export const CHECKBOX_VALUE_ACCESSOR: any = {
|
||||
providers: [CHECKBOX_VALUE_ACCESSOR]
|
||||
})
|
||||
export class CheckboxControlValueAccessor implements ControlValueAccessor {
|
||||
/**
|
||||
* @description
|
||||
* The registered callback function called when a change event occurs on the input element.
|
||||
*/
|
||||
onChange = (_: any) => {};
|
||||
|
||||
/**
|
||||
* @description
|
||||
* The registered callback function called when a blur event occurs on the input element.
|
||||
*/
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(private _renderer: Renderer2, private _elementRef: ElementRef) {}
|
||||
|
||||
/**
|
||||
* Sets the "checked" property on the input element.
|
||||
*
|
||||
* @param value The checked value
|
||||
*/
|
||||
writeValue(value: any): void {
|
||||
this._renderer.setProperty(this._elementRef.nativeElement, 'checked', value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Registers a function called when the control value changes.
|
||||
*
|
||||
* @param fn The callback function
|
||||
*/
|
||||
registerOnChange(fn: (_: any) => {}): void { this.onChange = fn; }
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Registers a function called when the control is touched.
|
||||
*
|
||||
* @param fn The callback function
|
||||
*/
|
||||
registerOnTouched(fn: () => {}): void { this.onTouched = fn; }
|
||||
|
||||
/**
|
||||
* Sets the "disabled" property on the input element.
|
||||
*
|
||||
* @param isDisabled The disabled value
|
||||
*/
|
||||
setDisabledState(isDisabled: boolean): void {
|
||||
this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled);
|
||||
}
|
||||
|
@ -32,18 +32,28 @@ function _isAndroid(): boolean {
|
||||
export const COMPOSITION_BUFFER_MODE = new InjectionToken<boolean>('CompositionEventMode');
|
||||
|
||||
/**
|
||||
* The default accessor for writing a value and listening to changes that is used by the
|
||||
* `NgModel`, `FormControlDirective`, and `FormControlName` directives.
|
||||
* @description
|
||||
* The default `ControlValueAccessor` for writing a value and listening to changes on input
|
||||
* elements. The accessor is used by the `FormControlDirective`, `FormControlName`, and
|
||||
* `NgModel` directives.
|
||||
*
|
||||
* @usageNotes
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* <input type="text" name="searchQuery" ngModel>
|
||||
* ### Using the default value accessor
|
||||
*
|
||||
* The following example shows how to use an input element that activates the default value accessor
|
||||
* (in this case, a text field).
|
||||
*
|
||||
* ```ts
|
||||
* const firstNameControl = new FormControl();
|
||||
* ```
|
||||
*
|
||||
* ```
|
||||
* <input type="text" [formControl]="firstNameControl">
|
||||
* ```
|
||||
*
|
||||
* @ngModule FormsModule
|
||||
* @ngModule ReactiveFormsModule
|
||||
* @ngModule FormsModule
|
||||
* @publicApi
|
||||
*/
|
||||
@Directive({
|
||||
@ -61,7 +71,16 @@ export const COMPOSITION_BUFFER_MODE = new InjectionToken<boolean>('CompositionE
|
||||
providers: [DEFAULT_VALUE_ACCESSOR]
|
||||
})
|
||||
export class DefaultValueAccessor implements ControlValueAccessor {
|
||||
/**
|
||||
* @description
|
||||
* The registered callback function called when an input event occurs on the input element.
|
||||
*/
|
||||
onChange = (_: any) => {};
|
||||
|
||||
/**
|
||||
* @description
|
||||
* The registered callback function called when a blur event occurs on the input element.
|
||||
*/
|
||||
onTouched = () => {};
|
||||
|
||||
/** Whether the user is creating a composition string (IME events). */
|
||||
@ -75,14 +94,37 @@ export class DefaultValueAccessor implements ControlValueAccessor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the "value" property on the input element.
|
||||
*
|
||||
* @param value The checked value
|
||||
*/
|
||||
writeValue(value: any): void {
|
||||
const normalizedValue = value == null ? '' : value;
|
||||
this._renderer.setProperty(this._elementRef.nativeElement, 'value', normalizedValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Registers a function called when the control value changes.
|
||||
*
|
||||
* @param fn The callback function
|
||||
*/
|
||||
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Registers a function called when the control is touched.
|
||||
*
|
||||
* @param fn The callback function
|
||||
*/
|
||||
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
|
||||
|
||||
/**
|
||||
* Sets the "disabled" property on the input element.
|
||||
*
|
||||
* @param isDisabled The disabled value
|
||||
*/
|
||||
setDisabledState(isDisabled: boolean): void {
|
||||
this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled);
|
||||
}
|
||||
|
@ -17,18 +17,27 @@ export const NUMBER_VALUE_ACCESSOR: any = {
|
||||
};
|
||||
|
||||
/**
|
||||
* The accessor for writing a number value and listening to changes that is used by the
|
||||
* `NgModel`, `FormControlDirective`, and `FormControlName` directives.
|
||||
* @description
|
||||
* The `ControlValueAccessor` for writing a number value and listening to number input changes.
|
||||
* The value accessor is used by the `FormControlDirective`, `FormControlName`, and `NgModel`
|
||||
* directives.
|
||||
*
|
||||
* @usageNotes
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* <input type="number" [(ngModel)]="age">
|
||||
* ### Using a number input with a reactive form.
|
||||
*
|
||||
* The following example shows how to use a number input with a reactive form.
|
||||
*
|
||||
* ```ts
|
||||
* const totalCountControl = new FormControl();
|
||||
* ```
|
||||
*
|
||||
* ```
|
||||
* <input type="number" [formControl]="totalCountControl">
|
||||
* ```
|
||||
*
|
||||
* @ngModule FormsModule
|
||||
* @ngModule ReactiveFormsModule
|
||||
* @ngModule FormsModule
|
||||
*/
|
||||
@Directive({
|
||||
selector:
|
||||
@ -41,22 +50,55 @@ export const NUMBER_VALUE_ACCESSOR: any = {
|
||||
providers: [NUMBER_VALUE_ACCESSOR]
|
||||
})
|
||||
export class NumberValueAccessor implements ControlValueAccessor {
|
||||
/**
|
||||
* @description
|
||||
* The registered callback function called when a change or input event occurs on the input
|
||||
* element.
|
||||
*/
|
||||
onChange = (_: any) => {};
|
||||
|
||||
/**
|
||||
* @description
|
||||
* The registered callback function called when a blur event occurs on the input element.
|
||||
*/
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(private _renderer: Renderer2, private _elementRef: ElementRef) {}
|
||||
|
||||
/**
|
||||
* Sets the "value" property on the input element.
|
||||
*
|
||||
* @param value The checked value
|
||||
*/
|
||||
writeValue(value: number): void {
|
||||
// The value needs to be normalized for IE9, otherwise it is set to 'null' when null
|
||||
const normalizedValue = value == null ? '' : value;
|
||||
this._renderer.setProperty(this._elementRef.nativeElement, 'value', normalizedValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Registers a function called when the control value changes.
|
||||
*
|
||||
* @param fn The callback function
|
||||
*/
|
||||
registerOnChange(fn: (_: number|null) => void): void {
|
||||
this.onChange = (value) => { fn(value == '' ? null : parseFloat(value)); };
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Registers a function called when the control is touched.
|
||||
*
|
||||
* @param fn The callback function
|
||||
*/
|
||||
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
|
||||
|
||||
/**
|
||||
* Sets the "disabled" property on the input element.
|
||||
*
|
||||
* @param isDisabled The disabled value
|
||||
*/
|
||||
setDisabledState(isDisabled: boolean): void {
|
||||
this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled);
|
||||
}
|
||||
|
@ -18,16 +18,25 @@ export const RADIO_VALUE_ACCESSOR: any = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal class used by Angular to uncheck radio buttons with the matching name.
|
||||
* @description
|
||||
* Class used by Angular to track radio buttons. For internal use only.
|
||||
*/
|
||||
@Injectable()
|
||||
export class RadioControlRegistry {
|
||||
private _accessors: any[] = [];
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Adds a control to the internal registry. For internal use only.
|
||||
*/
|
||||
add(control: NgControl, accessor: RadioControlValueAccessor) {
|
||||
this._accessors.push([control, accessor]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Removes a control from the internal registry. For internal use only.
|
||||
*/
|
||||
remove(accessor: RadioControlValueAccessor) {
|
||||
for (let i = this._accessors.length - 1; i >= 0; --i) {
|
||||
if (this._accessors[i][1] === accessor) {
|
||||
@ -37,6 +46,10 @@ export class RadioControlRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Selects a radio button. For internal use only.
|
||||
*/
|
||||
select(accessor: RadioControlValueAccessor) {
|
||||
this._accessors.forEach((c) => {
|
||||
if (this._isSameGroup(c, accessor) && c[1] !== accessor) {
|
||||
@ -56,32 +69,22 @@ export class RadioControlRegistry {
|
||||
|
||||
/**
|
||||
* @description
|
||||
*
|
||||
* Writes radio control values and listens to radio control changes.
|
||||
*
|
||||
* Used by `NgModel`, `FormControlDirective`, and `FormControlName`
|
||||
* to keep the view synced with the `FormControl` model.
|
||||
*
|
||||
* If you have imported the `FormsModule` or the `ReactiveFormsModule`, this
|
||||
* value accessor will be active on any radio control that has a form directive. You do
|
||||
* **not** need to add a special selector to activate it.
|
||||
* The `ControlValueAccessor` for writing radio control values and listening to radio control
|
||||
* changes. The value accessor is used by the `FormControlDirective`, `FormControlName`, and
|
||||
* `NgModel` directives.
|
||||
*
|
||||
* @usageNotes
|
||||
* ### How to use radio buttons with form directives
|
||||
*
|
||||
* To use radio buttons in a template-driven form, you'll want to ensure that radio buttons
|
||||
* in the same group have the same `name` attribute. Radio buttons with different `name`
|
||||
* attributes do not affect each other.
|
||||
* ### Using radio buttons with reactive form directives
|
||||
*
|
||||
* {@example forms/ts/radioButtons/radio_button_example.ts region='TemplateDriven'}
|
||||
*
|
||||
* When using radio buttons in a reactive form, radio buttons in the same group should have the
|
||||
* same `formControlName`. You can also add a `name` attribute, but it's optional.
|
||||
* The follow example shows how to use radio buttons in a reactive form. When using radio buttons in
|
||||
* a reactive form, radio buttons in the same group should have the same `formControlName`.
|
||||
* Providing a `name` attribute is optional.
|
||||
*
|
||||
* {@example forms/ts/reactiveRadioButtons/reactive_radio_button_example.ts region='Reactive'}
|
||||
*
|
||||
* @ngModule FormsModule
|
||||
* @ngModule ReactiveFormsModule
|
||||
* @ngModule FormsModule
|
||||
* @publicApi
|
||||
*/
|
||||
@Directive({
|
||||
@ -101,32 +104,81 @@ export class RadioControlValueAccessor implements ControlValueAccessor,
|
||||
/** @internal */
|
||||
// TODO(issue/24571): remove '!'.
|
||||
_fn !: Function;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* The registered callback function called when a change event occurs on the input element.
|
||||
*/
|
||||
onChange = () => {};
|
||||
|
||||
/**
|
||||
* @description
|
||||
* The registered callback function called when a blur event occurs on the input element.
|
||||
*/
|
||||
onTouched = () => {};
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Tracks the name of the radio input element.
|
||||
*/
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() name !: string;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Tracks the name of the `FormControl` bound to the directive. The name corresponds
|
||||
* to a key in the parent `FormGroup` or `FormArray`.
|
||||
*/
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() formControlName !: string;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Tracks the value of the radio input element
|
||||
*/
|
||||
@Input() value: any;
|
||||
|
||||
constructor(
|
||||
private _renderer: Renderer2, private _elementRef: ElementRef,
|
||||
private _registry: RadioControlRegistry, private _injector: Injector) {}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* A lifecycle method called when the directive is initialized. For internal use only.
|
||||
*
|
||||
* @param changes A object of key/value pairs for the set of changed inputs.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this._control = this._injector.get(NgControl);
|
||||
this._checkName();
|
||||
this._registry.add(this._control, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Lifecycle method called before the directive's instance is destroyed. For internal use only.
|
||||
*
|
||||
* @param changes A object of key/value pairs for the set of changed inputs.
|
||||
*/
|
||||
ngOnDestroy(): void { this._registry.remove(this); }
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Sets the "checked" property value on the radio input element.
|
||||
*
|
||||
* @param value The checked value
|
||||
*/
|
||||
writeValue(value: any): void {
|
||||
this._state = value === this.value;
|
||||
this._renderer.setProperty(this._elementRef.nativeElement, 'checked', this._state);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Registers a function called when the control value changes.
|
||||
*
|
||||
* @param fn The callback function
|
||||
*/
|
||||
registerOnChange(fn: (_: any) => {}): void {
|
||||
this._fn = fn;
|
||||
this.onChange = () => {
|
||||
@ -135,10 +187,26 @@ export class RadioControlValueAccessor implements ControlValueAccessor,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the "value" on the radio input element and unchecks it.
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
fireUncheck(value: any): void { this.writeValue(value); }
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Registers a function called when the control is touched.
|
||||
*
|
||||
* @param fn The callback function
|
||||
*/
|
||||
registerOnTouched(fn: () => {}): void { this.onTouched = fn; }
|
||||
|
||||
/**
|
||||
* Sets the "disabled" property on the input element.
|
||||
*
|
||||
* @param isDisabled The disabled value
|
||||
*/
|
||||
setDisabledState(isDisabled: boolean): void {
|
||||
this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled);
|
||||
}
|
||||
|
@ -17,18 +17,27 @@ export const RANGE_VALUE_ACCESSOR: StaticProvider = {
|
||||
};
|
||||
|
||||
/**
|
||||
* The accessor for writing a range value and listening to changes that is used by the
|
||||
* `NgModel`, `FormControlDirective`, and `FormControlName` directives.
|
||||
* @description
|
||||
* The `ControlValueAccessor` for writing a range value and listening to range input changes.
|
||||
* The value accessor is used by the `FormControlDirective`, `FormControlName`, and `NgModel`
|
||||
* directives.
|
||||
*
|
||||
* @usageNotes
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* <input type="range" [(ngModel)]="age" >
|
||||
* ### Using a range input with a reactive form
|
||||
*
|
||||
* The following example shows how to use a range input with a reactive form.
|
||||
*
|
||||
* ```ts
|
||||
* const ageControl = new FormControl();
|
||||
* ```
|
||||
*
|
||||
* ```
|
||||
* <input type="range" [formControl]="ageControl">
|
||||
* ```
|
||||
*
|
||||
* @ngModule FormsModule
|
||||
* @ngModule ReactiveFormsModule
|
||||
* @ngModule FormsModule
|
||||
*/
|
||||
@Directive({
|
||||
selector:
|
||||
@ -41,21 +50,53 @@ export const RANGE_VALUE_ACCESSOR: StaticProvider = {
|
||||
providers: [RANGE_VALUE_ACCESSOR]
|
||||
})
|
||||
export class RangeValueAccessor implements ControlValueAccessor {
|
||||
/**
|
||||
* @description
|
||||
* The registered callback function called when a change or input event occurs on the input
|
||||
* element.
|
||||
*/
|
||||
onChange = (_: any) => {};
|
||||
|
||||
/**
|
||||
* @description
|
||||
* The registered callback function called when a blur event occurs on the input element.
|
||||
*/
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(private _renderer: Renderer2, private _elementRef: ElementRef) {}
|
||||
|
||||
/**
|
||||
* Sets the "value" property on the input element.
|
||||
*
|
||||
* @param value The checked value
|
||||
*/
|
||||
writeValue(value: any): void {
|
||||
this._renderer.setProperty(this._elementRef.nativeElement, 'value', parseFloat(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Registers a function called when the control value changes.
|
||||
*
|
||||
* @param fn The callback function
|
||||
*/
|
||||
registerOnChange(fn: (_: number|null) => void): void {
|
||||
this.onChange = (value) => { fn(value == '' ? null : parseFloat(value)); };
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Registers a function called when the control is touched.
|
||||
*
|
||||
* @param fn The callback function
|
||||
*/
|
||||
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
|
||||
|
||||
/**
|
||||
* Sets the "disabled" property on the range input element.
|
||||
*
|
||||
* @param isDisabled The disabled value
|
||||
*/
|
||||
setDisabledState(isDisabled: boolean): void {
|
||||
this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled);
|
||||
}
|
||||
|
@ -28,35 +28,26 @@ function _extractId(valueString: string): string {
|
||||
|
||||
/**
|
||||
* @description
|
||||
*
|
||||
* Writes values and listens to changes on a select element.
|
||||
*
|
||||
* Used by `NgModel`, `FormControlDirective`, and `FormControlName`
|
||||
* to keep the view synced with the `FormControl` model.
|
||||
*
|
||||
* If you have imported the `FormsModule` or the `ReactiveFormsModule`, this
|
||||
* value accessor will be active on any select control that has a form directive. You do
|
||||
* **not** need to add a special selector to activate it.
|
||||
* The `ControlValueAccessor` for writing select control values and listening to select control
|
||||
* changes. The value accessor is used by the `FormControlDirective`, `FormControlName`, and
|
||||
* `NgModel` directives.
|
||||
*
|
||||
* @usageNotes
|
||||
* ### How to use select controls with form directives
|
||||
*
|
||||
* ### Using select controls in a reactive form
|
||||
*
|
||||
* The following examples show how to use a select control in a reactive form.
|
||||
*
|
||||
* {@example forms/ts/reactiveSelectControl/reactive_select_control_example.ts region='Component'}
|
||||
*
|
||||
* ### Using select controls in a template-driven form
|
||||
*
|
||||
* To use a select in a template-driven form, simply add an `ngModel` and a `name`
|
||||
* attribute to the main `<select>` tag.
|
||||
*
|
||||
* If your option values are simple strings, you can bind to the normal `value` property
|
||||
* on the option. If your option values happen to be objects (and you'd like to save the
|
||||
* selection in your form as an object), use `ngValue` instead:
|
||||
*
|
||||
* {@example forms/ts/selectControl/select_control_example.ts region='Component'}
|
||||
*
|
||||
* In reactive forms, you'll also want to add your form directive (`formControlName` or
|
||||
* `formControl`) on the main `<select>` tag. Like in the former example, you have the
|
||||
* choice of binding to the `value` or `ngValue` property on the select's options.
|
||||
*
|
||||
* {@example forms/ts/reactiveSelectControl/reactive_select_control_example.ts region='Component'}
|
||||
*
|
||||
* ### Caveat: Option selection
|
||||
* ### Customizing option selection
|
||||
*
|
||||
* Angular uses object identity to select option. It's possible for the identities of items
|
||||
* to change while the data does not. This can happen, for example, if the items are produced
|
||||
@ -67,10 +58,12 @@ function _extractId(valueString: string): string {
|
||||
* `compareWith` takes a **function** which has two arguments: `option1` and `option2`.
|
||||
* If `compareWith` is given, Angular selects option by the return value of the function.
|
||||
*
|
||||
* ### Syntax
|
||||
* ```ts
|
||||
* const selectedCountriesControl = new FormControl();
|
||||
* ```
|
||||
*
|
||||
* ```
|
||||
* <select [compareWith]="compareFn" [(ngModel)]="selectedCountries">
|
||||
* <select [compareWith]="compareFn" [formControl]="selectedCountriesControl">
|
||||
* <option *ngFor="let country of countries" [ngValue]="country">
|
||||
* {{country.name}}
|
||||
* </option>
|
||||
@ -81,13 +74,13 @@ function _extractId(valueString: string): string {
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Note: We listen to the 'change' event because 'input' events aren't fired
|
||||
* **Note:** We listen to the 'change' event because 'input' events aren't fired
|
||||
* for selects in Firefox and IE:
|
||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=1024350
|
||||
* https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/4660045/
|
||||
*
|
||||
* @ngModule FormsModule
|
||||
* @ngModule ReactiveFormsModule
|
||||
* @ngModule FormsModule
|
||||
* @publicApi
|
||||
*/
|
||||
@Directive({
|
||||
@ -103,9 +96,23 @@ export class SelectControlValueAccessor implements ControlValueAccessor {
|
||||
/** @internal */
|
||||
_idCounter: number = 0;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* The registered callback function called when a change event occurs on the input element.
|
||||
*/
|
||||
onChange = (_: any) => {};
|
||||
|
||||
/**
|
||||
* @description
|
||||
* The registered callback function called when a blur event occurs on the input element.
|
||||
*/
|
||||
onTouched = () => {};
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Tracks the option comparison algorithm for tracking identities when
|
||||
* checking for changes.
|
||||
*/
|
||||
@Input()
|
||||
set compareWith(fn: (o1: any, o2: any) => boolean) {
|
||||
if (typeof fn !== 'function') {
|
||||
@ -118,6 +125,12 @@ export class SelectControlValueAccessor implements ControlValueAccessor {
|
||||
|
||||
constructor(private _renderer: Renderer2, private _elementRef: ElementRef) {}
|
||||
|
||||
/**
|
||||
* Sets the "value" property on the input element. The "selectedIndex"
|
||||
* property is also set if an ID is provided on the option element.
|
||||
*
|
||||
* @param value The checked value
|
||||
*/
|
||||
writeValue(value: any): void {
|
||||
this.value = value;
|
||||
const id: string|null = this._getOptionId(value);
|
||||
@ -128,14 +141,32 @@ export class SelectControlValueAccessor implements ControlValueAccessor {
|
||||
this._renderer.setProperty(this._elementRef.nativeElement, 'value', valueString);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Registers a function called when the control value changes.
|
||||
*
|
||||
* @param fn The callback function
|
||||
*/
|
||||
registerOnChange(fn: (value: any) => any): void {
|
||||
this.onChange = (valueString: string) => {
|
||||
this.value = this._getOptionValue(valueString);
|
||||
fn(this.value);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Registers a function called when the control is touched.
|
||||
*
|
||||
* @param fn The callback function
|
||||
*/
|
||||
registerOnTouched(fn: () => any): void { this.onTouched = fn; }
|
||||
|
||||
/**
|
||||
* Sets the "disabled" property on the select input element.
|
||||
*
|
||||
* @param isDisabled The disabled value
|
||||
*/
|
||||
setDisabledState(isDisabled: boolean): void {
|
||||
this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled);
|
||||
}
|
||||
@ -160,17 +191,20 @@ export class SelectControlValueAccessor implements ControlValueAccessor {
|
||||
|
||||
/**
|
||||
* @description
|
||||
*
|
||||
* Marks `<option>` as dynamic, so Angular can be notified when options change.
|
||||
*
|
||||
* See docs for `SelectControlValueAccessor` for usage examples.
|
||||
* @see `SelectControlValueAccessor`
|
||||
*
|
||||
* @ngModule FormsModule
|
||||
* @ngModule ReactiveFormsModule
|
||||
* @ngModule FormsModule
|
||||
* @publicApi
|
||||
*/
|
||||
@Directive({selector: 'option'})
|
||||
export class NgSelectOption implements OnDestroy {
|
||||
/**
|
||||
* @description
|
||||
* ID of the option element
|
||||
*/
|
||||
// TODO(issue/24571): remove '!'.
|
||||
id !: string;
|
||||
|
||||
@ -180,6 +214,11 @@ export class NgSelectOption implements OnDestroy {
|
||||
if (this._select) this.id = this._select._registerOption();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Tracks the value bound to the option element. Unlike the value binding,
|
||||
* ngValue supports binding to objects.
|
||||
*/
|
||||
@Input('ngValue')
|
||||
set ngValue(value: any) {
|
||||
if (this._select == null) return;
|
||||
@ -188,6 +227,11 @@ export class NgSelectOption implements OnDestroy {
|
||||
this._select.writeValue(this._select.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Tracks simple string values bound to the option element.
|
||||
* For objects, use the `ngValue` input binding.
|
||||
*/
|
||||
@Input('value')
|
||||
set value(value: any) {
|
||||
this._setElementValue(value);
|
||||
@ -199,6 +243,10 @@ export class NgSelectOption implements OnDestroy {
|
||||
this._renderer.setProperty(this._element.nativeElement, 'value', value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Lifecycle method called before the directive's instance is destroyed. For internal use only.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
if (this._select) {
|
||||
this._select._optionMap.delete(this.id);
|
||||
|
@ -41,33 +41,35 @@ abstract class HTMLCollection {
|
||||
}
|
||||
|
||||
/**
|
||||
* The accessor for writing a value and listening to changes on a select element.
|
||||
* @description
|
||||
* The `ControlValueAccessor` for writing multi-select control values and listening to multi-select control
|
||||
* changes. The value accessor is used by the `FormControlDirective`, `FormControlName`, and `NgModel`
|
||||
* directives.
|
||||
*
|
||||
* @see `SelectControlValueAccessor`
|
||||
*
|
||||
* @usageNotes
|
||||
* ### Caveat: Options selection
|
||||
*
|
||||
* Angular uses object identity to select options. It's possible for the identities of items
|
||||
* to change while the data does not. This can happen, for example, if the items are produced
|
||||
* from an RPC to the server, and that RPC is re-run. Even if the data hasn't changed, the
|
||||
* second response will produce objects with different identities.
|
||||
*
|
||||
* To customize the default option comparison algorithm, `<select multiple>` supports `compareWith`
|
||||
* input. `compareWith` takes a **function** which has two arguments: `option1` and `option2`.
|
||||
* If `compareWith` is given, Angular selects options by the return value of the function.
|
||||
*
|
||||
* ### Syntax
|
||||
*
|
||||
* ### Using a multi-select control
|
||||
*
|
||||
* The follow example shows you how to use a multi-select control with a reactive form.
|
||||
*
|
||||
* ```ts
|
||||
* const countryControl = new FormControl();
|
||||
* ```
|
||||
*
|
||||
* ```
|
||||
* <select multiple [compareWith]="compareFn" [(ngModel)]="selectedCountries">
|
||||
* <option *ngFor="let country of countries" [ngValue]="country">
|
||||
* {{country.name}}
|
||||
* </option>
|
||||
* <select multiple name="countries" [formControl]="countryControl">
|
||||
* <option *ngFor="let country of countries" [ngValue]="country">
|
||||
* {{ country.name }}
|
||||
* </option>
|
||||
* </select>
|
||||
*
|
||||
* compareFn(c1: Country, c2: Country): boolean {
|
||||
* return c1 && c2 ? c1.id === c2.id : c1 === c2;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ### Customizing option selection
|
||||
*
|
||||
* To customize the default option comparison algorithm, `<select>` supports `compareWith` input.
|
||||
* See the `SelectControlValueAccessor` for usage.
|
||||
*
|
||||
* @ngModule ReactiveFormsModule
|
||||
* @ngModule FormsModule
|
||||
@ -80,15 +82,34 @@ abstract class HTMLCollection {
|
||||
providers: [SELECT_MULTIPLE_VALUE_ACCESSOR]
|
||||
})
|
||||
export class SelectMultipleControlValueAccessor implements ControlValueAccessor {
|
||||
/**
|
||||
* @description
|
||||
* The current value
|
||||
*/
|
||||
value: any;
|
||||
|
||||
/** @internal */
|
||||
_optionMap: Map<string, NgSelectMultipleOption> = new Map<string, NgSelectMultipleOption>();
|
||||
/** @internal */
|
||||
_idCounter: number = 0;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* The registered callback function called when a change event occurs on the input element.
|
||||
*/
|
||||
onChange = (_: any) => {};
|
||||
|
||||
/**
|
||||
* @description
|
||||
* The registered callback function called when a blur event occurs on the input element.
|
||||
*/
|
||||
onTouched = () => {};
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Tracks the option comparison algorithm for tracking identities when
|
||||
* checking for changes.
|
||||
*/
|
||||
@Input()
|
||||
set compareWith(fn: (o1: any, o2: any) => boolean) {
|
||||
if (typeof fn !== 'function') {
|
||||
@ -101,6 +122,13 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor
|
||||
|
||||
constructor(private _renderer: Renderer2, private _elementRef: ElementRef) {}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Sets the "value" property on one or of more
|
||||
* of the select's options.
|
||||
*
|
||||
* @param value The value
|
||||
*/
|
||||
writeValue(value: any): void {
|
||||
this.value = value;
|
||||
let optionSelectedStateSetter: (opt: NgSelectMultipleOption, o: any) => void;
|
||||
@ -114,6 +142,13 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor
|
||||
this._optionMap.forEach(optionSelectedStateSetter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Registers a function called when the control value changes
|
||||
* and writes an array of the selected options.
|
||||
*
|
||||
* @param fn The callback function
|
||||
*/
|
||||
registerOnChange(fn: (value: any) => any): void {
|
||||
this.onChange = (_: any) => {
|
||||
const selected: Array<any> = [];
|
||||
@ -140,8 +175,20 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor
|
||||
fn(selected);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Registers a function called when the control is touched.
|
||||
*
|
||||
* @param fn The callback function
|
||||
*/
|
||||
registerOnTouched(fn: () => any): void { this.onTouched = fn; }
|
||||
|
||||
/**
|
||||
* Sets the "disabled" property on the select input element.
|
||||
*
|
||||
* @param isDisabled The disabled value
|
||||
*/
|
||||
setDisabledState(isDisabled: boolean): void {
|
||||
this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled);
|
||||
}
|
||||
@ -169,18 +216,14 @@ export class SelectMultipleControlValueAccessor implements ControlValueAccessor
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Marks `<option>` as dynamic, so Angular can be notified when options change.
|
||||
*
|
||||
* @usageNotes
|
||||
* ### Example
|
||||
* @see `SelectMultipleControlValueAccessor`
|
||||
*
|
||||
* ```
|
||||
* <select multiple name="city" ngModel>
|
||||
* <option *ngFor="let c of cities" [value]="c"></option>
|
||||
* </select>
|
||||
* ```
|
||||
* @ngModule FormsModule
|
||||
* @ngModule ReactiveFormsModule
|
||||
* @ngModule FormsModule
|
||||
* @publicApi
|
||||
*/
|
||||
@Directive({selector: 'option'})
|
||||
export class NgSelectMultipleOption implements OnDestroy {
|
||||
@ -197,6 +240,11 @@ export class NgSelectMultipleOption implements OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Tracks the value bound to the option element. Unlike the value binding,
|
||||
* ngValue supports binding to objects.
|
||||
*/
|
||||
@Input('ngValue')
|
||||
set ngValue(value: any) {
|
||||
if (this._select == null) return;
|
||||
@ -205,6 +253,11 @@ export class NgSelectMultipleOption implements OnDestroy {
|
||||
this._select.writeValue(this._select.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Tracks simple string values bound to the option element.
|
||||
* For objects, use the `ngValue` input binding.
|
||||
*/
|
||||
@Input('value')
|
||||
set value(value: any) {
|
||||
if (this._select) {
|
||||
@ -226,6 +279,10 @@ export class NgSelectMultipleOption implements OnDestroy {
|
||||
this._renderer.setProperty(this._element.nativeElement, 'selected', selected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Lifecycle method called before the directive's instance is destroyed. For internal use only.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
if (this._select) {
|
||||
this._select._optionMap.delete(this.id);
|
||||
|
@ -33,7 +33,9 @@ export type NavigationTrigger = 'imperative' | 'popstate' | 'hashchange';
|
||||
* ```
|
||||
* class MyService {
|
||||
* constructor(public router: Router, logger: Logger) {
|
||||
* router.events.filter(e => e instanceof RouterEvent).subscribe(e => {
|
||||
* router.events.pipe(
|
||||
* filter(e => e instanceof RouterEvent)
|
||||
* ).subscribe(e => {
|
||||
* logger.log(e.id, e.url);
|
||||
* });
|
||||
* }
|
||||
|
@ -17,13 +17,13 @@ import {UrlTree} from '../url_tree';
|
||||
|
||||
export function recognize(
|
||||
rootComponentType: Type<any>| null, config: Route[], serializer: (url: UrlTree) => string,
|
||||
paramsInheritanceStrategy: 'emptyOnly' |
|
||||
'always'): MonoTypeOperatorFunction<NavigationTransition> {
|
||||
paramsInheritanceStrategy: 'emptyOnly' | 'always', relativeLinkResolution: 'legacy' |
|
||||
'corrected'): MonoTypeOperatorFunction<NavigationTransition> {
|
||||
return function(source: Observable<NavigationTransition>) {
|
||||
return source.pipe(mergeMap(
|
||||
t => recognizeFn(
|
||||
rootComponentType, config, t.urlAfterRedirects, serializer(t.urlAfterRedirects),
|
||||
paramsInheritanceStrategy)
|
||||
paramsInheritanceStrategy, relativeLinkResolution)
|
||||
.pipe(map(targetSnapshot => ({...t, targetSnapshot})))));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -413,7 +413,7 @@ export class Router {
|
||||
// Recognize
|
||||
recognize(
|
||||
this.rootComponentType, this.config, (url) => this.serializeUrl(url),
|
||||
this.paramsInheritanceStrategy),
|
||||
this.paramsInheritanceStrategy, this.relativeLinkResolution),
|
||||
|
||||
// Fire RoutesRecognized
|
||||
tap(t => {
|
||||
|
@ -76,6 +76,41 @@ describe('Integration', () => {
|
||||
]);
|
||||
})));
|
||||
|
||||
describe('relativeLinkResolution', () => {
|
||||
beforeEach(inject([Router], (router: Router) => {
|
||||
router.resetConfig([{
|
||||
path: 'foo',
|
||||
children: [{path: 'bar', children: [{path: '', component: RelativeLinkCmp}]}]
|
||||
}]);
|
||||
}));
|
||||
|
||||
it('should not ignore empty paths in legacy mode',
|
||||
fakeAsync(inject([Router], (router: Router) => {
|
||||
router.relativeLinkResolution = 'legacy';
|
||||
|
||||
const fixture = createRoot(router, RootCmp);
|
||||
|
||||
router.navigateByUrl('/foo/bar');
|
||||
advance(fixture);
|
||||
|
||||
const link = fixture.nativeElement.querySelector('a');
|
||||
expect(link.getAttribute('href')).toEqual('/foo/bar/simple');
|
||||
})));
|
||||
|
||||
it('should ignore empty paths in corrected mode',
|
||||
fakeAsync(inject([Router], (router: Router) => {
|
||||
router.relativeLinkResolution = 'corrected';
|
||||
|
||||
const fixture = createRoot(router, RootCmp);
|
||||
|
||||
router.navigateByUrl('/foo/bar');
|
||||
advance(fixture);
|
||||
|
||||
const link = fixture.nativeElement.querySelector('a');
|
||||
expect(link.getAttribute('href')).toEqual('/foo/simple');
|
||||
})));
|
||||
});
|
||||
|
||||
it('should set the restoredState to null when executing imperative navigations',
|
||||
fakeAsync(inject([Router], (router: Router) => {
|
||||
router.resetConfig([
|
||||
@ -3977,6 +4012,57 @@ describe('Integration', () => {
|
||||
]);
|
||||
})));
|
||||
});
|
||||
|
||||
describe('relativeLinkResolution', () => {
|
||||
@Component({selector: 'link-cmp', template: `<a [routerLink]="['../simple']">link</a>`})
|
||||
class RelativeLinkCmp {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [RelativeLinkCmp],
|
||||
imports: [RouterModule.forChild([
|
||||
{path: 'foo/bar', children: [{path: '', component: RelativeLinkCmp}]},
|
||||
])]
|
||||
})
|
||||
class LazyLoadedModule {
|
||||
}
|
||||
|
||||
it('should not ignore empty path when in legacy mode',
|
||||
fakeAsync(inject(
|
||||
[Router, NgModuleFactoryLoader],
|
||||
(router: Router, loader: SpyNgModuleFactoryLoader) => {
|
||||
router.relativeLinkResolution = 'legacy';
|
||||
loader.stubbedModules = {expected: LazyLoadedModule};
|
||||
|
||||
const fixture = createRoot(router, RootCmp);
|
||||
|
||||
router.resetConfig([{path: 'lazy', loadChildren: 'expected'}]);
|
||||
|
||||
router.navigateByUrl('/lazy/foo/bar');
|
||||
advance(fixture);
|
||||
|
||||
const link = fixture.nativeElement.querySelector('a');
|
||||
expect(link.getAttribute('href')).toEqual('/lazy/foo/bar/simple');
|
||||
})));
|
||||
|
||||
it('should ignore empty path when in corrected mode',
|
||||
fakeAsync(inject(
|
||||
[Router, NgModuleFactoryLoader],
|
||||
(router: Router, loader: SpyNgModuleFactoryLoader) => {
|
||||
router.relativeLinkResolution = 'corrected';
|
||||
loader.stubbedModules = {expected: LazyLoadedModule};
|
||||
|
||||
const fixture = createRoot(router, RootCmp);
|
||||
|
||||
router.resetConfig([{path: 'lazy', loadChildren: 'expected'}]);
|
||||
|
||||
router.navigateByUrl('/lazy/foo/bar');
|
||||
advance(fixture);
|
||||
|
||||
const link = fixture.nativeElement.querySelector('a');
|
||||
expect(link.getAttribute('href')).toEqual('/lazy/foo/simple');
|
||||
})));
|
||||
});
|
||||
});
|
||||
|
||||
describe('Custom Route Reuse Strategy', () => {
|
||||
|
@ -191,7 +191,7 @@ function resolveBazelFilePath(fileName: string): string {
|
||||
// are not available in the working directory. In order to resolve the real path for the
|
||||
// runfile, we need to use `require.resolve` which handles runfiles properly on Windows.
|
||||
if (process.env['BAZEL_TARGET']) {
|
||||
return path.posix.relative(process.cwd(), require.resolve(fileName));
|
||||
return path.relative(process.cwd(), require.resolve(fileName));
|
||||
}
|
||||
|
||||
return fileName;
|
||||
|
@ -381,7 +381,7 @@ const memberDeclarationOrder: {[key: number]: number} = {
|
||||
};
|
||||
|
||||
function stripEmptyLines(text: string): string {
|
||||
return text.split('\n').filter(x => !!x.length).join('\n');
|
||||
return text.split(/\r?\n/).filter(x => !!x.length).join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user