Compare commits

..

197 Commits

Author SHA1 Message Date
3e80f0e526 release: cut the v10.0.10 release 2020-08-17 13:10:36 -07:00
f3dd6c224c fix(ngcc): detect synthesized delegate constructors for downleveled ES2015 classes (#38500)
Similarly to the change we landed in the `@angular/core` reflection
capabilities, we need to make sure that ngcc can detect pass-through
delegate constructors for classes using downleveled ES2015 output.

More details can be found in the preceding commit, and in the issue
outlining the problem: #38453.

Fixes #38453.

PR Close #38500
2020-08-17 12:50:19 -07:00
863acb6c21 fix(core): detect DI parameters in JIT mode for downleveled ES2015 classes (#38500)
In the Angular Package Format, we always shipped UMD bundles and previously even ES5 module output.
With V10, we removed the ES5 module output but kept the UMD ES5 output.

For this, we were able to remove our second TypeScript transpilation. Instead we started only
building ES2015 output and then downleveled it to ES5 UMD for the NPM packages. This worked
as expected but unveiled an issue in the `@angular/core` reflection capabilities.

In JIT mode, Angular determines constructor parameters (for DI) using the `ReflectionCapabilities`. The
reflection capabilities basically read runtime metadata of classes to determine the DI parameters. Such
metadata can be either stored in static class properties like `ctorParameters` or within TypeScript's `design:params`.

If Angular comes across a class that does not have any parameter metadata, it tries to detect if the
given class is actually delegating to an inherited class. It does this naively in JIT by checking if the
stringified class (function in ES5) matches a certain pattern. e.g.

```js
function MatTable() {
  var _this = _super.apply(this, arguments) || this;
```

These patterns are reluctant to changes of the class output. If a class is not recognized properly, the
DI parameters will be assumed empty and the class is **incorrectly** constructed without arguments.

This actually happened as part of v10 now. Since we downlevel ES2015 to ES5 (instead of previously
compiling sources directly to ES5), the class output changed slightly so that Angular no longer detects
it. e.g.

```js
var _this = _super.apply(this, __spread(arguments)) || this;
```

This happens because the ES2015 output will receive an auto-generated constructor if the class
defines class properties. This constructor is then already containing an explicit `super` call.

```js
export class MatTable extends CdkTable {
    constructor() {
        super(...arguments);
        this.disabled = true;
    }
}
```

If we then downlevel this file to ES5 with `--downlevelIteration`, TypeScript adjusts the `super` call so that
the spread operator is no longer used (not supported in ES5). The resulting super call is different to the
super call that would have been emitted if we would directly transpile to ES5. Ultimately, Angular no
longer detects such classes as having an delegate constructor -> and DI breaks.

We fix this by expanding the rather naive RegExp patterns used for the reflection capabilities
so that downleveled pass-through/delegate constructors are properly detected. There is a risk
of a false-positive as we cannot detect whether `__spread` is actually the TypeScript spread
helper, but given the reflection patterns already make lots of assumptions (e.g. that `super` is
actually the superclass, we should be fine making this assumption too. The false-positive would
not result in a broken app, but rather in unnecessary providers being injected (as a noop).

Fixes #38453

PR Close #38500
2020-08-17 12:50:16 -07:00
989e8a1f99 fix(router): ensure routerLinkActive updates when associated routerLinks change (#38349)
This commit introduces a new subscription in the `routerLinkActive` directive which triggers an update
when any of its associated routerLinks have changes. `RouterLinkActive` not only needs to know when
links are added or removed, but it also needs to know about if a link it already knows about
changes in some way.

Quick note that `from...mergeAll` is used instead of just a simple
`merge` (or `scheduled...mergeAll`) to avoid introducing new rxjs
operators in order to keep bundle size down.

Fixes #18469

PR Close #38349
2020-08-17 12:34:02 -07:00
84d1ba792b refactor(language-service): [Ivy] remove temporary compiler (#38310)
Now that Ivy compiler has a proper `TemplateTypeChecker` interface
(see https://github.com/angular/angular/pull/38105) we no longer need to
keep the temporary compiler implementation.

The temporary compiler was created to enable testing infrastructure to
be developed for the Ivy language service.

This commit removes the whole `ivy/compiler` directory and moves two
functions `createTypeCheckingProgramStrategy` and
`getOrCreateTypeCheckScriptInfo` to the `LanguageService` class.

Also re-enable the Ivy LS test since it's no longer blocking development.

PR Close #38310
2020-08-17 11:30:36 -07:00
b32126c335 fix(common): Allow scrolling when browser supports scrollTo (#38468)
This commit fixes a regression from "fix(common): ensure
scrollRestoration is writable (#30630)" that caused scrolling to not
happen at all in browsers that do not support scroll restoration. The
issue was that `supportScrollRestoration` was updated to return `false`
if a browser did not have a writable `scrollRestoration`. However, the
previous behavior was that the function would return `true` if
`window.scrollTo` was defined. Every scrolling function in the
`ViewportScroller` used `supportScrollRestoration` and, with the update
in bb88c9fa3d, no scrolling would be
performed if a browser did not have writable `scrollRestoration` but
_did_ have `window.scrollTo`.

Note, that this failure was detected in the saucelabs tests. IE does not
support scroll restoration so IE tests were failing.

PR Close #38468
2020-08-14 11:41:26 -07:00
d5e09f4d62 fix(core): fix multiple nested views removal from ViewContainerRef (#38317)
When removal of one view causes removal of another one from the same
ViewContainerRef it triggers an error with views length calculation. This commit
fixes this bug by removing a view from the list of available views before invoking
actual view removal (which might be recursive and relies on the length of the list
of available views).

Fixes #38201.

PR Close #38317
2020-08-13 13:35:56 -07:00
Ahn
c25c57c3a3 style(compiler-cli): remove unused constant (#38441)
Remove unused constant allDiagnostics

PR Close #38441
2020-08-13 13:32:44 -07:00
692e34d4a2 test(docs-infra): remove deprecated ReflectiveInjector (#38408)
This commit replaces the old and slow `ReflectiveInjector` that was
deprecated in v5 with the new `Injector`. Note: This change was only
done in the spec files inside the `aio` folder.

While changing this, it was not possible to directly use `Injector.get`
to get the correct typing for the mocked classes. For example:

```typescript
locationService = injector.get<TestLocationService>(LocationService);
```

Fails with:

> Argument of type 'typeof LocationService' is not assignable to parameter
of type 'Type<TestLocationService> | InjectionToken<TestLocationService> |
AbstractType<TestLocationService>'.
  Type 'typeof LocationService' is not assignable to type 'Type<TestLocationService>'.
    Property 'searchResult' is missing in type 'LocationService' but required in type
    'TestLocationService'.

Therefore, it was necessary to first convert to `unknown` and then to
`TestLocationService`.

```typescript
locationService = injector.get(LocationService) as unknown as TestLocationService;
```

PR Close #38408
2020-08-13 12:56:17 -07:00
071348eb72 build: run browsers tests on chromium locally (#38435) (#38450)
Previously we added a browser target for `firefox` into the
dev-infra package. It looks like as part of this change, we
accidentally switched the local web testing target to `firefox`.

Web tests are not commonly run locally as we use Domino and
NodeJS tests for primary development. Sometimes though we intend
to run tests in a browser. This would currently work with Firefox
but not on Windows (as Firefox is a noop there in Bazel).

This commit switches the primary browser back to `chromium`. Also
Firefox has been added as a second browser to web testing targets.

This allows us to reduce browsers in the legacy Saucelabs job. i.e.
not running Chrome and Firefox there. This should increase stability
and speed up the legacy job (+ reduced rate limit for Saucelabs).

PR Close #38435

PR Close #38450
2020-08-13 12:55:16 -07:00
8803f9f4dc ci: disable saucelabs tests on Firefox ESR while investigating failures (#37647) (#38450)
Firefox ESR tests fail running the acceptance tests on saucelabs.  These tests are being
disabled while investigating the failure as it is not entirely clear whether this is
saucelabs failure or a something like a memory pressure error in the test itself.

PR Close #37647

PR Close #38450
2020-08-13 12:55:13 -07:00
aa816d3887 ci: disable closure size tracking test (#38449)
We should define ngDevMode to false in Closure, but --define only works in the global scope.
With ngDevMode not being set to false, this size tracking test provides little value but a lot of
headache to continue updating the size.

PR Close #38449
2020-08-13 11:41:16 -07:00
a5ba40a78b refactor(router): Add annotations to correct Router documentation (#38448)
The `@HostListener` functions and lifecycle hooks aren't intended to be public API but
do need to appear in the `.d.ts` files or type checking will break. Adding the
nodoc annotation will correctly hide this function on the docs site.

Again, note that `@internal` cannot be used because the result would be
that the functions then do not appear in the `.d.ts` files. This would
break lifecycle hooks because the class would be seen as not
implementing the interface correctly. This would also break
`HostListener` because the compiled templates would attempt to call the
`onClick` functions, but those would also not appear in the `d.ts` and
would produce errors like "Property 'onClick' does not exist on type 'RouterLinkWithHref'".

PR Close #38448
2020-08-13 11:36:13 -07:00
cb83b8a887 fix(core): error if CSS custom property in host binding has number in name (#38432)
Fixes an error if a CSS custom property, used inside a host binding, has a
number in its name. The error is thrown because the styling parser only
expects characters from A to Z,dashes, underscores and a handful of other
characters.

Fixes #37292.

PR Close #38432
2020-08-13 10:35:11 -07:00
8bb726e899 build: update ng-dev config file for new commit message configuration (#38430)
Removes the commit message types from the config as they are now staticly
defined in the dev-infra code.

PR Close #38430
2020-08-13 09:11:25 -07:00
88662a540d feat(dev-infra): migrate to unified commit message types in commit message linting (#38430)
Previously commit message types were provided as part of the ng-dev config in the repository
using the ng-dev toolset.  This change removes this configuration expectation and instead
predefines the valid types for commit messages.

Additionally, with this new unified set of types requirements around providing a scope have
been put in place.  Scopes are either required, optional or forbidden for a given commit
type.

PR Close #38430
2020-08-13 09:11:23 -07:00
9b32a5917c feat(dev-infra): update to latest benchpress version (#38440)
We recently updated the benchpress package to have a more loose
Angular core peer dependency, and less other unused dependencies.

We should make sure to use that in the dev-infra package so that
peer dependencies can be satisified in consumer projects, and so
that less unused dependencies are brought into projects.

PR Close #38440
2020-08-13 09:09:34 -07:00
ffd1691ba9 feat(dev-infra): save invalid commit message attempts to be restored on next commit attempt (#38304)
When a commit message fails validation, rather than throwing out the commit message entirely
the commit message is saved into a draft file and restored on the next commit attempt.

PR Close #38304
2020-08-13 08:45:28 -07:00
afd4417a7b refactor(dev-infra): extract the commit message parsing function into its own file (#38429)
Extracts the commit message parsing function into its own file.

PR Close #38429
2020-08-12 16:10:08 -07:00
2f53bbba20 docs: remove unused Input decorator (#38306)
In the part "5. Add In-app Navigation" of the tutorial it was already removed
PR Close #38306
2020-08-12 11:26:11 -07:00
a4f99f4de9 refactor(dev-infra): use promptConfirm util in ng-dev's formatter (#38419)
Use the promptConfirm util instead of manually creating a confirm prompt with
inquirer.

PR Close #38419
2020-08-12 11:25:12 -07:00
0711128d28 docs: update web-worker CLI commands to bash style (#38421)
With this change we update the CLI generate commands to be in bash style.

PR Close #38421
2020-08-12 11:24:36 -07:00
25f79ba73e release: cut the v10.0.9 release 2020-08-12 09:35:37 -07:00
2590a9b535 docs: delete one superfluous sentence (#38339)
PR Close #38339
2020-08-12 08:23:19 -07:00
f68a1d3708 build: run formatting automatically on pre-commit hook (#38402)
Runs the `ng-dev format changed` command whenever the `git commit` command is
run.  As all changes which are checked by CI will require this check passing, this
change can prevent needless roundtrips to correct lint/formatting errors. This
automatic formatting can be bypassed with the `--no-verify` flag on the `git commit`
command.

PR Close #38402
2020-08-11 16:32:55 -07:00
89f7f63e18 feat(dev-infra): Add support for formatting all staged files (#38402)
Adds an ng-dev formatter option to format all of the staged files. This will can
be used to format only the staged files during the pre-commit hook.

PR Close #38402
2020-08-11 16:32:55 -07:00
ca2b4bcd4b fix(compiler-cli): avoid creating value expressions for symbols from type-only imports (#38415)
In TypeScript 3.8 support was added for type-only imports, which only brings in
the symbol as a type, not their value. The Angular compiler did not yet take
the type-only keyword into account when representing symbols in type positions
as value expressions. The class metadata that the compiler emits would include
the value expression for its parameter types, generating actual imports as
necessary. For type-only imports this should not be done, as it introduces an
actual import of the module that was originally just a type-only import.

This commit lets the compiler deal with type-only imports specially, preventing
a value expression from being created.

Backports #37912 to patch

PR Close #38415
2020-08-11 12:31:58 -07:00
f6cfc9294b ci: update payload size limits for Closure tests (#38411)
Currently the Closure-related tests are not tree-shaking the dev-mode-only content, thus payload
size checks are failing even if dev-mode-only content is added.
The 2e9fdbde9e commit
added some logic to JIT compiler, which is likely triggered the payload size increase. This commit
updates the payload size limits for Closure-related test to get master and patch branches back to
the "green" state.

PR Close #38411
2020-08-11 10:59:40 -07:00
5b87c67aed fix(compiler-cli): infer quote expressions as any type in type checker (#37917)
"Quote expressions" are expressions that start with an identifier followed by a
comma, allowing arbitrary syntax to follow. These kinds of expressions would
throw a an error in the template type checker, which would make them hard to
track down. As quote expressions are not generally used at all, the error would
typically occur for URLs that would inadvertently occur in a binding:

```html
<a [href]="https://example.com"></a>
```

This commit lets such bindings be inferred as the `any` type.

Fixes #36568
Resolves FW-2051

PR Close #37917
2020-08-11 09:54:54 -07:00
f5b9d87913 fix(compiler): evaluate safe navigation expressions in correct binding order (#37911)
When using the safe navigation operator in a binding expression, a temporary
variable may be used for storing the result of a side-effectful call.
For example, the following template uses a pipe and a safe property access:

```html
<app-person-view [enabled]="enabled" [firstName]="(person$ | async)?.name"></app-person-view>
```

The result of the pipe evaluation is stored in a temporary to be able to check
whether it is present. The temporary variable needs to be declared in a separate
statement and this would also cause the full expression itself to be pulled out
into a separate statement. This would compile into the following
pseudo-code instructions:

```js
var temp = null;
var firstName = (temp = pipe('async', ctx.person$)) == null ? null : temp.name;
property('enabled', ctx.enabled)('firstName', firstName);
```

Notice that the pipe evaluation happens before evaluating the `enabled` binding,
such that the runtime's internal binding index would correspond with `enabled`,
not `firstName`. This introduces a problem when the pipe uses `WrappedValue` to
force a change to be detected, as the runtime would then mark the binding slot
corresponding with `enabled` as dirty, instead of `firstName`. This results
in the `enabled` binding to be updated, triggering setters and affecting how
`OnChanges` is called.

In the pseudo-code above, the intermediate `firstName` variable is not strictly
necessary---it only improved readability a bit---and emitting it inline with
the binding itself avoids the out-of-order execution of the pipe:

```js
var temp = null;
property('enabled', ctx.enabled)
  ('firstName', (temp = pipe('async', ctx.person$)) == null ? null : temp.name);
```

This commit introduces a new `BindingForm` that results in the above code to be
generated and adds compiler and acceptance tests to verify the proper behavior.

Fixes #37194

PR Close #37911
2020-08-11 09:51:11 -07:00
3acebdcf5d fix(core): prevent NgModule scope being overwritten in JIT compiler (#37795)
In JIT compiled apps, component definitions are compiled upon first
access. For a component class `A` that extends component class `B`, the
`B` component is also compiled when the `InheritDefinitionFeature` runs
during the compilation of `A` before it has finalized. A problem arises
when the compilation of `B` would flush the NgModule scoping queue,
where the NgModule declaring `A` is still pending. The scope information
would be applied to the definition of `A`, but its compilation is still
in progress so requesting the component definition would compile `A`
again from scratch. This "inner compilation" is correctly assigned the
NgModule scope, but once the "outer compilation" of `A` finishes it
would overwrite the inner compilation's definition, losing the NgModule
scope information.

In summary, flushing the NgModule scope queue could trigger a reentrant
compilation, where JIT compilation is non-reentrant. To avoid the
reentrant compilation, a compilation depth counter is introduced to
avoid flushing the NgModule scope during nested compilations.

Fixes #37105

PR Close #37795
2020-08-11 09:50:28 -07:00
02aca135bd fix(dev-infra): update i18n-related file locations in PullApprove config (#38403)
The changes in https://github.com/angular/angular/pull/38368 split `render3/i18n.ts` files into
smaller scripts, but the PullApprove config was not updated to reflect that. This commit updates
the PullApprove config to reflect the recent changes in i18n-related files.

PR Close #38403
2020-08-10 17:29:52 -07:00
32109dc414 fix(core): queries not matching string injection tokens (#38321)
Queries weren't matching directives that provide themselves via string
injection tokens, because the assumption was that any string passed to
a query decorator refers to a template reference.

These changes make it so we match both template references and
providers while giving precedence to the template references.

Fixes #38313.
Fixes #38315.

PR Close #38321
2020-08-10 15:27:25 -07:00
c9acb7b7e2 fix(compiler-cli): preserve quotes in class member names (#38387)
When we were outputting class members for `setClassMetadata` calls,
we were using the string representation of the member name. This can
lead to us generating invalid code when the name contains dashes and
is quoted (e.g. `@Output() 'has-dashes' = new EventEmitter()`), because
the quotes will be stripped for the string representation.

These changes fix the issue by using the original name AST node that was
used for the declaration and which knows whether it's supposed to be
quoted or not.

Fixes #38311.

PR Close #38387
2020-08-10 15:26:46 -07:00
545444d86f refactor(core): break i18n.ts into smaller files (#38368)
This commit contains no changes to code. It only breaks `i18n.ts` file
into `i18n.ts` + `i18n_apply.ts` + `i18n_parse.ts` +
`i18n_postprocess.ts` for easier maintenance.

PR Close #38368
2020-08-10 15:07:44 -07:00
7f111491a8 fix(platform-server): remove styles added by ServerStylesHost on destruction (#38367)
When a ServerStylesHost instance is destroyed, all of the shared styles added to the DOM
head element by that instance should be removed.  Without this removal, over time a large
number of style rules will build up and cause extra memory pressure.  This brings the
ServerStylesHost in line with the DomStylesHost used by the platform browser, which
performs this same cleanup.

PR Close #38367
2020-08-10 13:12:24 -07:00
ee5123f5dc fix(core): Store the currently selected ICU in LView (#38345)
The currently selected ICU was incorrectly being stored it `TNode`
rather than in `LView`.

Remove: `TIcuContainerNode.activeCaseIndex`
Add: `LView[TIcu.currentCaseIndex]`

PR Close #38345
2020-08-10 12:41:18 -07:00
3dfaf56fdd docs: Remove redundant sentence from Router (#38398)
PR Close #38398
2020-08-10 09:52:31 -07:00
432a688edf docs: update team contributors page (#38384)
Removing Kara, Denny, Judy, Tony, Matias as they are no longer actively working on the project
PR Close #38384
2020-08-10 09:51:51 -07:00
42a649d80f docs: fix purpose description of "builders.json" (#36830)
PR Close #36830
2020-08-07 15:04:57 -07:00
47fbfb37bf refactor(forms): get rid of duplicate functions (#38371) (#38380)
This commit performs minor refactoring in Forms package to get rid of duplicate functions.
It looks like the functions were duplicated due to a slightly different type signatures, but
their logic is completely identical. The logic in retained functions remains the same and now
these function also accept a generic type to achieve the same level of type safety.

PR Close #38380
2020-08-07 12:11:37 -07:00
4107abba2d refactor(common): use getElementById in ViewportScroller.scrollToAnchor (#38372)
This commit uses getElementById and getElementsByName when an anchor scroll happens,
to avoid escaping the anchor and wrapping the code in a try/catch block.

Related to #28960

PR Close #38372
2020-08-07 11:17:53 -07:00
1d26bc544e test: update components repo to test against recent revision (#38273) (#38378)
The changes in angular/components#20136 are required to allow the
framework tests to succeed.

PR Close #38273

PR Close #38378
2020-08-07 11:12:49 -07:00
ef7b70a375 refactor(core): add debug ranges to LViewDebug with matchers (#38359)
This change provides better typing for the `LView.debug` property which
is intended to be used by humans while debugging the application with
`ngDevMode` turned on.

In addition this chang also adds jasmine matchers for better asserting
that `LView` is in the correct state.

PR Close #38359
2020-08-06 16:58:12 -07:00
deb290bc2c refactor(core): extract icuSwitchCase, icuUpdateCase, removeNestedIcu (#38154) (#38370)
Extract `icuSwitchCase`, `icuUpdateCase`, `removeNestedIcu` into
separate functions to align them with the `.debug` property text.

PR Close #38154

PR Close #38370
2020-08-06 16:27:28 -07:00
5c8b7d2c64 refactor(core): add human readable debug for i18n (#38154) (#38370)
I18n code breaks up internationalization into opCodes which are then stored
in arrays. To make it easier to debug the codebase this PR adds `debug`
property to the arrays which presents the data in human readable format.

PR Close #38154

PR Close #38370
2020-08-06 16:27:27 -07:00
f5d5bac076 fix(service-worker): fix the chrome debugger syntax highlighter (#38332)
The Chrome debugger is not able to render the syntax properly when the
code contains backticks. This is a known issue in Chrome and they have an
open [issue](https://bugs.chromium.org/p/chromium/issues/detail?id=659515) for that.
This commit adds the work-around to use double backslash with one
backtick ``\\` `` at the end of the line.

This can be reproduced by running the following command:

`yarn bazel test //packages/forms/test --config=debug`

When opening the chrome debugger tools, you should see the correct
code highlighting syntax.

PR Close #38332
2020-08-06 15:22:57 -07:00
ad16e2f97a docs(service-worker): describe how asset-/data-group order affects request handling (#38364)
The order of asset- and data-groups in `ngsw-config.json` affects how a
request is handled by the ServiceWorker. Previously, this was not
clearly documented.

This commit describes how the order of asset-/data-groups affects
request handling.

Closes #21189

PR Close #38364
2020-08-06 11:41:59 -07:00
d0191a7397 fix(docs-infra): correctly generate CLI commands docs when the overview page moves (#38365)
Previously, the [processCliCommands][1] dgeni processor, which is used
to generate the docs pages for the CLI commands, expected the CLI
commands overview page (with a URL of `cli`) to exist as a child of a
top-level navigation section (`CLI Commands`). If one tried to move the
`CLI Commands` section inside another section, `processCliCommnads`
would fail to find it and thus fail to generate the CLI commands docs.
This problem came up in #38353.

This commit updates the `processCliCommands` processor to be able to
find it regardless of the position of the `CLI Commands` section inside
the navigation doc.

[1]:
dca4443a8e/aio/tools/transforms/cli-docs-package/processors/processCliCommands.js (L7-L9)

PR Close #38365
2020-08-06 11:41:07 -07:00
016a41b14d fix(compiler-cli): mark eager NgModuleFactory construction as not side effectful (#38320)
Roll forward of #38147.

This allows Closure compiler to tree shake unused constructor calls to `NgModuleFactory`, which is otherwise considered
side-effectful. The Angular compiler generates factory objects which are exported but typically not used, as they are
only needed for compatibility with View Engine. This results in top-level constructor calls, such as:

```typescript
export const FooNgFactory = new NgModuleFactory(Foo);
```

`NgModuleFactory` has a side-effecting constructor, so this statement cannot be tree shaken, even if `FooNgFactory` is
never imported. The `NgModuleFactory` continues to reference its associated `NgModule` and prevents the module and all
its unused dependencies from being tree shaken, making Closure builds significantly larger than necessary.

The fix here is to wrap `NgModuleFactory` constructor with `noSideEffects(() => /* ... */)`, which tricks the Closure
compiler into assuming that the invoked function has no side effects. This allows it to tree-shake unused
`NgModuleFactory()` constructors when they aren't imported. Since the factory can be removed, the module can also be
removed (if nothing else references it), thus tree shaking unused dependencies as expected.

The one notable edge case is for lazy loaded modules. Internally, lazy loading is done as a side effect when the lazy
script is evaluated. For Angular, this side effect is registering the `NgModule`. In Ivy this is done by the
`NgModuleFactory` constructor, so lazy loaded modules **cannot** have their top-level `NgModuleFactory` constructor
call tree shaken. We handle this case by looking for the `id` field on `@NgModule` annotations. All lazy loaded modules
include an `id`. When this `id` is found, the `NgModuleFactory` is generated **without** with `noSideEffects()` call,
so Closure will not tree shake it and the module will lazy-load correctly.

PR Close #38320
2020-08-06 09:02:17 -07:00
7a527b4f95 refactor(compiler): add ModuleInfo interface (#38320)
This introduces a new `ModuleInfo` interface to represent some of the statically analyzed data from an `NgModule`. This
gets passed into transforms to give them more context around a given `NgModule` in the compilation.

PR Close #38320
2020-08-06 09:02:17 -07:00
fd28f0c219 refactor(core): add noSideEffects() as private export (#38320)
This is to enable the compiler to generate `noSideEffects()` calls. This is a private export, gated by `ɵ`.

PR Close #38320
2020-08-06 09:02:17 -07:00
7342b1ecdf docs: remove https://angular.io from internal links (#38360)
PR #36601 introduces icons on all links if the link contains
https:// or http:// but there were some internal links left
which contained https://angular.io. Removed https://angular.io
from all these links.

PR Close #38360
2020-08-06 09:01:35 -07:00
58f4b3ad80 fix(common): ensure scrollRestoration is writable (#30630) (#38357)
Some specialised browsers that do not support scroll restoration
(e.g. some web crawlers) do not allow `scrollRestoration` to be
writable.

We already sniff the browser to see if it has the `window.scrollTo`
method, so now we also check whether `window.history.scrollRestoration`
is writable too.

Fixes #30629

PR Close #30630

PR Close #38357
2020-08-05 17:49:56 -07:00
3974312c3a style(docs-infra): reformat ScrollService file (#30630) (#38357)
Pre-empting code formatting changes when the
code is updated in a subsequent commit.

PR Close #30630

PR Close #38357
2020-08-05 17:49:55 -07:00
db897f4f7a docs: add a page with the Angular roadmap (#38356)
PR Close #38356
2020-08-05 17:16:38 -07:00
77093c21b7 refactor(platform-browser): specify return type of parseEventName (#38089)
This commit refactors the argument of the `parseEventName` function
to use an object with named properties instead of using an object indexer.

PR Close #38089
2020-08-05 17:06:29 -07:00
415131413d fix(router): prevent calling unsubscribe on undefined subscription in RouterPreloader (#38344)
Previously, the `ngOnDestroy` method called `unsubscribe` regardless of if `subscription` had
been initialized.  This can lead to an error attempting to call `unsubscribe` of undefined.
This change prevents this error, and instead only attempts `unsubscribe` when the subscription
has been defined.

PR Close #38344
2020-08-05 10:54:42 -07:00
df01a82fa2 fix(compiler-cli): match wrapHost parameter types within plugin interface (#38004)
The `TscPlugin` interface using a type of `ts.CompilerHost&Partial<UnifiedModulesHost>` for the `host` parameter
of the `wrapHost` method. However, prior to this change, the interface implementing `NgTscPlugin` class used a
type of `ts.CompilerHost&UnifiedModulesHost` for the parameter. This change corrects the inconsistency and
allows `UnifiedModulesHost` members to be optional when using the `NgtscPlugin`.

PR Close #38004
2020-08-05 10:54:08 -07:00
1912fa34ba feat(dev-infra): provide organization-wide merge-tool label configuration (#38223)
Previously, each Angular repository had its own strategy/configuration
for merging pull requests and cherry-picking. We worked out a new
strategy for labeling/branching/versioning that should be the canonical
strategy for all actively maintained projects in the Angular organization.

This PR provides a `ng-dev` merge configuration that implements the
labeling/branching/merging as per the approved proposal.

See the following document for the proposal this commit is based on
for the merge script labeling/branching: https://docs.google.com/document/d/197kVillDwx-RZtSVOBtPb4BBIAw0E9RT3q3v6DZkykU

The merge tool label configuration can be conveniently accesed
within each `.ng-dev` configuration, and can also be extended
if there are special labels on individual projects. This is one
of the reasons why the labels are not directly built into the
merge script. The script should remain unopinionated and flexible.

The configuration is conceptually powerful enough to achieve the
procedures as outlined in the versioning/branching/labeling proposal.

PR Close #38223
2020-08-05 10:53:18 -07:00
db5e1de07a feat(dev-infra): support user-failures when computing branches for target label (#38223)
The merge tool provides a way for configurations to determine the branches
for a label lazily. This is supported because it allows labels to respect
the currently selected base branch through the Github UI. e.g. if `target: label`
is applied on a PR and the PR is based on the patch branch, then the change
could only go into the selected target branch, while if it would be based on
`master`, the change would be cherry-picked to `master` too. This allows for
convenient back-porting of changes if they did not apply cleanly to the primary
development branch (`master`).

We want to expand this function so that it is possible to report failures if an
invalid target label is appplied (e.g. `target: major` not allowed in
some situations), or if the Github base branch is not valid for the given target
label (e.g. if `target: lts` is used, but it's not based on a LTS branch).

PR Close #38223
2020-08-05 10:53:18 -07:00
84661eac64 feat(dev-infra): provide github API instance to lazy merge configuration (#38223)
The merge script currently accepts a configuration function that will
be invoked _only_ when the `ng-dev merge` command is executed. This
has been done that way because the merge tooling usually relies on
external requests to Git or NPM for constructing the branch configurations.

We do not want to perform these slow external queries on any `ng-dev` command
though, so this became a lazily invoked function.

This commit adds support for these configuration functions to run
asynchronously (by returning a Promise that will be awaited), so that
requests could also be made to the Github API. This is benefical as it
could avoid dependence on the local Git state and the HTTP requests
are more powerful/faster.

Additionally, in order to be able to perform Github API requests
with an authenticated instance, the merge tool will pass through
a `GithubClient` instance that uses the specified `--github-token`
(or from the environment). This ensures that all API requests use
the same `GithubClient` instance and can be authenticated (mitigating
potential rate limits).

PR Close #38223
2020-08-05 10:53:18 -07:00
629203f17a docs: fix typo in glossary (#38318)
Add missing comma  in structural directive section that made dash display incorrectly.

PR Close #38318
2020-08-05 10:52:28 -07:00
2ab8e88924 release: cut the v10.0.8 release 2020-08-04 15:51:57 -07:00
a91dd2e42a build: cleanup .bazelrc file to no longer set unused flags (#38124)
This option is no longer needed in Bazel and will be an error in the future

PR Close #38124
2020-08-03 12:53:11 -07:00
6eca80b4aa revert: docs(core): correct SomeService to SomeComponent (#38325)
This reverts commit b4449e35bf.

The example given from the previous change was for a component selector and not a provider selector.

This change fixes it.

Fixes #38323.

PR Close #38325
2020-08-03 12:52:20 -07:00
cea46786a2 fix(compiler): update unparsable character reference entity error messages (#38319)
Within an angular template, when a character entity is unable to be parsed, previously a generic
unexpected character error was thrown.  This does not properly express the issue that was discovered
as the issue is actually caused by the discovered character making the whole of the entity unparsable.
The compiler will now instead inform via the error message what string was attempted to be parsed
and what it was attempted to be parsed as.

Example, for this template:
```
<p>
  &#x123p
</p>
```
Before this change:
`Unexpected character "p"`

After this change:
`Unable to parse entity "&#x123p" - hexadecimal character reference entities must end with ";"`

Fixes #26067

PR Close #38319
2020-07-31 15:32:54 -07:00
9c5fc1693a refactor(docs-infra): update docs examples tslint.json to match CLI and fix failures (#38143)
This commit updates the `tslint.json` configuration file, that is used
to lint the docs examples, to match the one generated for new Angular
CLI apps. There are some minimal differences (marked with `TODO`
comments) for things, such as component selector prefix, that would
require extensive and/or difficult to validate changes in guides.

This commit also includes the final adjustments to make the docs
examples code compatible with the new tslint rules. (The bulk of the
work has been done in previous commits.)

PR Close #38143
2020-07-31 11:00:08 -07:00
42f5770b7b refactor(docs-infra): fix docs examples for Angular-specific tslint rules (#38143)
This commit updates the docs examples to be compatible with the
following Angular-specific tslint rules:
- `component-selector`
- `directive-selector`
- `no-conflicting-lifecycle`
- `no-host-metadata-property`
- `no-input-rename`
- `no-output-native`
- `no-output-rename`

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:08 -07:00
01db37435f refactor(docs-infra): fix docs examples for tslint rule prefer-const (#38143)
This commit updates the docs examples to be compatible with the
`prefer-const` tslint rule.

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:08 -07:00
07a003352d refactor(docs-infra): fix docs examples for tslint rules related to underscores in variable names (#38143)
This commit updates the docs examples to be compatible with the
`variable-name` tslint rule without requiring the
`allow-leading-underscore` and `allow-trailing-underscore` options.

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:08 -07:00
ac009d293d refactor(docs-infra): fix docs examples for tslint rules related to variable names (#38143)
This commit updates the docs examples to be compatible with the
`no-shadowed-variable` and `variable-name` tslint rules.

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:08 -07:00
eb8f8c93c8 refactor(docs-infra): fix docs examples for tslint rule member-ordering (#38143)
This commit updates the docs examples to be compatible with the
`member-ordering` tslint rule.

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:08 -07:00
18a5117d1e refactor(docs-infra): fix docs examples for tslint rule no-angle-bracket-type-assertion (#38143)
This commit updates the docs examples to be compatible with the
`no-angle-bracket-type-assertion` tslint rule.

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:08 -07:00
ff72da60d3 refactor(docs-infra): fix docs examples for tslint rules related to object properties (#38143)
This commit updates the docs examples to be compatible with the
`no-string-literal`, `object-literal-key-quotes` and
`object-literal-shorthand` tslint rules.

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:08 -07:00
d4a723a464 refactor(docs-infra): fix docs examples for tslint rule only-arrow-functions (#38143)
This commit updates the docs examples to be compatible with the
`only-arrow-functions` tslint rule.

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:08 -07:00
9c2f0b8ac4 style(docs-infra): fix docs examples for tslint rule jsdoc-format (#38143)
This commit updates the docs examples to be compatible with the
`jsdoc-format` tslint rule.

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:07 -07:00
dc081fb0bf style(docs-infra): fix docs examples for tslint rule semicolon (#38143)
This commit updates the docs examples to be compatible with the
`semicolon` tslint rule.

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:07 -07:00
f882099f9d style(docs-infra): fix docs examples for tslint rules related to whitespace (#38143)
This commit updates the docs examples to be compatible with the `align`,
`space-before-function-paren` and `typedef-whitespace` tslint rules.

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:07 -07:00
24498eb416 style(docs-infra): fix docs examples for tslint rule import-spacing (#38143)
This commit updates the docs examples to be compatible with the
`import-spacing` tslint rule.

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:07 -07:00
4c2cdc682b refactor(docs-infra): remove unnecessary use strict from docs examples TS files (#38143)
By default, TypeScript will emit `"use strict"` directives, so it is not
necessary to include `'use strict'` in `.ts` files:
https://www.typescriptlang.org/docs/handbook/compiler-options.html#:~:text=--noImplicitUseStrict

PR Close #38143
2020-07-31 11:00:07 -07:00
1cb66bb39f refactor(docs-infra): remove unused styleguide examples (#38143)
The `03-*` code style rule have been removed from the style guide in
be0bc799f3.

This commit removes the corresponding files and related unused code from
the`styleguide` example project.

PR Close #38143
2020-07-31 11:00:07 -07:00
7ff5ef27c7 fix(language-service): [ivy] do not retrieve ts.Program at startup (#38120)
This commit removes compiler instantiation at startup.
This is because the constructor is invoked during the plugin loading phase,
in which the project has not been completely loaded.

Retrieving `ts.Program` at startup will trigger an `updateGraph` operation,
which could only be called after the Project has loaded completely.
Without this change, the Ivy LS cannot be loaded as a tsserver plugin.

Note that the whole `Compiler` class is temporary, so changes made there are
only for development. Once we have proper integration with ngtsc the
`Compiler` class would be removed.

PR Close #38120
2020-07-30 16:54:20 -07:00
03d8e317c4 fix(compiler): add PURE annotation to getInheritedFactory calls (#38291)
Currently the `getInheritedFactory` function is implemented to allow
closure to remove the call if the base factory is unused.  However, this
method does not work with terser.  By adding the PURE annotation,
terser will also be able to remove the call when unused.

PR Close #38291
2020-07-30 16:53:52 -07:00
1de4fe5dbf release: cut the v10.0.7 release 2020-07-30 16:37:25 -07:00
879ff08f0e fix(compiler): Metadata should not include methods on Object.prototype (#38292)
This commit fixes a bug in View Engine whereby the compiler errorneously
thinks that a method of a component has decorator metadata when that
method is one of those in `Object.prototype`, for example `toString`.

This bug is discovered in v10.0.4 of `@angular/language-service` after
the default bundle format was switched from ES5 to ES2015.

ES5 output:
```js
if (propMetadata[propName]) {
    decorators.push.apply(decorators, __spread(propMetadata[propName]));
}
```

ES2015 output:
```js
if (propMetadata[propName]) {
    decorators.push(...propMetadata[propName]);
}
```

The bug was not discovered in ES5 because the polyfill for the spread
operator happily accepts parameters that do not have the `iterable`
symbol:

```js
function __spread() {
    for (var ar = [], i = 0; i < arguments.length; i++)
        ar = ar.concat(__read(arguments[i]));
    return ar;
}
```

whereas in es2015 it’ll fail since the iterable symbol is not present in
`propMetadata['toString']` which evaluates to a function.

Fixes https://github.com/angular/vscode-ng-language-service/issues/859

PR Close #38292
2020-07-30 15:18:28 -07:00
a15d7ac1da Revert "fix(compiler): mark NgModuleFactory construction as not side effectful (#38147)" (#38303)
This reverts commit 7f8c2225f2.

This commit caused test failures internally, which were traced back to the
optimizer removing NgModuleFactory constructor calls when those calls caused
side-effectful registration of NgModules by their ids.

PR Close #38303
2020-07-30 12:19:36 -07:00
4a6abbdfc6 fix(compiler-cli): correct type of makeDiagnostic()
This commit corrects the type of makeDiagnostic()'s messageText parameter,
which was not compatible with some compiler refactorings merged to 10.0.x
due to unforeseen dependencies on prior refactorings.
2020-07-29 18:00:40 -07:00
78eb5f6777 refactor(compiler-cli): create diagnostics using ts.DiagnosticRelatedInformation (#37587)
Previously, an anonymous type was used for creating a diagnostic with related
information. The anonymous type would then be translated into the necessary
`ts.DiagnosticRelatedInformation` shape within `makeDiagnostic`. This commit
switches the `makeDiagnostic` signature over to taking `ts.DiagnosticRelatedInformation`
directly and introduces `makeRelatedInformation` to easily create such objects.
This is done to aid in making upcoming work more readable.

PR Close #37587
2020-07-29 17:50:08 -07:00
0cdb5231c2 test(compiler-cli): disable one TypeChecker test on Windows due to path sensitivity issue (#38294)
This commit disables one TypeChecker test (added as a part of
https://github.com/angular/angular/pull/38105) which make assertions about the filename while
running on Windows.

Such assertions are currently suffering from a case sensitivity issue.

PR Close #38294
2020-07-29 17:49:45 -07:00
5af845d049 docs(router): clarify how base href is used to construct targets (#38123)
The documentation is not clear on how the base href and APP_BASE_HREF are used. This commit
should help clarify more complicated use-cases beyond the most common one of just a '/'

PR Close #38123
2020-07-29 13:34:02 -07:00
a5c28497b5 fix(compiler): mark NgModuleFactory construction as not side effectful (#38147)
This allows Closure compiler to tree shake unused constructor calls to `NgModuleFactory`, which is otherwise considered
side-effectful. The Angular compiler generates factory objects which are exported but typically not used, as they are
only needed for compatibility with View Engine. This results in top-level constructor calls, such as:

```typescript
export const FooNgFactory = new NgModuleFactory(Foo);
```

`NgModuleFactory` has a side-effecting constructor, so this statement cannot be tree shaken, even if `FooNgFactory` is
never imported. The `NgModuleFactory` continues to reference its associated `NgModule` and prevents the module and all
its unused dependencies from being tree shaken. This effectively prevents all components from being tree shaken, making
Closure builds significantly larger than they should be.

The fix here is to wrap `NgModuleFactory` constructor with `noSideEffects(() => /* ... */)`, which tricks the Closure
compiler into assuming that the invoked function has no side effects. This allows it to tree-shake unused
`NgModuleFactory()` constructors when they aren't imported. Since the factory can be removed, the module can also be
removed (if nothing else references it), thus tree shaking unused components as expected.

PR Close #38147
2020-07-29 13:32:08 -07:00
801ad62ae3 refactor(compiler): wrap large strings in function (#38253)
Large strings constants are now wrapped in a function which is called whenever used. This works around a unique
limitation of Closure, where it will **always** inline string literals at **every** usage, regardless of how large the
string literal is or how many times it is used.The workaround is to use a function rather than a string literal.
Closure has differently inlining semantics for functions, where it will check the length of the function and the number
of times it is used before choosing to inline it. By using a function, `ngtsc` makes Closure more conservative about
inlining large strings, and avoids blowing up the bundle size.This optimization is only used if the constant is a large
string. A wrapping function is not included for other use cases, since it would just increase the bundle size and add
unnecessary runtime performance overhead.

PR Close #38253
2020-07-29 13:31:04 -07:00
7d4d2e29b0 docs: update bio picture (#38272)
Updates my profile picture which was quite old.

PR Close #38272
2020-07-29 10:33:37 -07:00
c33f719ddc refactor(docs-infra): Lazy-loads SVG icons (#38268)
Prior to this commit, SVG icons were all loaded in the constructor
of the `CustomIconRegistry`. This commit avoids that, and loads SVG
icons on demand.

PR Close #38268
2020-07-29 10:32:55 -07:00
69fc6b4f75 refactor(docs-infra): simplify/improve CopierService hidden textarea creation (#38244)
This commit simplifies the creation of the temporary, hidden
`<textarea>` element used by `CopierService` by switching from absolute
to fixed positioning and not requiring page's scroll offset.

It also makes the following minor improvements:
- Make the element invisible (via `opacity: 0`).
- Instruct screen-readers to ignore the element (via
  `aria-hidden: true`).

NOTE: These improvements are based on Angular CDK's [PendingCopy][1]
      class and the changes proposed in PR angular/components#20073.

[1]: https://github.com/angular/components/blob/89b5fa89d1437c3054c5/src/cdk/clipboard/pending-copy.ts

PR Close #38244
2020-07-29 10:32:07 -07:00
5ec4c02a43 fix(docs-infra): preserve focus on copy (and prevent scrolling to bottom on IE11) (#38244)
The `CopierService` is used for copying text to the user's clipboard. It
is, for example, used in `CodeComponent` to copy example code snippets.
This is implemented by creating a temporary, hidden `<textarea>`
elements, setting its value to the text that needs to be copied,
executing the `copy` command and finally removing the element from the
DOM.

Previously, as a result of `CopierService`'s implementation, the focused
element would lose focus, while the temporary `<textarea>` element would
implicitly gain focus when selecting its contents. This had an even
worse side-effect on IE11, which seems to scroll to the bottom of the
containing element (here `<body>`) when the focused element is removed.

This commit fixes these issues by keeping track of the previously
focused element and restoring its focus after the copy operation.

NOTE: This fix is inspired by Angular CDK's [PendingCopy][1] class.

[1]: https://github.com/angular/components/blob/89b5fa89d1437c3054c5/src/cdk/clipboard/pending-copy.ts

Fixes #37796

PR Close #38244
2020-07-29 10:32:07 -07:00
f0489ee72a refactor(docs-infra): improve code readability of CopierService (#38244)
This commit improves the code readability of the `CopierService` by:
- Adding/Improving JSDoc comments for methods.
- Avoiding unnecessary instance-wide properties.
- Fixing indentation to be consistent (at two spaces).
- Clearly separating the logic for creating and populating a
  `<textarea>` from the logic for selecting and copying its contents.

PR Close #38244
2020-07-29 10:32:06 -07:00
d0f625305b refactor(compiler-cli): support type-checking a single component (#38105)
This commit adds a method `getDiagnosticsForComponent` to the
`TemplateTypeChecker`, which does the minimum amount of work to retrieve
diagnostics for a single component.

With the normal `ReusedProgramStrategy` this offers virtually no improvement
over the standard `getDiagnosticsForFile` operation, but if the
`TypeCheckingProgramStrategy` supports separate shims for each component,
this operation can yield a faster turnaround for components that are
declared in files with many other components.

PR Close #38105
2020-07-29 10:31:22 -07:00
c15a58fa2b refactor(compiler-cli): add TemplateId to template diagnostics (#38105)
Previously, a stable template id was implemented for each component in a
file. This commit adds this id to each `TemplateDiagnostic` generated from
the template type-checker, so it can potentially be used for filtration.

PR Close #38105
2020-07-29 10:31:22 -07:00
3d67d1970b refactor(compiler-cli): allow overriding templates in the type checker (#38105)
This commit adds an `overrideComponentTemplate` operation to the template
type-checker. This operation changes the template used during template
type-checking operations.

Overriding a template causes any previous work for it to be discarded, and
the template type-checking engine will regenerate the TCB for that template
on the next request.

This operation can be used by a consumer such as the language service to
get rapid feedback or diagnostics as the user is editing a template file,
without the need for a full incremental build iteration.

Closes #38058

PR Close #38105
2020-07-29 10:31:22 -07:00
ce08a3c8c7 refactor(compiler-cli): efficient single-file type checking diagnostics (#38105)
Previously, the `TemplateTypeChecker` abstraction allowed fetching
diagnostics for a single file, but under the hood would generate type
checking code for the entire program to satisfy the request.

With this commit, an `OptimizeFor` hint is passed to `getDiagnosticsForFile`
which indicates whether the user intends to request diagnostics for the
whole program or is truly interested in just the single file. If the latter,
the `TemplateTypeChecker` can perform only the work needed to produce
diagnostics for just that file, thus returning answers more efficiently.

PR Close #38105
2020-07-29 10:31:22 -07:00
bc29712271 refactor(compiler-cli): allow program strategies to opt out of inlining (#38105)
The template type-checking engine relies on the abstraction interface
`TypeCheckingProgramStrategy` to create updated `ts.Program`s for
template type-checking. The basic API is that the type-checking engine
requests changes to certain files in the program, and the strategy provides
an updated `ts.Program`.

Typically, such changes are made to 'ngtypecheck' shim files, but certain
conditions can cause template type-checking to require "inline" operations,
which change user .ts files instead. The strategy used by 'ngc' (the
`ReusedProgramStrategy`) supports these kinds of updates, but other clients
such as the language service might not always support modifying user files.

To accommodate this, the `TypeCheckingProgramStrategy` interface was
modified to include a `supportsInlineOperations` flag. If an implementation
specifies `false` for inline support, the template type-checking system will
return diagnostics on components which would otherwise require inline
operations.

Closes #38059

PR Close #38105
2020-07-29 10:31:22 -07:00
cc48b5c066 refactor(compiler-cli): introduce the TemplateTypeChecker abstraction (#38105)
This commit significantly refactors the 'typecheck' package to introduce a
new abstraction, the `TemplateTypeChecker`. To achieve this:

* a 'typecheck:api' package is introduced, containing common interfaces that
  consumers of the template type-checking infrastructure can depend on
  without incurring a dependency on the template type-checking machinery as
  a whole.
* interfaces for `TemplateTypeChecker` and `TypeCheckContext` are introduced
  which contain the abstract operations supported by the implementation
  classes `TemplateTypeCheckerImpl` and `TypeCheckContextImpl` respectively.
* the `TemplateTypeChecker` interface supports diagnostics on a whole
  program basis to start with, but the implementation is purposefully
  designed to support incremental diagnostics at a per-file or per-component
  level.
* `TemplateTypeChecker` supports direct access to the type check block of a
  component.
* the testing utility is refactored to be a lot more useful, and new tests
  are added for the new abstraction.

PR Close #38105
2020-07-29 10:31:22 -07:00
deba0e21f1 refactor(compiler-cli): make file/shim split 1:n instead of 1:1 (#38105)
Previously in the template type-checking engine, it was assumed that every
input file would have an associated type-checking shim. The type check block
code for all components in the input file would be generated into this shim.

This is fine for whole-program type checking operations, but to support the
language service's requirements for low latency, it would be ideal to be
able to check a single component in isolation, especially if the component
is declared along with many others in a single file.

This commit removes the assumption that the file/shim mapping is 1:1, and
introduces the concept of component-to-shim mapping. Any
`TypeCheckingProgramStrategy` must provide such a mapping.

To achieve this:

 * type checking record information is now split into file-level data as
   well as per-shim data.
 * components are now assigned a stable `TemplateId` which is unique to the
   file in which they're declared.

PR Close #38105
2020-07-29 10:31:21 -07:00
0469d9240a Revert "refactor(platform-browser): specify return type of parseEventName (#38089)"
This reverts commit c3ddc3d6b1.
2020-07-29 09:30:39 -07:00
cdda60a430 release: cut the v10.0.6 release 2020-07-28 14:38:28 -07:00
7570356bfa build: fix broken build (#38274)
```
export const __core_private_testing_placeholder__ = '';
```
This API should be removed. But doing so seems to break `google3` and
so it requires a bit of investigation. A work around is to mark it as
`@codeGenApi` for now and investigate later.

PR Close #38274
2020-07-28 12:31:00 -07:00
c4a97d822e Revert "ci: roll back phased review conditions" (#38259)
This reverts commit ca8eafc2983f983803cd03e4a08bf732672712dd.

PR Close #38259
2020-07-28 11:26:29 -07:00
fc4dfc5eb1 ci: only check active groups for the no-groups-above-this-* checks (#38259)
Since PullApprove starts all inactive groups as a pending state, to properly
assess if any groups we care about are pending we must only check the active
groups.  We additionally do this with rejected because the intention of the
reusable checks is around checking active rules only.

PR Close #38259
2020-07-28 11:26:28 -07:00
25d95dae6d docs(elements): update api doc for custom elements (#38252)
by adding cross-references to Angular Elements Overview guide.

PR Close #38252
2020-07-28 11:19:04 -07:00
1c4fcce2a1 fix(core): Attribute decorator attributeName is mandatory (#38131)
`Attribute` decorator has defined `attributeName` as optional but actually its
 mandatory and compiler throws an error if `attributeName` is undefined. Made
`attributeName` mandatory in the `Attribute` decorator to reflect this functionality

Fixes #32658

PR Close #38131
2020-07-28 11:17:25 -07:00
Ahn
6e73faaed7 docs(core): correct SomeService to SomeComponent (#38264)
PR Close #38264
2020-07-28 11:10:59 -07:00
41c9910613 docs: update api reference doc for router-link-active directive (#38247)
Edits and organizes the usage information for the directive.

PR Close #38247
2020-07-28 11:09:45 -07:00
aaddef213d ci: roll back phased review conditions (#38257)
It was determined that the list of 'pending' groups also included inactive groups.
That means that the 'no-groups-above-this-pending' would generally fail because
there's almost always some inactive group above it.

This commit removes the conditions for phased review while we
investigate if the inactive groups can be excluded.

PR Close #38257
2020-07-28 10:02:15 -07:00
02f3aee1db docs: Refactor module-types.md to make it easier to understand (#38206)
Project DOCS-736 to rewrite headings to focus on user tasks,
verify that the content is up-to-date and complete, and
add relevant links to other NgModule topics to improve readability.
Also addresses one of many issues in GitHub issue 21531.

PR Close #38206
2020-07-28 10:01:38 -07:00
c27ba96093 ci: add more owners for limited categories (#38170)
* Add alxhub, atscott, and AndrewKushnir to code owners
* Add atscott & AndrewKushnir to public-api and size-tracking

Follow-up to #37994

PR Close #38170
2020-07-28 10:01:05 -07:00
c5a474cb54 docs: Refactor ngmodule-vs-jsmodule.md to make it easier to understand (#38148)
Project DOCS-734 to rewrite headings to focus on user tasks,
verify that the content is up-to-date and complete, and
add relevant links to other NgModule topics to improve readability.
Also addresses one of many issues in GitHub issue 21531.

PR Close #38148
2020-07-28 10:00:29 -07:00
d5264f5645 fix(core): unify the signature between ngZone and noopZone (#37581)
Now we have two implementations of Zone in Angular, one is NgZone, the other is NoopZone.
They should have the same signatures, includes
1. properties
2. methods

In this PR, unify the signatures of the two implementations, and remove the unnecessary cast.

PR Close #37581
2020-07-28 09:59:49 -07:00
0cd4b87021 Revert "refactor(core): remove unused export (#38224)"
This reverts commit c6c8e15813.
2020-07-28 09:56:24 -07:00
b1e7775a8a fix(compiler-cli): Add support for string literal class members (#38226)
The current implementation of the TypeScriptReflectionHost does not account for members that
are string literals, i.e. `class A { 'string-literal-prop': string; }`

PR Close #38226
2020-07-27 15:26:27 -07:00
87f5feff11 docs: update api reference doc for router link directive (#38181)
Edits and organizes the usage information for the directive.

PR Close #38181
2020-07-27 15:25:45 -07:00
c3ddc3d6b1 refactor(platform-browser): specify return type of parseEventName (#38089)
This commit refactors the argument of the `parseEventName` function
to use an object with named properties instead of using an object indexer.

PR Close #38089
2020-07-27 15:25:00 -07:00
cec39a7d16 test: update ts-api-guardian's strip_export_pattern to exclude Ivy instructions (#38224)
Previously the instructions were included in the golden files to monitor the frequency and rate of
the instruction API changes for the purpose of understanding the stability of this API (as it was
considered for becoming a public API and deployed to npm via generated code).

This experiment has confirmed that the instruction API is not stable enough to be used as public
API. We've since also came up with an alternative plan to compile libraries with the Ivy compiler
for npm deployment and this plan does not rely on making Ivy instructions public.

For these reasons, I'm removing the instructions from the golden files as it's no longer important
to track them.

The are three instructions that are still being included: `ɵɵdefineInjectable`, `ɵɵinject`, and
`ɵɵInjectableDef`.

These instructions are already generated by the VE compiler to support tree-shakable providers, and
code depending on these instructions is already deployed to npm. For this reason we need to treat
them as public api.

This change also reduces the code review overhead, because changes to public api golden files now
require multiple approvals.

PR Close #38224
2020-07-27 15:23:28 -07:00
c6c8e15813 refactor(core): remove unused export (#38224)
This export used to be here to turn this file into an ES Module - this is no longer needed
because the file contains imports.

PR Close #38224
2020-07-27 15:10:24 -07:00
752fd14fe5 refactor: correct @publicApi and @codeGenApi markers in various files (#38224)
The markers were previously incorrectly assigned. I noticed the issues when reviewing
the golden files and this change corrects them.

PR Close #38224
2020-07-27 15:10:18 -07:00
776067cd43 fix(zone.js): zone patch rxjs should return null _unsubscribe correctly. (#37091)
Close #31684.

In some rxjs operator, such as `retryWhen`, rxjs internally will set
`Subscription._unsubscribe` method to null, and the current zone.js monkey patch
didn't handle this case correctly, even rxjs set _unsubscribe to null, zone.js
still return a function by finding the prototype chain.

This PR fix this issue and the following test will pass.

```
const errorGenerator = () => {
  return throwError(new Error('error emit'));
};

const genericRetryStrategy = (finalizer: () => void) => (attempts: Observable<any>) =>
    attempts.pipe(
      mergeMap((error, i) => {
        const retryAttempt = i + 1;
        if (retryAttempt > 3) {
          return throwError(error);
        }
        return timer(retryAttempt * 1);
      }),
      finalize(() => finalizer()));

errorGenerator()
  .pipe(
    retryWhen(genericRetryStrategy(() => {
      expect(log.length).toBe(3);
      done();
    })),
    catchError(error => of(error)))
  .subscribe()
```

PR Close #37091
2020-07-27 12:11:27 -07:00
e87a46be21 docs: add template ref var to glossary (#36743)
There is not an entry in the glossary for template
reference variable. To clarify for site visitors,
we are adding one here.

PR Close #36743
2020-07-27 12:01:47 -07:00
89a7ff3ada docs: fix breaking URL for RxJS marble testing (#38209)
When checking this URL for the `RxJS marble testing` Ive found it pointing to `Page not found`

PR Close #38209
2020-07-27 11:12:08 -07:00
3d6e50dc02 docs: clarify the description of pipes (#37950)
This commit clarifies some of the language regarding pipes in the pipes guide.
This commit also specifies the term transforming rather than formatting.

PR Close #37950
2020-07-27 10:23:21 -07:00
264950bbf2 fix(compiler): share identical stylesheets between components in the same file (#38213)
Prior to this commit, duplicated styles defined in multiple components in the same file were not
shared between components, thus causing extra payload size. This commit updates compiler logic to
use `ConstantPool` for the styles (while generating the `styles` array on component def), which
enables styles sharing when needed (when duplicates styles are present).

Resolves #38204.

PR Close #38213
2020-07-27 10:11:56 -07:00
84c5be0b5b refactor(compiler): allow strings with certain length to be included into ConstantPool (#38213)
Prior to this commit, the `ConstantPool` ignored all primitive values. It turned out that it's
beneficial to include strings above certain length to the pool as well. This commit updates the
`ConstantPool` logic to allow such strings to be shared across multiple instances if needed.
For instance, this is helpful for component styles that might be reused across multiple components
in the same file.

PR Close #38213
2020-07-27 10:09:48 -07:00
eda8f2f8b9 refactor(compiler): separate compilation and transform phases (#38213)
This commit splits the transformation into 2 separate steps: Ivy compilation and actual transformation
of corresponding TS nodes. This is needed to have all `o.Expression`s generated before any TS transforms
happen. This allows `ConstantPool` to properly identify expressions that can be shared across multiple
components declared in the same file.

Resolves #38203.

PR Close #38213
2020-07-27 10:09:33 -07:00
cc52945d00 docs: add ng-add save option (#38198)
PR Close #38198
2020-07-27 09:52:15 -07:00
07f184a69d refactor(router): extract Router config utils to a separate file (#38229)
This commit refactors Router package to move config utils to a separate file for better
organization and to resolve the problem with circular dependency issue.

Resolves #38212.

PR Close #38229
2020-07-27 09:49:14 -07:00
a123ef58b1 fix(dev-infra): Ensure conditions with groups do not fail verification (#37798)
There are a few changes in this PR to ensure conditions that are based
on groups (i.e. `- groups.pending.length == 0`) do not fail the verify
task:

* Remove the warning when a condition is encountered that depends on the
`groups` state. The warning will otherwise be printed once for every
file that triggers the execution of the condition (400,000+ times)
* Add an `unverifiable` flag to `GroupCondition` interface and set it to
true when an error is encountered due to attempting to get the state of
`groups` in a condition
* Ignore any unverifiable conditions when gathering unmatched
conditions. These should not be considered `unmatched` for verification
purposes.
* Print the unverifiable conditions by group in the results

Sample output:
```

┌──────────────────────────────────────────────────────────────────────────────┐
│                         PullApprove results by group                         │
└──────────────────────────────────────────────────────────────────────────────┘
Groups skipped (4 groups)
Matched conditions by Group (37 groups)
Unmatched conditions by Group (0 groups)
Unverifiable conditions by Group (3 groups)
  [public-api]
    len(groups.pending.exclude("required-minimum-review")...
    len(groups.rejected.exclude("required-minimum-review")...
  [size-tracking]
    len(groups.pending.exclude("required-minimum-review")...
    len(groups.rejected.exclude("required-minimum-review")...
  [circular-dependencies]
    len(groups.pending.exclude("required-minimum-review")...
    len(groups.rejected.exclude("required-minimum-review")...

```

PR Close #37798
2020-07-24 18:02:49 -07:00
024126dde4 feat(dev-infra): add phased review to groups requiring final sign-off after initial review (#37798)
The size-tracking, public-api, and circular-dependencies groups can get a lot of
PRs to review. In addition, the members of these groups do not always
have the necessary context to fully review the PR in question. This
change ensures that the owners in the groups where the changes are being
made have approve the changes (ie, the aren't pending or rejected)
before requesting final sign-off from these three critical groups.

PR Close #37798
2020-07-24 18:02:42 -07:00
4275c34818 refactor(dev-infra): create anchors/aliases for excluded always active groups (#37798)
global-approvers, global-docs-approvers, and required-minimum-review groups are always active. It's useful
to have aliases for getting groups that are active/pending/rejected while excluding these few.

PR Close #37798
2020-07-24 18:02:19 -07:00
c4e6f585c5 fix(zone.js): patch nodejs EventEmtter.prototype.off (#37863)
Close #35473

zone.js nodejs patch should also patch `EventEmitter.prototype.off` as `removeListener`.
So `off` can correctly remove the listeners added by `EventEmitter.prototype.addListener`

PR Close #37863
2020-07-24 15:45:01 -07:00
7467fd36b9 fix(zone.js): clearTimeout/clearInterval should call on object global (#37858)
Close #37333

`clearTimeout` is patched by `zone.js`, and it finally calls the native delegate of `clearTimeout`,
the current implemention only call `clearNative(id)`, but it should call on object `global` like
`clearNative.call(global, id)`. Otherwise in some env, it will throw error
`clearTimeout called on an object that does not implement interface Window`

PR Close #37858
2020-07-24 15:22:48 -07:00
aca01985fd docs: Fix link by removing a space (#38214)
PR Close #38214
2020-07-24 09:53:06 -07:00
eb5e14e6e0 docs(core): Fix incorrectly rendered code example in structural directives guide (#38207)
The code example was missing a close brace and also incorrectly rendered
the template div as an actual div in the page DOM.

PR Close #38207
2020-07-24 09:52:31 -07:00
b8af10902f docs: fixed that class attribute is not closed (#38219)
PR Close #38219
2020-07-24 08:15:44 -07:00
f411c9e5b9 build(docs-infra): simplify ExampleZipper by removing PackageJsonCustomizer (#38192)
Previously, `ExampleZipper` (the tool used for creating ZIP archives
from our docs examples) used the `PackageJsonCustomizer` to generate
`package.json` files for each example type. This had the following
drawbacks:
- The generated files had to be kept up-to-date with the corresponding
  boilerplate files in `aio/tools/examples/shared/boilerplate/` and
  there was no easy way to find out when the files got out-of-sync.
- The `PackageJsonCustomizer` logic was non-trivial and difficult to
  reason about.
- The same information was duplicated in the boilerplate files and the
  customizer configuration files.

This setup was useful when we used a single `package.json` file for all
docs examples. Now, however, each example type can have its own
boilerplate `package.json` file, including scripts and dependencies
relevant to the example type. Therefore, it is no longer necessary to
generate `package.json` files for ZIP archives.

This commit eliminates the drawbacks mentioned above and simplifies the
`ExampleZipper` tool by removing `PackageJsonCustomizer` and re-using
the boilerplate `package.json` files for ZIP archives.

The changes in this commit also fix some ZIP archives that were
previously broken (for example due to missing dependencies).

PR Close #38192
2020-07-23 11:08:12 -07:00
7f455e6eec fix(docs-infra): correctly display copy button in IE11 (#38186)
Fix button top portion was clipped in IE11 by setting overflow to visible

Fixes #37816

PR Close #38186
2020-07-23 11:07:29 -07:00
e36caafa52 build(docs-infra): upgrade cli command docs sources to a404d2a86 (#38183)
Updating [angular#10.0.x](https://github.com/angular/angular/tree/10.0.x) from
[cli-builds#10.0.x](https://github.com/angular/cli-builds/tree/10.0.x).

##
Relevant changes in
[commit range](14af4e07c...a404d2a86):

**Modified**
- help/update.json

PR Close #38183
2020-07-23 11:06:55 -07:00
5e89d98876 refactor(dev-infra): Add support for groups in the conditions evaluator (#38164)
Conditions can refer to the groups array that is a list of the preceding groups.
This commit adds support to the verification for those conditions.

This commit also adds some tests to the parsing and condition matching
to ensure everything works as expected.

PR Close #38164
2020-07-23 11:05:43 -07:00
200dbd4860 docs: add Kevin Kreuzer to GDE page (#37929)
This commit adds Kevin Kreuzer to the Angular GDE page along with a biography, his contributions, and a photograph.

PR Close #37929
2020-07-23 11:03:58 -07:00
c90952884a docs: update dynamic-component loading guide (#36959)
The 'entryComponents' array is no longer a special case for dynamic component loading because of the Ivy compiler.

PR Close #36959
2020-07-23 11:03:02 -07:00
7c2d8fc672 docs: remove duplicate https:// (#38199)
This doc contained a duplicate `http://` before the domain name leading to an invalid link.
This commit fixes this issue.
PR Close #38199
2020-07-23 10:54:45 -07:00
a50a688aaf docs: update api reference for router outlet directive (#38166)
Incorporate more specific information about multiple outlets and how to target them, with link to tutorial example.

PR Close #38166
2020-07-22 20:50:13 -07:00
6ec7297e43 build(docs-infra): remove boilerplate file listings in example-boilerplate.js (#38173)
To avoid unnecessary code duplication in docs examples, we have some
boilerplate files for various example types (in
`aio/tools/examples/shared/boilerplate/`). These files are copied to
each example project in `aio/content/examples/` (according to the
example's type, as specified in its `example-config.json` file).

Previously, the `example-boilerplate.js`, which is responsible for
copying the boilerplate files, had lists for files to be copied for each
project type and only copied the listed files from the boilerplate
directory to the example directory. This approach had some drawbacks:
- Files need to be updated in two separate locations: in the boilerplate
  directory that includes the files and the file list in
  `example-boilerplate.js`.
- It is easy to add a file in the boilerplate directory but forget to
  add it in `example-boilerplate.js` and not realize that it is not
  being included in the example project (including the generated
  StackBlitz project and ZIP archive).

This commit changes the approach by removing the boilerplate file
listings from `example-boilerplate.js` and copying all files from a
boilerplate directory to example directories. This addresses the above
drawbacks and simplifies the `example-boilerplate.js` script.

I have verified that the resulting code example doc regions as well as
the generated StackBlitz projects and ZIP archives are identical to the
ones generated before this commit.

PR Close #38173
2020-07-22 10:15:10 -07:00
f264cd1cb8 fix(docs-infra): include .gitignore file in CLI-based docs examples (#38173)
Previously, the `.gitignore` file that is part of the boilerplate files
for CLI-based docs examples (located in
`aio/tools/examples/shared/boilerplate/cli/`) was not added to the
example projects, because it was not included in the boilerplate file
list in `example-boilerplate.js`.

This commit fixes it by adding the `.gitignore` file to the list. This
ensures that docs examples more closely match CLI-generated projects.

PR Close #38173
2020-07-22 10:15:10 -07:00
fc17bddcde fix(docs-infra): correctly add polyfills.ts file as boilerplate for i18n docs examples (#38173)
Docs examples of type `i18n` need a slightly modified version of
`polyfills.ts` that imports `@angular/localize/init`. Previously, this
file was not included in `i18n` example projects for two reasons:

- While the file was included in the `i18n` boilerplate files (at
  `aio/tools/examples/shared/boilerplate/i18n/`), it was not included in
  the boilerplate file list in `example-boilerplate.js`.
- The file was in the wrong location: It was located at the project root
  instead of inside the `src/` directory.

This commit addresses the above issues (i.e. adds the file to the
boilerplate file list for `i18n` projects and moves the file inside the
`src/` directory).

PR Close #38173
2020-07-22 10:15:10 -07:00
0765626761 build(docs-infra): remove obsolete systemjs.config.web[.build].js files from docs examples (#38173)
There were some `systemjs.config.web[.build].js` files in the `systemjs`
boilerplate directory that are not used any more. In the past, these
files were used in the Plunker-based live examples, but we no longer use
Plunker for live examples.

This commit removes these obsolete files.

PR Close #38173
2020-07-22 10:15:10 -07:00
f146b34042 build(docs-infra): remove obsolete typings.d.ts files from angular.io and docs examples (#38173)
There were two `typings.d.ts` files with SystemJS module definitions in
`aio/src/` and `aio/tools/examples/shared/boilerplate/cli/`. These are
remnants from old CLI versions that used SystemJS and are no longer
needed. For docs examples specifically, these files were never copied
over to example projects and thus not included in StackBlitz projects
and ZIP archives.

This commit removes these obsolete files.

PR Close #38173
2020-07-22 10:15:10 -07:00
f899d6ea44 docs: fix typo in ng_control.ts (#38157)
PR Close #38157
2020-07-22 10:14:24 -07:00
c18d7a1469 docs: fix typo from singular to plural spelling (#36586)
This commit fixes the spelling of the singular form
of the word function to the plural spelling in
packages/core/src/application_init.ts

PR Close #36586
2020-07-22 10:12:45 -07:00
2c7ff82f31 release: cut the v10.0.5 release 2020-07-22 09:36:00 -07:00
3f8f3a2daa docs: fix type in lazy-load callout (#38153)
PR Close #38153
2020-07-21 14:30:48 -07:00
f5eeb1a714 docs: fix DOCUMENT import path (#38158) (#38159)
PR Close #38159
2020-07-21 11:35:42 -07:00
2af3d9c040 refactor(core): rename synthetic host property and listener instructions (#38150)
This commit updates synthetic host property and listener instruction names to better align with other instructions.
The `ɵɵupdateSyntheticHostBinding` instruction was renamed to `ɵɵsyntheticHostProperty` (to match the `ɵɵhostProperty`
instruction name) and `ɵɵcomponentHostSyntheticListener` was renamed to `ɵɵsyntheticHostListener` since this
instruction is generated for both Components and Directives (so 'component' is removed from the name).
This PR is a followup after PR #35568.

PR Close #38150
2020-07-21 09:11:49 -07:00
4664acce50 docs: add routing terms to glossary (#38053)
Update glossary to add term definitions for routing; componentless route, link parameters array, router outlet.

PR Close #38053
2020-07-20 17:09:34 -07:00
3797861dfe docs(router): fix typos (#38132)
PR Close #38132
2020-07-20 14:12:22 -07:00
d6d3984524 docs(common): fix selector field in NgIfAs example component (#35854)
PR Close #35854
2020-07-20 13:35:05 -07:00
c17f5c10cc build: update pullapprove to remove @matsko from reviewer lists (#38146)
With @matsko leaving the Angular team, we need to update the pullapprove
configuration to reflect his no longer being a reviewer for file groups
throughout the repository.

PR Close #38146
2020-07-20 13:33:59 -07:00
9cb318f5a2 docs: separate template syntax into multiple docs (#36954)
This is part of a re-factor of template syntax and
structure. The first phase breaks out template syntax
into multiple documents. The second phase will be
a rewrite of each doc.

Specifically, this PR does the following:

- Breaks sections of the current template syntax document each into their own page.
- Corrects the links to and from these new pages.
- Adds template syntax subsection to the left side NAV which contains all the new pages.
- Adds the new files to pullapprove.

PR Close #36954
2020-07-20 11:16:45 -07:00
b026dd8b52 docs: add known issue for bazel (#38074)
This commit adds a known issue for windows, when running tests using
`bazal run`, instead of using `bazel test`

PR Close #38074
2020-07-20 11:15:37 -07:00
0ebc316311 refactor(dev-infra): Update triage labels documentation (#38081)
Add new type: confusing and type: use-case labels to the triage readme as well
as clarify that freq and severity are only required for type: bug/fix

PR Close #38081
2020-07-20 11:14:48 -07:00
dc412c5f02 refactor(dev-infra): allow use-case and confusing types to be marked as 'triaged' (#38081)
Some issue reports don't really fall into any of the current buckets that count
towards triage level 2: bug/fix, feature, or refactor. Some reports are:
* working as intended but confusing - the labels might be 'type: confusing', 'comp: docs', 'comp: router'
* generally working as originally designed but a use-case could be argued for a different implementation.
 This type of report is a little hard to triage; it may be neither a bug, nor feature but more of a
 'type: use-case'. These may eventually turn into a bug/fix or feature, but can't necessarily be
 put in those buckets immediately.

PR Close #38081
2020-07-20 11:14:47 -07:00
e80278cf02 fix(compiler): properly associate source spans for implicitly closed elements (#38126)
HTML is very lenient when it comes to closing elements, so Angular's parser has
rules that specify which elements are implicitly closed when closing a tag.
The parser keeps track of the nesting of tag names using a stack and parsing
a closing tag will pop as many elements off the stack as possible, provided
that the elements can be implicitly closed.

For example, consider the following templates:

- `<div><br></div>`, the `<br>` is implicitly closed when parsing `</div>`,
  because `<br>` is a void element.
- `<div><p></div>`, the `<p>` is implicitly closed when parsing `</div>`,
  as `<p>` is allowed to be closed by the closing of its parent element.
- `<ul><li>A <li>B</ul>`, the first `<li>` is implicitly closed when parsing
  the second `<li>`, whereas the second `<li>` would be implicitly closed when
  parsing the `</ul>`.

In all the cases above the parsed structure would be correct, however the source
span of the closing `</div>` would incorrectly be assigned to the element that
is implicitly closed. The problem was that closing an element would associate
the source span with the element at the top of the stack, however this may not
be the element that is actually being closed if some elements would be
implicitly closed.

This commit fixes the issue by assigning the end source span with the element
on the stack that is actually being closed. Any implicitly closed elements that
are popped off the stack will not be assigned an end source span, as the
implicit closing implies that no ending element is present.

Note that there is a difference between self-closed elements such as `<input/>`
and implicitly closed elements such as `<input>`. The former does have an end
source span (identical to its start source span) whereas the latter does not.

Fixes #36118
Resolves FW-2004

PR Close #38126
2020-07-20 10:02:07 -07:00
307699ac89 refactor(compiler): remove unused parser methods (#38126)
These methods are no longer used so they can safely be removed.

PR Close #38126
2020-07-20 10:02:06 -07:00
4df0b7e9de build(language-service): Remove ls_rollup_bundle (#38086) (#38129)
`ls_rollup_bundle` is no longer needed since we could invoke `ng_rollup_bundle`
directly.

Background: language service runs rollup to produce a single file to reduce
startup time in the editor. However, due to the need to load dynamic versions
of typescript at runtime (think the case where users can change typescript
version in their editor), we hack the "banner" to export a CommonJS default function,
so that we could dynamically load the typescript module provided at runtime via AMD
and use it throughout the implementation.

PR Close #38086

PR Close #38129
2020-07-20 10:00:55 -07:00
371831f9cb docs(core): add note about not mutating multi provider arrays (#37645)
Adds a note to the provider docs that users shouldn't mutate an array that
is returned from a `multi` provider, because it can cause unforeseen
consequences in other parts of the app.

Closes #37481.

PR Close #37645
2020-07-20 10:00:06 -07:00
8e305e7099 docs: correct flag default values in --strict (#37982)
Docs state that `strictInjectionParameters` is true by default in `ng new`, however this is not the case in `10.0.1`. It is only set when `--strict` is provided. Clarified that the `--strict` flag is required.

`strictTemplates` does not mention anything about `--strict`, so I included a similar point that it is `true` when a new project is generated with `--strict`.

PR Close #37982
2020-07-17 16:26:50 -07:00
481df830ad build: Ignore .history for the xyz.local-history VSCode extension (#38121)
Ignore .history for the xyz.local-history VSCode extension

PR Close #38121
2020-07-17 13:33:40 -07:00
14b4718cc2 fix(core): Allow modification of lifecycle hooks any time before bootstrap (#38119)
Currently we read lifecycle hooks eagerly during `ɵɵdefineComponent`.
The result is that it is not possible to do any sort of meta-programing
such as mixins or adding lifecycle hooks using custom decorators since
any such code executes after `ɵɵdefineComponent` has extracted the
lifecycle hooks from the prototype. Additionally the behavior is
inconsistent between AOT and JIT mode. In JIT mode overriding lifecycle
hooks is possible because the whole `ɵɵdefineComponent` is placed in
getter which is executed lazily. This is because JIT mode must compile a
template which can be specified as `templateURL` and those we are
waiting for its resolution.

- `+` `ɵɵdefineComponent` becomes smaller as it no longer needs to copy
  lifecycle hooks from prototype to `ComponentDef`
- `-` `ɵɵNgOnChangesFeature` feature is now always included with the
  codebase as it is no longer tree shakable.

Previously we have read lifecycle hooks from prototype in the
`ɵɵdefineComponent` so that lifecycle hook access would be monomorphic.
This decision was made before we had `T*` data structures. By not
reading the lifecycle hooks we are moving the megamorhic read form
`ɵɵdefineComponent` to instructions. However, the reads happen on
`firstTemplatePass` only and are subsequently cached in the `T*` data
structures. The result is that the overall performance should be same
(or slightly better as the intermediate `ComponentDef` has been
removed.)

- [ ] Remove `ɵɵNgOnChangesFeature` from compiler. (It will no longer
      be a feature.)
- [ ] Discuss the future of `Features` as they hinder meta-programing.

Fix #30497

PR Close #38119
2020-07-17 13:14:35 -07:00
7b6e73cb98 fix(core): error due to integer overflow when there are too many host bindings (#38014)
We currently use 16 bits to store information about nodes in a view.
The 16 bits give us 65536 entries in the array, but the problem is that while
the number is large, it can be reached by ~4300 directive instances with host
bindings which could realistically happen is a very large view, as seen in #37876.
Once we hit the limit, we end up overflowing which eventually leads to a runtime error.

These changes bump to using 20 bits which gives us around 1048576 entries in
the array or 16 times more than the current amount which could still technically
be reached, but is much less likely and the user may start hitting browser limitations
by that point.

I picked the 20 bit number since it gives us enough buffer over the 16 bit one,
while not being as massive as a 24 bit or 32 bit.

I've also added a dev mode assertion so it's easier to track down if it happens
again in the future.

Fixes #37876.

PR Close #38014
2020-07-17 12:58:16 -07:00
f2ca4634e2 fix(docs-infra): correctly display SVG icons in IE11 (#38046)
Fix two issues that affected displaying of SVG icons in IE11:

1. All SVG icons except for one appeared empty. This was related how the
CustomIconRegistry re-used the same <div> element to create all
SVG elements.

2. The GitHub and Twitter buttons next to the search bar were not sized
properly.

Fixes #37847

PR Close #38046
2020-07-17 11:44:35 -07:00
d30cf2f9d6 docs(docs-infra): reformat redundant sentence (#38109)
reformat sentence uses the npm package manager since npm is node package manager

Fixes #38106

PR Close #38109
2020-07-17 11:39:17 -07:00
e9cb6fbe87 build(language-service): add script to build package locally (#38103)
This commit adds a script to build @angular/language-service
locally so that it can be consumed by the Angular extension for
local development.

PR Close #38103
2020-07-16 16:39:55 -07:00
99960a98d2 docs: update router api documentation (#37980)
Edit descriptions, usage examples, and add links to be complete and consistent with API reference doc style

PR Close #37980
2020-07-16 13:52:41 -07:00
9ac3383d01 build(language-service): remove typescript from ivy bundle (#38088)
Currently the Ivy language service bundle is [10MB](
https://unpkg.com/browse/@angular/language-service@10.0.4/bundles/) because we
accidentally included typescript in the bundle.

With this change, the bundle size goes down to 1.6MB, which is even smaller
than the View Engine bundle (1.8MB).

```bash
$ yarn bazel build //packages/language-service/bundles:ivy
$ ls -lh dist/bin/packages/language-service/bundles/ivy.umd.js
1.6M Jul 15 15:49 dist/bin/packages/language-service/bundles/ivy.umd.js
```

PR Close #38088
2020-07-16 11:04:58 -07:00
06ac75724f ci: add more owners for some categories (#37994)
* Add petebacondarwin to public-api, size-tracking, and circular-dependencies
* Add mhevery, josephperrott, and jelbourn to code-ownership

PR Close #37994
2020-07-16 11:03:56 -07:00
a1d691ecc8 docs: add Ajit Singh to the collaborators (#37792)
Ajit Singh is a newly added collborator after a few months of contributing add him to the contributors.json

PR Close #37792
2020-07-16 11:02:08 -07:00
6e329721be docs: add Emma Twersky to DevRel Contributor page (#38084)
This commit adds Emma Twersky to the Angular Contributors page along with a bio & a photograph.

PR Close #38084
2020-07-15 13:50:40 -07:00
739bf5c325 docs: fix typo in "Creating libraries" guide (by publishing...ensures --> publishing...ensures) (#38032)
PR Close #38032
2020-07-15 13:12:15 -07:00
ee340b7c6c docs(service-worker): fix typos in SwRegistrationOptions API docs (#38047)
PR Close #38047
2020-07-15 13:10:26 -07:00
17ddab98fb fix(core): incorrectly validating properties on ng-content and ng-container (#37773)
Fixes the following issues related to how we validate properties during JIT:
- The invalid property warning was printing `null` as the node name
for `ng-content`. The problem is that when generating a template from
 `ng-content` we weren't capturing the node name.
- We weren't running property validation on `ng-container` at all.
This used to be supported on ViewEngine and seems like an oversight.

In the process of making these changes, I found and cleaned up a
few places where we were passing in `LView` unnecessarily.

PR Close #37773
2020-07-15 12:39:40 -07:00
4f65f473e4 ci(docs-infra): increase minimum a11y scores for various pages (#37899)
As part of our CI checks, we ensure the a11y score on certain angular.io
pages do not fall below some thresholds.

This commit increases these thresholds based on our current scores to
ensure we do not regress below current values.

PR Close #37899
2020-07-15 12:38:08 -07:00
527a04d21e build(docs-infra): upgrade lighthouse to 6.1.0 (#37899)
To take advantage of lazy loaded images `img[loading=lazy]`, this commit
upgrades lighthouse to version 6.1.0.

Closes #35965

PR Close #37899
2020-07-15 12:38:08 -07:00
f2ee468d76 fix(dev-infra): add missing BUILD file to dev-infra/bazel:files (#38026)
* Without this BUILD file we were seeing errors about the reference to
  expand_template.bzl in ng_rollup_bundle.bzl because dev-infra/bazel
  was not considered a package.

PR Close #38026
2020-07-15 12:34:47 -07:00
0320096538 fix(dev-infra): fix broken zone.js substitution for dev-infra:npm_package (#38026)
* fix substitution that was broken by PR #36540 to match
  the new import path

PR Close #38026
2020-07-15 12:34:47 -07:00
7abb48adfe feat(dev-infra): add bazel firefox browser with RBE compatibility (#38029)
Adds Firefox as browser to `dev-infra/browsers` with RBE
compatibility. The default Firefox browser is not compatible similar to
the default Chromium version exposed by `rules_webtesting`.

The Angular Components repository will use this browser target as
it enables RBE support. Also it gives us more flexibility about
the Firefox version we test against. The version provided by
`rules_webtesting` is very old and most likely not frequently
updated (based on past experience).

PR Close #38029
2020-07-15 12:34:06 -07:00
b40d3c0817 docs: update reference doc for router guards and resolvers (#38079)
Complete and clarify descriptions and example of the guard and resolver functions in Router API documentation.

PR Close #38079
2020-07-15 12:32:12 -07:00
0e5617152a docs: remove all references to Angular Console (#37608)
Angular Console has been renamed and links no longer work. It has been decided to remove references to this third-party tool from the AIO documentation.

Closes #37604

PR Close #37608
2020-07-15 12:30:59 -07:00
dba402344f fix(compiler-cli): ensure file_system handles mixed Windows drives (#38030)
The `fs.relative()` method assumed that the file-system is a single tree,
which is not the case in Windows, where you can have multiple drives,
e.g. `C:`, `D:` etc.

This commit changes `fs.relative()` so that it no longer forces the result
to be a `PathSegment` and then flows that refactoring through the rest of
the compiler-cli (and ngcc).  The main difference is that now, in some cases,
we needed to check whether the result is "rooted", i.e an `AbsoluteFsPath`,
rather than a `PathSegment`, before using it.

Fixes #36777

PR Close #38030
2020-07-15 12:29:44 -07:00
878 changed files with 18360 additions and 11866 deletions

View File

@ -136,15 +136,6 @@ build:remote --remote_executor=remotebuildexecution.googleapis.com
# retry mechanism and we do not want to retry unnecessarily if Karma already tried multiple times.
test:saucelabs --flaky_test_attempts=1
###############################
# NodeJS rules settings
# These settings are required for rules_nodejs
###############################
# Turn on managed directories feature in Bazel
# This allows us to avoid installing a second copy of node_modules
common --experimental_allow_incremental_repository_updates
####################################################
# User bazel configuration
# NOTE: This needs to be the *last* entry in the config.

View File

@ -32,8 +32,8 @@ var_4_win: &cache_key_win_fallback v7-angular-win-node-12-{{ checksum ".bazelver
# Cache key for the `components-repo-unit-tests` job. **Note** when updating the SHA in the
# cache keys also update the SHA for the "COMPONENTS_REPO_COMMIT" environment variable.
var_5: &components_repo_unit_tests_cache_key v7-angular-components-f428c00465dfcf8a020237f22532480eedbd2cb6
var_6: &components_repo_unit_tests_cache_key_fallback v7-angular-components-
var_5: &components_repo_unit_tests_cache_key v9-angular-components-09e68db8ed5b1253f2fe38ff954ef0df019fc25a
var_6: &components_repo_unit_tests_cache_key_fallback v9-angular-components-
# Workspace initially persisted by the `setup` job, and then enhanced by `build-npm-packages` and
# `build-ivy-npm-packages`.
@ -656,6 +656,18 @@ jobs:
- run: yarn tsc -p packages
- run: yarn tsc -p modules
- run: yarn bazel build //packages/zone.js:npm_package
# Build test fixtures for a test that rely on Bazel-generated fixtures. Note that disabling
# specific tests which are reliant on such generated fixtures is not an option as SystemJS
# in the Saucelabs legacy job always fetches referenced files, even if the imports would be
# guarded by an check to skip in the Saucelabs legacy job. We should be good running such
# test in all supported browsers on Saucelabs anyway until this job can be removed.
- run:
name: Preparing Bazel-generated fixtures required in legacy tests
command: |
yarn bazel build //packages/core/test:downleveled_es5_fixture
# Needed for the ES5 downlevel reflector test in `packages/core/test/reflection`.
cp dist/bin/packages/core/test/reflection/es5_downleveled_inheritance_fixture.js \
dist/all/@angular/core/test/reflection/es5_downleveled_inheritance_fixture.js
- run:
# Waiting on ready ensures that we don't run tests too early without Saucelabs not being ready.
name: Waiting for Saucelabs tunnel to connect

View File

@ -74,7 +74,7 @@ setPublicVar COMPONENTS_REPO_TMP_DIR "/tmp/angular-components-repo"
setPublicVar COMPONENTS_REPO_URL "https://github.com/angular/components.git"
setPublicVar COMPONENTS_REPO_BRANCH "master"
# **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI `config.yml`.
setPublicVar COMPONENTS_REPO_COMMIT "f428c00465dfcf8a020237f22532480eedbd2cb6"
setPublicVar COMPONENTS_REPO_COMMIT "09e68db8ed5b1253f2fe38ff954ef0df019fc25a"
####################################################################################################

View File

@ -154,6 +154,12 @@ triage:
-
- "type: RFC / Discussion / question"
- "comp: *"
-
- "type: confusing"
- "comp: *"
-
- "type: use-case"
- "comp: *"
# options for the triage PR plugin
triagePR:

3
.gitignore vendored
View File

@ -42,3 +42,6 @@ yarn-error.log
.notes.md
baseline.json
# Ignore .history for the xyz.local-history VSCode extension
.history

View File

@ -7,18 +7,6 @@ export const commitMessage: CommitMessageConfig = {
maxLineLength: 120,
minBodyLength: 20,
minBodyLengthTypeExcludes: ['docs'],
types: [
'build',
'ci',
'docs',
'feat',
'fix',
'perf',
'refactor',
'release',
'style',
'test',
],
scopes: [
'animations',
'bazel',

View File

@ -67,6 +67,25 @@ version: 3
# Meta field that goes unused by PullApprove to allow for defining aliases to be
# used throughout the config.
meta:
# The following groups have no file based conditions and will be initially `active` on all PRs
# - `global-approvers`
# - `global-docs-approvers`
# - `required-minimum-review`
#
# By checking the number of active/pending/rejected groups when these are excluded, we can determine
# if any other groups are matched.
#
# Note: Because all inactive groups start as pending, we are only checking pending and rejected active groups.
#
# Also note that the ordering of groups matters in this file. The only groups visible to the current
# one are those that appear above it.
no-groups-above-this-pending: &no-groups-above-this-pending
len(groups.active.pending.exclude("required-minimum-review").exclude("global-approvers").exclude("global-docs-approvers")) == 0
no-groups-above-this-rejected: &no-groups-above-this-rejected
len(groups.active.rejected.exclude("required-minimum-review").exclude("global-approvers").exclude("global-docs-approvers")) == 0
no-groups-above-this-active: &no-groups-above-this-active
len(groups.active.exclude("required-minimum-review").exclude("global-approvers").exclude("global-docs-approvers")) == 0
can-be-global-approved: &can-be-global-approved "\"global-approvers\" not in groups.approved"
can-be-global-docs-approved: &can-be-global-docs-approved "\"global-docs-approvers\" not in groups.approved"
defaults: &defaults
@ -136,7 +155,6 @@ groups:
- kara # Kara Erickson
- kyliau # Keen Yee Liau
- manughub # Manu Murthy
- matsko # Matias Niemela
- mgechev # Minko Gechev
- mhevery # Miško Hevery
- michaelprentice # Michael Prentice
@ -203,7 +221,9 @@ groups:
])
reviewers:
users:
- matsko
- crisbeto
- IgorMinar
- jelbourn
# =========================================================
@ -342,20 +362,31 @@ groups:
'aio/content/guide/ngmodule-vs-jsmodule.md',
'aio/content/guide/module-types.md',
'aio/content/guide/template-syntax.md',
'aio/content/guide/built-in-template-functions.md',
'aio/content/examples/built-in-template-functions/**',
'aio/content/guide/event-binding.md',
'aio/content/examples/event-binding/**',
'aio/content/guide/interpolation.md',
'aio/content/examples/interpolation/**',
'aio/content/examples/template-syntax/**',
'aio/content/images/guide/template-syntax/**',
'aio/content/guide/binding-syntax.md',
'aio/content/examples/binding-syntax/**',
'aio/content/guide/property-binding.md',
'aio/content/examples/property-binding/**',
'aio/content/guide/attribute-binding.md',
'aio/content/examples/attribute-binding/**',
'aio/content/guide/two-way-binding.md',
'aio/content/examples/two-way-binding/**',
'aio/content/guide/built-in-directives.md',
'aio/content/examples/built-in-directives/**',
'aio/content/images/guide/built-in-directives/**',
'aio/content/guide/template-reference-variables.md',
'aio/content/examples/template-reference-variables/**',
'aio/content/guide/inputs-outputs.md',
'aio/content/examples/inputs-outputs/**',
'aio/content/images/guide/inputs-outputs/**',
'aio/content/guide/template-expression-operators.md',
'aio/content/examples/template-expression-operators/**',
'aio/content/guide/pipes.md',
'aio/content/examples/pipes/**',
@ -370,7 +401,9 @@ groups:
'aio/content/guide/sharing-ngmodules.md',
'aio/content/guide/structural-directives.md',
'aio/content/examples/structural-directives/**',
'aio/content/guide/svg-in-templates.md',
'aio/content/images/guide/structural-directives/**',
'aio/content/guide/template-statements.md',
'aio/content/guide/user-input.md',
'aio/content/examples/user-input/**',
'aio/content/images/guide/user-input/**'
@ -472,8 +505,8 @@ groups:
- >
contains_any_globs(files, [
'packages/core/src/i18n/**',
'packages/core/src/render3/i18n.ts',
'packages/core/src/render3/i18n.md',
'packages/core/src/render3/i18n/**',
'packages/core/src/render3/instructions/i18n.ts',
'packages/core/src/render3/interfaces/i18n.ts',
'packages/common/locales/**',
'packages/common/src/i18n/**',
@ -830,6 +863,7 @@ groups:
- *can-be-global-docs-approved
- >
contains_any_globs(files, [
'aio/content/guide/roadmap.md',
'aio/content/marketing/**',
'aio/content/images/bios/**',
'aio/content/images/marketing/**',
@ -1109,6 +1143,8 @@ groups:
public-api:
<<: *defaults
conditions:
- *no-groups-above-this-pending
- *no-groups-above-this-rejected
- *can-be-global-approved
- >
contains_any_globs(files, [
@ -1122,13 +1158,16 @@ groups:
])
reviewers:
users:
- alxhub
- AndrewKushnir
- IgorMinar
- alxhub
- atscott
- jelbourn
- petebacondarwin
- pkozlowski-opensource
reviews:
request: -1 # request reviews from everyone
required: 3 # require at least 3 approvals
request: 4 # Request reviews from four people
required: 3 # Require that three people approve
reviewed_for: required
@ -1138,6 +1177,8 @@ groups:
size-tracking:
<<: *defaults
conditions:
- *no-groups-above-this-pending
- *no-groups-above-this-rejected
- *can-be-global-approved
- >
contains_any_globs(files, [
@ -1145,13 +1186,16 @@ groups:
])
reviewers:
users:
- alxhub
- AndrewKushnir
- IgorMinar
- alxhub
- atscott
- jelbourn
- petebacondarwin
- pkozlowski-opensource
reviews:
request: -1 # request reviews from everyone
required: 2 # require at least 2 approvals
request: 4 # Request reviews from four people
required: 2 # Require that two people approve
reviewed_for: required
@ -1161,6 +1205,8 @@ groups:
circular-dependencies:
<<: *defaults
conditions:
- *no-groups-above-this-pending
- *no-groups-above-this-rejected
- *can-be-global-approved
- >
contains_any_globs(files, [
@ -1168,9 +1214,12 @@ groups:
])
reviewers:
users:
- AndrewKushnir
- IgorMinar
- alxhub
- atscott
- jelbourn
- josephperrott
- petebacondarwin
- pkozlowski-opensource
@ -1191,7 +1240,13 @@ groups:
])
reviewers:
users:
- AndrewKushnir
- IgorMinar
- alxhub
- atscott
- jelbourn
- josephperrott
- mhevery
# ====================================================
@ -1218,13 +1273,10 @@ groups:
# `global-approvers` can still approve PRs that match this `fallback` rule,
# but that should be an exception and not an expectation.
conditions:
- *no-groups-above-this-active
# When any of the `global-*` groups is approved, they cause other groups to deactivate.
# In those cases, the condition above would evaluate to `true` while in reality, only a global
# approval has been provided. To ensure we don't activate the fallback group in such cases,
# ensure that no explicit global approval has been provided.
- *can-be-global-approved
# The following groups have no conditions and will be `active` on all PRs
# - `global-approvers`
# - `global-docs-approvers`
#
# Since this means the minimum number of active groups a PR can have is 2, this
# `fallback` group should be matched anytime the number of active groups is at or
# below this minimum. This work as a protection to ensure that pullapprove does
# not incidently mark a PR as passing without meeting the review criteria.
- len(groups.active) <= 2
- *can-be-global-docs-approved

View File

@ -26,6 +26,7 @@
"**/bazel-out": true,
"**/dist": true,
"**/aio/src/generated": true,
".history": true,
},
"git.ignoreLimitWarning": true,
}
}

View File

@ -1,3 +1,88 @@
<a name="10.0.10"></a>
## 10.0.10 (2020-08-17)
### Bug Fixes
* **common:** Allow scrolling when browser supports scrollTo ([#38468](https://github.com/angular/angular/issues/38468)) ([b32126c](https://github.com/angular/angular/commit/b32126c)), closes [#30630](https://github.com/angular/angular/issues/30630)
* **core:** detect DI parameters in JIT mode for downleveled ES2015 classes ([#38500](https://github.com/angular/angular/issues/38500)) ([863acb6](https://github.com/angular/angular/commit/863acb6)), closes [#38453](https://github.com/angular/angular/issues/38453)
* **core:** error if CSS custom property in host binding has number in name ([#38432](https://github.com/angular/angular/issues/38432)) ([cb83b8a](https://github.com/angular/angular/commit/cb83b8a)), closes [#37292](https://github.com/angular/angular/issues/37292)
* **core:** fix multiple nested views removal from ViewContainerRef ([#38317](https://github.com/angular/angular/issues/38317)) ([d5e09f4](https://github.com/angular/angular/commit/d5e09f4)), closes [#38201](https://github.com/angular/angular/issues/38201)
* **ngcc:** detect synthesized delegate constructors for downleveled ES2015 classes ([#38500](https://github.com/angular/angular/issues/38500)) ([f3dd6c2](https://github.com/angular/angular/commit/f3dd6c2)), closes [#38453](https://github.com/angular/angular/issues/38453) [#38453](https://github.com/angular/angular/issues/38453)
* **router:** ensure routerLinkActive updates when associated routerLinks change ([#38349](https://github.com/angular/angular/issues/38349)) ([989e8a1](https://github.com/angular/angular/commit/989e8a1)), closes [#18469](https://github.com/angular/angular/issues/18469)
<a name="10.0.9"></a>
## 10.0.9 (2020-08-12)
### Bug Fixes
* **common:** ensure scrollRestoration is writable ([#30630](https://github.com/angular/angular/issues/30630)) ([#38357](https://github.com/angular/angular/issues/38357)) ([58f4b3a](https://github.com/angular/angular/commit/58f4b3a)), closes [#30629](https://github.com/angular/angular/issues/30629)
* **compiler:** evaluate safe navigation expressions in correct binding order ([#37911](https://github.com/angular/angular/issues/37911)) ([f5b9d87](https://github.com/angular/angular/commit/f5b9d87)), closes [#37194](https://github.com/angular/angular/issues/37194)
* **compiler-cli:** avoid creating value expressions for symbols from type-only imports ([#38415](https://github.com/angular/angular/issues/38415)) ([ca2b4bc](https://github.com/angular/angular/commit/ca2b4bc)), closes [#37912](https://github.com/angular/angular/issues/37912)
* **compiler-cli:** infer quote expressions as any type in type checker ([#37917](https://github.com/angular/angular/issues/37917)) ([5b87c67](https://github.com/angular/angular/commit/5b87c67)), closes [#36568](https://github.com/angular/angular/issues/36568)
* **compiler-cli:** mark eager `NgModuleFactory` construction as not side effectful ([#38320](https://github.com/angular/angular/issues/38320)) ([016a41b](https://github.com/angular/angular/commit/016a41b)), closes [#38147](https://github.com/angular/angular/issues/38147)
* **compiler-cli:** match wrapHost parameter types within plugin interface ([#38004](https://github.com/angular/angular/issues/38004)) ([df01a82](https://github.com/angular/angular/commit/df01a82))
* **compiler-cli:** preserve quotes in class member names ([#38387](https://github.com/angular/angular/issues/38387)) ([c9acb7b](https://github.com/angular/angular/commit/c9acb7b)), closes [#38311](https://github.com/angular/angular/issues/38311)
* **core:** prevent NgModule scope being overwritten in JIT compiler ([#37795](https://github.com/angular/angular/issues/37795)) ([3acebdc](https://github.com/angular/angular/commit/3acebdc)), closes [#37105](https://github.com/angular/angular/issues/37105)
* **core:** queries not matching string injection tokens ([#38321](https://github.com/angular/angular/issues/38321)) ([32109dc](https://github.com/angular/angular/commit/32109dc)), closes [#38313](https://github.com/angular/angular/issues/38313) [#38315](https://github.com/angular/angular/issues/38315)
* **core:** Store the currently selected ICU in `LView` ([#38345](https://github.com/angular/angular/issues/38345)) ([ee5123f](https://github.com/angular/angular/commit/ee5123f))
* **platform-server:** remove styles added by ServerStylesHost on destruction ([#38367](https://github.com/angular/angular/issues/38367)) ([7f11149](https://github.com/angular/angular/commit/7f11149))
* **router:** prevent calling unsubscribe on undefined subscription in RouterPreloader ([#38344](https://github.com/angular/angular/issues/38344)) ([4151314](https://github.com/angular/angular/commit/4151314))
* **service-worker:** fix the chrome debugger syntax highlighter ([#38332](https://github.com/angular/angular/issues/38332)) ([f5d5bac](https://github.com/angular/angular/commit/f5d5bac))
<a name="10.0.8"></a>
## 10.0.8 (2020-08-04)
### Bug Fixes
* **compiler:** add PURE annotation to getInheritedFactory calls ([#38291](https://github.com/angular/angular/issues/38291)) ([03d8e31](https://github.com/angular/angular/commit/03d8e31))
* **compiler:** update unparsable character reference entity error messages ([#38319](https://github.com/angular/angular/issues/38319)) ([cea4678](https://github.com/angular/angular/commit/cea4678)), closes [#26067](https://github.com/angular/angular/issues/26067)
<a name="10.0.7"></a>
## 10.0.7 (2020-07-30)
### Bug Fixes
* **compiler:** Metadata should not include methods on Object.prototype ([#38292](https://github.com/angular/angular/issues/38292)) ([879ff08](https://github.com/angular/angular/commit/879ff08))
<a name="10.0.6"></a>
## 10.0.6 (2020-07-28)
### Bug Fixes
* **compiler:** share identical stylesheets between components in the same file ([#38213](https://github.com/angular/angular/issues/38213)) ([264950b](https://github.com/angular/angular/commit/264950b)), closes [#38204](https://github.com/angular/angular/issues/38204)
* **compiler-cli:** Add support for string literal class members ([#38226](https://github.com/angular/angular/issues/38226)) ([b1e7775](https://github.com/angular/angular/commit/b1e7775))
* **core:** `Attribute` decorator `attributeName` is mandatory ([#38131](https://github.com/angular/angular/issues/38131)) ([1c4fcce](https://github.com/angular/angular/commit/1c4fcce)), closes [#32658](https://github.com/angular/angular/issues/32658)
* **core:** unify the signature between ngZone and noopZone ([#37581](https://github.com/angular/angular/issues/37581)) ([d5264f5](https://github.com/angular/angular/commit/d5264f5))
<a name="10.0.5"></a>
## 10.0.5 (2020-07-22)
### Bug Fixes
* **compiler:** properly associate source spans for implicitly closed elements ([#38126](https://github.com/angular/angular/issues/38126)) ([e80278c](https://github.com/angular/angular/commit/e80278c)), closes [#36118](https://github.com/angular/angular/issues/36118)
* **compiler-cli:** ensure file_system handles mixed Windows drives ([#38030](https://github.com/angular/angular/issues/38030)) ([dba4023](https://github.com/angular/angular/commit/dba4023)), closes [#36777](https://github.com/angular/angular/issues/36777)
* **core:** Allow modification of lifecycle hooks any time before bootstrap ([#38119](https://github.com/angular/angular/issues/38119)) ([14b4718](https://github.com/angular/angular/commit/14b4718)), closes [#30497](https://github.com/angular/angular/issues/30497)
* **core:** error due to integer overflow when there are too many host bindings ([#38014](https://github.com/angular/angular/issues/38014)) ([7b6e73c](https://github.com/angular/angular/commit/7b6e73c)), closes [#37876](https://github.com/angular/angular/issues/37876) [#37876](https://github.com/angular/angular/issues/37876)
* **core:** incorrectly validating properties on ng-content and ng-container ([#37773](https://github.com/angular/angular/issues/37773)) ([17ddab9](https://github.com/angular/angular/commit/17ddab9))
<a name="10.0.4"></a>
## 10.0.4 (2020-07-15)

View File

@ -1,6 +1,6 @@
# CLI Overview and Command Reference
The Angular CLI is a command-line interface tool that you use to initialize, develop, scaffold, and maintain Angular applications. You can use the tool directly in a command shell, or indirectly through an interactive UI such as [Angular Console](https://angularconsole.com).
The Angular CLI is a command-line interface tool that you use to initialize, develop, scaffold, and maintain Angular applications directly from a command shell.
## Installing Angular CLI

View File

@ -18,6 +18,7 @@
**/src/karma.conf.js
**/.angular-cli.json
**/.editorconfig
**/.gitignore
**/angular.json
**/tsconfig.json
**/bs-config.e2e.json

View File

@ -1,5 +1,3 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor';
describe('Accessibility example e2e tests', () => {
@ -8,11 +6,11 @@ describe('Accessibility example e2e tests', () => {
browser.get('');
});
it('should display Accessibility Example', function () {
it('should display Accessibility Example', () => {
expect(element(by.css('h1')).getText()).toEqual('Accessibility Example');
});
it('should take a number and change progressbar width', function () {
it('should take a number and change progressbar width', () => {
element(by.css('input')).sendKeys('16');
expect(element(by.css('input')).getAttribute('value')).toEqual('016');
expect(element(by.css('app-example-progressbar div')).getCssValue('width')).toBe('48px');

View File

@ -1,3 +1,4 @@
// tslint:disable: no-host-metadata-property
// #docregion progressbar-component
import { Component, Input } from '@angular/core';

View File

@ -1,20 +1,18 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor';
describe('AngularJS to Angular Quick Reference Tests', function () {
describe('AngularJS to Angular Quick Reference Tests', () => {
beforeAll(function () {
beforeAll(() => {
browser.get('');
});
it('should display no poster images after bootstrap', function () {
it('should display no poster images after bootstrap', () => {
testImagesAreDisplayed(false);
});
it('should display proper movie data', function () {
it('should display proper movie data', () => {
// We check only a few samples
let expectedSamples: any[] = [
const expectedSamples: any[] = [
{row: 0, column: 0, element: 'img', attr: 'src', value: 'images/hero.png', contains: true},
{row: 0, column: 2, value: 'Celeritas'},
{row: 1, column: 3, matches: /Dec 1[678], 2015/}, // absorb timezone dif; we care about date format
@ -25,18 +23,17 @@ describe('AngularJS to Angular Quick Reference Tests', function () {
];
// Go through the samples
let movieRows = getMovieRows();
for (let i = 0; i < expectedSamples.length; i++) {
let sample = expectedSamples[i];
let tableCell = movieRows.get(sample.row)
const movieRows = getMovieRows();
for (const sample of expectedSamples) {
const tableCell = movieRows.get(sample.row)
.all(by.tagName('td')).get(sample.column);
// Check the cell or its nested element
let elementToCheck = sample.element
const elementToCheck = sample.element
? tableCell.element(by.tagName(sample.element))
: tableCell;
// Check element attribute or text
let valueToCheck = sample.attr
const valueToCheck = sample.attr
? elementToCheck.getAttribute(sample.attr)
: elementToCheck.getText();
@ -51,42 +48,42 @@ describe('AngularJS to Angular Quick Reference Tests', function () {
}
});
it('should display images after Show Poster', function () {
it('should display images after Show Poster', () => {
testPosterButtonClick('Show Poster', true);
});
it('should hide images after Hide Poster', function () {
it('should hide images after Hide Poster', () => {
testPosterButtonClick('Hide Poster', false);
});
it('should display no movie when no favorite hero is specified', function () {
it('should display no movie when no favorite hero is specified', () => {
testFavoriteHero(null, 'Please enter your favorite hero.');
});
it('should display no movie for Magneta', function () {
it('should display no movie for Magneta', () => {
testFavoriteHero('Magneta', 'No movie, sorry!');
});
it('should display a movie for Dr Nice', function () {
it('should display a movie for Dr Nice', () => {
testFavoriteHero('Dr Nice', 'Excellent choice!');
});
function testImagesAreDisplayed(isDisplayed: boolean) {
let expectedMovieCount = 3;
const expectedMovieCount = 3;
let movieRows = getMovieRows();
const movieRows = getMovieRows();
expect(movieRows.count()).toBe(expectedMovieCount);
for (let i = 0; i < expectedMovieCount; i++) {
let movieImage = movieRows.get(i).element(by.css('td > img'));
const movieImage = movieRows.get(i).element(by.css('td > img'));
expect(movieImage.isDisplayed()).toBe(isDisplayed);
}
}
function testPosterButtonClick(expectedButtonText: string, isDisplayed: boolean) {
let posterButton = element(by.css('app-movie-list tr > th > button'));
const posterButton = element(by.css('app-movie-list tr > th > button'));
expect(posterButton.getText()).toBe(expectedButtonText);
posterButton.click().then(function () {
posterButton.click().then(() => {
testImagesAreDisplayed(isDisplayed);
});
}
@ -96,12 +93,12 @@ describe('AngularJS to Angular Quick Reference Tests', function () {
}
function testFavoriteHero(heroName: string, expectedLabel: string) {
let movieListComp = element(by.tagName('app-movie-list'));
let heroInput = movieListComp.element(by.tagName('input'));
let favoriteHeroLabel = movieListComp.element(by.tagName('h3'));
let resultLabel = movieListComp.element(by.css('span > p'));
const movieListComp = element(by.tagName('app-movie-list'));
const heroInput = movieListComp.element(by.tagName('input'));
const favoriteHeroLabel = movieListComp.element(by.tagName('h3'));
const resultLabel = movieListComp.element(by.css('span > p'));
heroInput.clear().then(function () {
heroInput.clear().then(() => {
heroInput.sendKeys(heroName || '');
expect(resultLabel.getText()).toBe(expectedLabel);
if (heroName) {

View File

@ -1,6 +1,6 @@
// #docregion
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { Routes, RouterModule } from '@angular/router';
import { MovieListComponent } from './movie-list.component';

View File

@ -1,8 +1,8 @@
// #docregion
import { NgModule } from '@angular/core';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { AppComponent } from './app.component';
@NgModule({
imports: [ BrowserModule ],

View File

@ -1,9 +1,9 @@
// #docregion
import { NgModule } from '@angular/core';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { AppComponent } from './app.component';
import { MovieListComponent } from './movie-list.component';
import { AppRoutingModule } from './app-routing.module';

View File

@ -1,5 +1,3 @@
'use strict'; // necessary for es6 output in node
import { browser, ExpectedConditions as EC } from 'protractor';
import { logging } from 'selenium-webdriver';
import * as openClose from './open-close.po';

View File

@ -34,7 +34,7 @@ export class AppComponent {
// #docregion prepare-router-outlet
prepareRoute(outlet: RouterOutlet) {
return outlet && outlet.activatedRouteData && outlet.activatedRouteData['animation'];
return outlet && outlet.activatedRouteData && outlet.activatedRouteData.animation;
}
// #enddocregion prepare-router-outlet

View File

@ -1,4 +1,6 @@
// tslint:disable: variable-name
// #docplaster
// #docregion
import { Component, HostBinding, OnInit } from '@angular/core';
import { trigger, transition, animate, style, query, stagger } from '@angular/animations';
import { HEROES } from './mock-heroes';
@ -52,13 +54,11 @@ export class HeroListPageComponent implements OnInit {
@HostBinding('@pageAnimations')
public animatePage = true;
_heroes = [];
// #docregion filter-animations
heroTotal = -1;
// #enddocregion filter-animations
get heroes() {
return this._heroes;
}
get heroes() { return this._heroes; }
private _heroes = [];
ngOnInit() {
this._heroes = HEROES;

View File

@ -8,8 +8,7 @@ import { trigger, transition, state, animate, style, AnimationEvent } from '@ang
// #docregion trigger, trigger-wildcard1, trigger-transition
animations: [
trigger('openClose', [
// #enddocregion events1
// #docregion state1, events1
// #docregion state1
// ...
// #enddocregion events1
state('open', style({
@ -34,8 +33,7 @@ import { trigger, transition, state, animate, style, AnimationEvent } from '@ang
transition('closed => open', [
animate('0.5s')
]),
// #enddocregion trigger, component
// #enddocregion transition2
// #enddocregion transition2, trigger, component
// #docregion trigger-wildcard1
transition('* => closed', [
animate('1s')
@ -70,7 +68,9 @@ import { trigger, transition, state, animate, style, AnimationEvent } from '@ang
})
// #docregion events
export class OpenCloseComponent {
// #enddocregion events1, events
// #enddocregion events1, events, component
@Input() logging = false;
// #docregion component
isOpen = true;
toggle() {
@ -78,9 +78,8 @@ export class OpenCloseComponent {
}
// #enddocregion component
@Input() logging = false;
// #docregion events1, events
onAnimationEvent ( event: AnimationEvent ) {
onAnimationEvent( event: AnimationEvent ) {
// #enddocregion events1, events
if (!this.logging) {
return;

View File

@ -1,5 +1,3 @@
'use strict'; // necessary for es6 output in node
import { protractor, browser, element, by, ElementFinder } from 'protractor';
const nameSuffix = 'X';
@ -21,7 +19,7 @@ describe('Architecture', () => {
});
it(`has h2 '${expectedH2}'`, () => {
let h2 = element.all(by.css('h2')).map((elt: any) => elt.getText());
const h2 = element.all(by.css('h2')).map((elt: any) => elt.getText());
expect(h2).toEqual(expectedH2);
});
@ -34,42 +32,42 @@ function heroTests() {
const targetHero: Hero = { id: 2, name: 'Dr Nice' };
it('has the right number of heroes', () => {
let page = getPageElts();
const page = getPageElts();
expect(page.heroes.count()).toEqual(3);
});
it('has no hero details initially', function () {
let page = getPageElts();
it('has no hero details initially', () => {
const page = getPageElts();
expect(page.heroDetail.isPresent()).toBeFalsy('no hero detail');
});
it('shows selected hero details', async () => {
await element(by.cssContainingText('li', targetHero.name)).click();
let page = getPageElts();
let hero = await heroFromDetail(page.heroDetail);
const page = getPageElts();
const hero = await heroFromDetail(page.heroDetail);
expect(hero.id).toEqual(targetHero.id);
expect(hero.name).toEqual(targetHero.name);
});
it(`shows updated hero name in details`, async () => {
let input = element.all(by.css('input')).first();
const input = element.all(by.css('input')).first();
input.sendKeys(nameSuffix);
let page = getPageElts();
let hero = await heroFromDetail(page.heroDetail);
let newName = targetHero.name + nameSuffix;
const page = getPageElts();
const hero = await heroFromDetail(page.heroDetail);
const newName = targetHero.name + nameSuffix;
expect(hero.id).toEqual(targetHero.id);
expect(hero.name).toEqual(newName);
});
}
function salesTaxTests() {
it('has no sales tax initially', function () {
let page = getPageElts();
it('has no sales tax initially', () => {
const page = getPageElts();
expect(page.salesTaxDetail.isPresent()).toBeFalsy('no sales tax info');
});
it('shows sales tax', async function () {
let page = getPageElts();
it('shows sales tax', async () => {
const page = getPageElts();
page.salesTaxAmountInput.sendKeys('10', protractor.Key.ENTER);
expect(page.salesTaxDetail.getText()).toEqual('The sales tax is $1.00');
});
@ -88,13 +86,11 @@ function getPageElts() {
async function heroFromDetail(detail: ElementFinder): Promise<Hero> {
// Get hero id from the first <div>
// let _id = await detail.all(by.css('div')).first().getText();
let _id = await detail.all(by.css('div')).first().getText();
const id = await detail.all(by.css('div')).first().getText();
// Get name from the h2
// let _name = await detail.element(by.css('h4')).getText();
let _name = await detail.element(by.css('h4')).getText();
const name = await detail.element(by.css('h4')).getText();
return {
id: +_id.substr(_id.indexOf(' ') + 1),
name: _name.substr(0, _name.lastIndexOf(' '))
id: +id.substr(id.indexOf(' ') + 1),
name: name.substr(0, name.lastIndexOf(' ')),
};
}

View File

@ -1,15 +1,15 @@
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
// #docregion imports
import { NgModule } from '@angular/core';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
// #enddocregion imports
import { HeroDetailComponent } from './hero-detail.component';
import { HeroListComponent } from './hero-list.component';
import { SalesTaxComponent } from './sales-tax.component';
import { HeroService } from './hero.service';
import { BackendService } from './backend.service';
import { Logger } from './logger.service';
import { HeroListComponent } from './hero-list.component';
import { SalesTaxComponent } from './sales-tax.component';
import { HeroService } from './hero.service';
import { BackendService } from './backend.service';
import { Logger } from './logger.service';
@NgModule({
imports: [

View File

@ -18,7 +18,7 @@ export class BackendService {
// TODO: get from the database
return Promise.resolve<Hero[]>(HEROES);
}
let err = new Error('Cannot get object of this type');
const err = new Error('Cannot get object of this type');
this.logger.error(err);
throw err;
}

View File

@ -1,7 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { Hero } from './hero';
import { HeroService } from './hero.service';
import { Hero } from './hero';
import { HeroService } from './hero.service';
// #docregion metadata, providers
@Component({

View File

@ -22,7 +22,7 @@ export class AppComponent {
}
// #docregion module
import { NgModule } from '@angular/core';
import { NgModule } from '@angular/core';
// #docregion import-browser-module
import { BrowserModule } from '@angular/platform-browser';
// #enddocregion import-browser-module

View File

@ -1,7 +1,7 @@
import { Component } from '@angular/core';
import { Component } from '@angular/core';
import { SalesTaxService } from './sales-tax.service';
import { TaxRateService } from './tax-rate.service';
import { TaxRateService } from './tax-rate.service';
@Component({
selector: 'app-sales-tax',

View File

@ -7,7 +7,7 @@ export class SalesTaxService {
constructor(private rateService: TaxRateService) { }
getVAT(value: string | number) {
let amount = (typeof value === 'string') ?
const amount = (typeof value === 'string') ?
parseFloat(value) : value;
return (amount || 0) * this.rateService.getRate('VAT');
}

View File

@ -1,34 +1,32 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor';
describe('Attribute binding example', function () {
describe('Attribute binding example', () => {
beforeEach(function () {
beforeEach(() => {
browser.get('');
});
it('should display Property Binding with Angular', function () {
it('should display Property Binding with Angular', () => {
expect(element(by.css('h1')).getText()).toEqual('Attribute, class, and style bindings');
});
it('should display a table', function() {
it('should display a table', () => {
expect(element.all(by.css('table')).isPresent()).toBe(true);
});
it('should display an Aria button', function () {
it('should display an Aria button', () => {
expect(element.all(by.css('button')).get(0).getText()).toBe('Go for it with Aria');
});
it('should display a blue background on div', function () {
it('should display a blue background on div', () => {
expect(element.all(by.css('div')).get(1).getCssValue('background-color')).toEqual('rgba(25, 118, 210, 1)');
});
it('should display a blue div with a red border', function () {
it('should display a blue div with a red border', () => {
expect(element.all(by.css('div')).get(1).getCssValue('border')).toEqual('2px solid rgb(212, 30, 46)');
});
it('should display a div with many classes', function () {
it('should display a div with many classes', () => {
expect(element.all(by.css('div')).get(1).getAttribute('class')).toContain('special');
expect(element.all(by.css('div')).get(1).getAttribute('class')).toContain('clearance');
});

View File

@ -1,16 +1,16 @@
import { Component } from '@angular/core';
import { Component, HostBinding } from '@angular/core';
@Component({
selector: 'comp-with-host-binding',
template: 'I am a component!',
host: {
'[class.special]': 'isSpecial',
'[style.color]': 'color',
'[style.width]': 'width'
}
})
export class CompWithHostBindingComponent {
@HostBinding('class.special')
isSpecial = false;
@HostBinding('style.color')
color = 'green';
@HostBinding('style.width')
width = '200px';
}

View File

@ -1,17 +1,15 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor';
describe('Attribute directives', () => {
let _title = 'My First Attribute Directive';
const title = 'My First Attribute Directive';
beforeAll(() => {
browser.get('');
});
it(`should display correct title: ${_title}`, () => {
expect(element(by.css('h1')).getText()).toEqual(_title);
it(`should display correct title: ${title}`, () => {
expect(element(by.css('h1')).getText()).toEqual(title);
});
it('should be able to select green highlight', () => {

View File

@ -3,7 +3,7 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component.1';
import { AppComponent } from './app.component.1';
import { HighlightDirective as HLD1 } from './highlight.directive.1';
import { HighlightDirective as HLD2 } from './highlight.directive.2';
import { HighlightDirective as HLD3 } from './highlight.directive.3';

View File

@ -3,57 +3,55 @@ import { logging } from 'selenium-webdriver';
describe('Binding syntax e2e tests', () => {
beforeEach(function () {
browser.get('');
});
beforeEach(() => browser.get(''));
// helper function used to test what's logged to the console
async function logChecker(button, contents) {
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
const message = logs.filter(({ message }) => message.indexOf(contents) !== -1 ? true : false);
expect(message.length).toBeGreaterThan(0);
}
// helper function used to test what's logged to the console
async function logChecker(button, contents) {
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
const messages = logs.filter(({ message }) => message.indexOf(contents) !== -1 ? true : false);
expect(messages.length).toBeGreaterThan(0);
}
it('should display Binding syntax', function () {
it('should display Binding syntax', () => {
expect(element(by.css('h1')).getText()).toEqual('Binding syntax');
});
it('should display Save button', function () {
it('should display Save button', () => {
expect(element.all(by.css('button')).get(0).getText()).toBe('Save');
});
it('should display HTML attributes and DOM properties', function () {
it('should display HTML attributes and DOM properties', () => {
expect(element.all(by.css('h2')).get(1).getText()).toBe('HTML attributes and DOM properties');
});
it('should display 1. Use the inspector...', function () {
it('should display 1. Use the inspector...', () => {
expect(element.all(by.css('p')).get(0).getText()).toContain('1. Use the inspector');
});
it('should display Disabled property vs. attribute', function () {
it('should display Disabled property vs. attribute', () => {
expect(element.all(by.css('h3')).get(0).getText()).toBe('Disabled property vs. attribute');
});
it('should log a message including Sarah', async () => {
let attributeButton = element.all(by.css('button')).get(1);
const attributeButton = element.all(by.css('button')).get(1);
await attributeButton.click();
const contents = 'Sarah';
logChecker(attributeButton, contents);
});
it('should log a message including Sarah for DOM property', async () => {
let DOMPropertyButton = element.all(by.css('button')).get(2);
const DOMPropertyButton = element.all(by.css('button')).get(2);
await DOMPropertyButton.click();
const contents = 'Sarah';
logChecker(DOMPropertyButton, contents);
});
it('should log a message including Sally for DOM property', async () => {
let DOMPropertyButton = element.all(by.css('button')).get(2);
let input = element(by.css('input'));
const DOMPropertyButton = element.all(by.css('button')).get(2);
const input = element(by.css('input'));
input.sendKeys('Sally');
await DOMPropertyButton.click();
const contents = 'Sally';
@ -61,14 +59,14 @@ describe('Binding syntax e2e tests', () => {
});
it('should log a message that Test Button works', async () => {
let testButton = element.all(by.css('button')).get(3);
const testButton = element.all(by.css('button')).get(3);
await testButton.click();
const contents = 'Test';
logChecker(testButton, contents);
});
it('should toggle Test Button disabled', async () => {
let toggleButton = element.all(by.css('button')).get(4);
const toggleButton = element.all(by.css('button')).get(4);
await toggleButton.click();
const contents = 'true';
logChecker(toggleButton, contents);

View File

@ -26,7 +26,7 @@ export class AppComponent {
toggleDisabled(): any {
let testButton = <HTMLInputElement> document.getElementById('testButton');
const testButton = document.getElementById('testButton') as HTMLInputElement;
testButton.disabled = !testButton.disabled;
console.warn(testButton.disabled);
}

View File

@ -1,21 +1,19 @@
'use strict';
import { browser, element, by } from 'protractor';
describe('Built-in Directives', function () {
describe('Built-in Directives', () => {
beforeAll(function () {
beforeAll(() => {
browser.get('');
});
it('should have title Built-in Directives', function () {
let title = element.all(by.css('h1')).get(0);
it('should have title Built-in Directives', () => {
const title = element.all(by.css('h1')).get(0);
expect(title.getText()).toEqual('Built-in Directives');
});
it('should change first Teapot header', async () => {
let firstLabel = element.all(by.css('p')).get(0);
let firstInput = element.all(by.css('input')).get(0);
const firstLabel = element.all(by.css('p')).get(0);
const firstInput = element.all(by.css('input')).get(0);
expect(firstLabel.getText()).toEqual('Current item name: Teapot');
firstInput.sendKeys('abc');
@ -23,49 +21,49 @@ describe('Built-in Directives', function () {
});
it('should modify sentence when modified checkbox checked', function () {
let modifiedChkbxLabel = element.all(by.css('input[type="checkbox"]')).get(1);
let modifiedSentence = element.all(by.css('div')).get(1);
it('should modify sentence when modified checkbox checked', () => {
const modifiedChkbxLabel = element.all(by.css('input[type="checkbox"]')).get(1);
const modifiedSentence = element.all(by.css('div')).get(1);
modifiedChkbxLabel.click();
expect(modifiedSentence.getText()).toContain('modified');
});
it('should modify sentence when normal checkbox checked', function () {
let normalChkbxLabel = element.all(by.css('input[type="checkbox"]')).get(4);
let normalSentence = element.all(by.css('div')).get(7);
it('should modify sentence when normal checkbox checked', () => {
const normalChkbxLabel = element.all(by.css('input[type="checkbox"]')).get(4);
const normalSentence = element.all(by.css('div')).get(7);
normalChkbxLabel.click();
expect(normalSentence.getText()).toContain('normal weight and, extra large');
});
it('should toggle app-item-detail', function () {
let toggleButton = element.all(by.css('button')).get(3);
let toggledDiv = element.all(by.css('app-item-detail')).get(0);
it('should toggle app-item-detail', () => {
const toggleButton = element.all(by.css('button')).get(3);
const toggledDiv = element.all(by.css('app-item-detail')).get(0);
toggleButton.click();
expect(toggledDiv.isDisplayed()).toBe(true);
});
it('should hide app-item-detail', function () {
let hiddenMessage = element.all(by.css('p')).get(11);
let hiddenDiv = element.all(by.css('app-item-detail')).get(2);
it('should hide app-item-detail', () => {
const hiddenMessage = element.all(by.css('p')).get(11);
const hiddenDiv = element.all(by.css('app-item-detail')).get(2);
expect(hiddenMessage.getText()).toContain('in the DOM');
expect(hiddenDiv.isDisplayed()).toBe(true);
});
it('should have 10 lists each containing the string Teapot', function () {
let listDiv = element.all(by.cssContainingText('.box', 'Teapot'));
it('should have 10 lists each containing the string Teapot', () => {
const listDiv = element.all(by.cssContainingText('.box', 'Teapot'));
expect(listDiv.count()).toBe(10);
});
it('should switch case', function () {
let tvRadioButton = element.all(by.css('input[type="radio"]')).get(3);
let tvDiv = element(by.css('app-lost-item'));
it('should switch case', () => {
const tvRadioButton = element.all(by.css('input[type="radio"]')).get(3);
const tvDiv = element(by.css('app-lost-item'));
let fishbowlRadioButton = element.all(by.css('input[type="radio"]')).get(4);
let fishbowlDiv = element(by.css('app-unknown-item'));
const fishbowlRadioButton = element.all(by.css('input[type="radio"]')).get(4);
const fishbowlDiv = element(by.css('app-unknown-item'));
tvRadioButton.click();
expect(tvDiv.getText()).toContain('Television');

View File

@ -30,6 +30,14 @@ export class AppComponent implements OnInit {
itemsWithTrackByCountReset = 0;
itemIdIncrement = 1;
// #docregion setClasses
currentClasses: {};
// #enddocregion setClasses
// #docregion setStyles
currentStyles: {};
// #enddocregion setStyles
ngOnInit() {
this.resetItems();
this.setCurrentClasses();
@ -41,20 +49,18 @@ export class AppComponent implements OnInit {
this.currentItem.name = name.toUpperCase();
}
// #docregion setClasses
currentClasses: {};
// #docregion setClasses
setCurrentClasses() {
// CSS classes: added/removed per current state of component properties
this.currentClasses = {
'saveable': this.canSave,
'modified': !this.isUnchanged,
'special': this.isSpecial
saveable: this.canSave,
modified: !this.isUnchanged,
special: this.isSpecial
};
}
// #enddocregion setClasses
// #docregion setStyles
currentStyles: {};
setCurrentStyles() {
// CSS styles: set per current state of component properties
this.currentStyles = {
@ -70,11 +76,7 @@ export class AppComponent implements OnInit {
}
giveNullCustomerValue() {
!(this.nullCustomer = null) ? (this.nullCustomer = 'Kelly') : (this.nullCustomer = null);
}
resetNullItem() {
this.nullCustomer = null;
this.nullCustomer = 'Kelly';
}
resetItems() {
@ -84,7 +86,7 @@ export class AppComponent implements OnInit {
}
resetList() {
this.resetItems()
this.resetItems();
this.itemsWithTrackByCountReset = 0;
this.itemsNoTrackByCount = ++this.itemsNoTrackByCount;
}
@ -107,7 +109,7 @@ export class AppComponent implements OnInit {
trackByItems(index: number, item: Item): number { return item.id; }
// #enddocregion trackByItems
trackById(index: number, item: any): number { return item['id']; }
trackById(index: number, item: any): number { return item.id; }
}

View File

@ -1,19 +1,17 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor';
describe('Built Template Functions Example', function () {
beforeAll(function () {
describe('Built Template Functions Example', () => {
beforeAll(() => {
browser.get('');
});
it('should have title Built-in Template Functions', function () {
let title = element.all(by.css('h1')).get(0);
it('should have title Built-in Template Functions', () => {
const title = element.all(by.css('h1')).get(0);
expect(title.getText()).toEqual('Built-in Template Functions');
});
it('should display $any( ) in h2', function () {
let header = element(by.css('h2'));
it('should display $any( ) in h2', () => {
const header = element(by.css('h2'));
expect(header.getText()).toContain('$any( )');
});

View File

@ -1,87 +1,85 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor';
describe('Component Communication Cookbook Tests', function () {
describe('Component Communication Cookbook Tests', () => {
// Note: '?e2e' which app can read to know it is running in protractor
// e.g. `if (!/e2e/.test(location.search)) { ...`
beforeAll(function () {
beforeAll(() => {
browser.get('?e2e');
});
describe('Parent-to-child communication', function() {
describe('Parent-to-child communication', () => {
// #docregion parent-to-child
// ...
let _heroNames = ['Dr IQ', 'Magneta', 'Bombasto'];
let _masterName = 'Master';
const heroNames = ['Dr IQ', 'Magneta', 'Bombasto'];
const masterName = 'Master';
it('should pass properties to children properly', function () {
let parent = element.all(by.tagName('app-hero-parent')).get(0);
let heroes = parent.all(by.tagName('app-hero-child'));
it('should pass properties to children properly', () => {
const parent = element.all(by.tagName('app-hero-parent')).get(0);
const heroes = parent.all(by.tagName('app-hero-child'));
for (let i = 0; i < _heroNames.length; i++) {
let childTitle = heroes.get(i).element(by.tagName('h3')).getText();
let childDetail = heroes.get(i).element(by.tagName('p')).getText();
expect(childTitle).toEqual(_heroNames[i] + ' says:');
expect(childDetail).toContain(_masterName);
for (let i = 0; i < heroNames.length; i++) {
const childTitle = heroes.get(i).element(by.tagName('h3')).getText();
const childDetail = heroes.get(i).element(by.tagName('p')).getText();
expect(childTitle).toEqual(heroNames[i] + ' says:');
expect(childDetail).toContain(masterName);
}
});
// ...
// #enddocregion parent-to-child
});
describe('Parent-to-child communication with setter', function() {
describe('Parent-to-child communication with setter', () => {
// #docregion parent-to-child-setter
// ...
it('should display trimmed, non-empty names', function () {
let _nonEmptyNameIndex = 0;
let _nonEmptyName = '"Dr IQ"';
let parent = element.all(by.tagName('app-name-parent')).get(0);
let hero = parent.all(by.tagName('app-name-child')).get(_nonEmptyNameIndex);
it('should display trimmed, non-empty names', () => {
const nonEmptyNameIndex = 0;
const nonEmptyName = '"Dr IQ"';
const parent = element.all(by.tagName('app-name-parent')).get(0);
const hero = parent.all(by.tagName('app-name-child')).get(nonEmptyNameIndex);
let displayName = hero.element(by.tagName('h3')).getText();
expect(displayName).toEqual(_nonEmptyName);
const displayName = hero.element(by.tagName('h3')).getText();
expect(displayName).toEqual(nonEmptyName);
});
it('should replace empty name with default name', function () {
let _emptyNameIndex = 1;
let _defaultName = '"<no name set>"';
let parent = element.all(by.tagName('app-name-parent')).get(0);
let hero = parent.all(by.tagName('app-name-child')).get(_emptyNameIndex);
it('should replace empty name with default name', () => {
const emptyNameIndex = 1;
const defaultName = '"<no name set>"';
const parent = element.all(by.tagName('app-name-parent')).get(0);
const hero = parent.all(by.tagName('app-name-child')).get(emptyNameIndex);
let displayName = hero.element(by.tagName('h3')).getText();
expect(displayName).toEqual(_defaultName);
const displayName = hero.element(by.tagName('h3')).getText();
expect(displayName).toEqual(defaultName);
});
// ...
// #enddocregion parent-to-child-setter
});
describe('Parent-to-child communication with ngOnChanges', function() {
describe('Parent-to-child communication with ngOnChanges', () => {
// #docregion parent-to-child-onchanges
// ...
// Test must all execute in this exact order
it('should set expected initial values', function () {
let actual = getActual();
it('should set expected initial values', () => {
const actual = getActual();
let initialLabel = 'Version 1.23';
let initialLog = 'Initial value of major set to 1, Initial value of minor set to 23';
const initialLabel = 'Version 1.23';
const initialLog = 'Initial value of major set to 1, Initial value of minor set to 23';
expect(actual.label).toBe(initialLabel);
expect(actual.count).toBe(1);
expect(actual.logs.get(0).getText()).toBe(initialLog);
});
it('should set expected values after clicking \'Minor\' twice', function () {
let repoTag = element(by.tagName('app-version-parent'));
let newMinorButton = repoTag.all(by.tagName('button')).get(0);
it('should set expected values after clicking \'Minor\' twice', () => {
const repoTag = element(by.tagName('app-version-parent'));
const newMinorButton = repoTag.all(by.tagName('button')).get(0);
newMinorButton.click().then(function() {
newMinorButton.click().then(function() {
let actual = getActual();
newMinorButton.click().then(() => {
newMinorButton.click().then(() => {
const actual = getActual();
let labelAfter2Minor = 'Version 1.25';
let logAfter2Minor = 'minor changed from 24 to 25';
const labelAfter2Minor = 'Version 1.25';
const logAfter2Minor = 'minor changed from 24 to 25';
expect(actual.label).toBe(labelAfter2Minor);
expect(actual.count).toBe(3);
@ -90,15 +88,15 @@ describe('Component Communication Cookbook Tests', function () {
});
});
it('should set expected values after clicking \'Major\' once', function () {
let repoTag = element(by.tagName('app-version-parent'));
let newMajorButton = repoTag.all(by.tagName('button')).get(1);
it('should set expected values after clicking \'Major\' once', () => {
const repoTag = element(by.tagName('app-version-parent'));
const newMajorButton = repoTag.all(by.tagName('button')).get(1);
newMajorButton.click().then(function() {
let actual = getActual();
newMajorButton.click().then(() => {
const actual = getActual();
let labelAfterMajor = 'Version 2.0';
let logAfterMajor = 'major changed from 1 to 2, minor changed from 25 to 0';
const labelAfterMajor = 'Version 2.0';
const logAfterMajor = 'major changed from 1 to 2, minor changed from 25 to 0';
expect(actual.label).toBe(labelAfterMajor);
expect(actual.count).toBe(4);
@ -107,14 +105,14 @@ describe('Component Communication Cookbook Tests', function () {
});
function getActual() {
let versionTag = element(by.tagName('app-version-child'));
let label = versionTag.element(by.tagName('h3')).getText();
let ul = versionTag.element((by.tagName('ul')));
let logs = ul.all(by.tagName('li'));
const versionTag = element(by.tagName('app-version-child'));
const label = versionTag.element(by.tagName('h3')).getText();
const ul = versionTag.element((by.tagName('ul')));
const logs = ul.all(by.tagName('li'));
return {
label: label,
logs: logs,
label,
logs,
count: logs.count()
};
}
@ -123,30 +121,30 @@ describe('Component Communication Cookbook Tests', function () {
});
describe('Child-to-parent communication', function() {
describe('Child-to-parent communication', () => {
// #docregion child-to-parent
// ...
it('should not emit the event initially', function () {
let voteLabel = element(by.tagName('app-vote-taker'))
it('should not emit the event initially', () => {
const voteLabel = element(by.tagName('app-vote-taker'))
.element(by.tagName('h3')).getText();
expect(voteLabel).toBe('Agree: 0, Disagree: 0');
});
it('should process Agree vote', function () {
let agreeButton1 = element.all(by.tagName('app-voter')).get(0)
it('should process Agree vote', () => {
const agreeButton1 = element.all(by.tagName('app-voter')).get(0)
.all(by.tagName('button')).get(0);
agreeButton1.click().then(function() {
let voteLabel = element(by.tagName('app-vote-taker'))
agreeButton1.click().then(() => {
const voteLabel = element(by.tagName('app-vote-taker'))
.element(by.tagName('h3')).getText();
expect(voteLabel).toBe('Agree: 1, Disagree: 0');
});
});
it('should process Disagree vote', function () {
let agreeButton1 = element.all(by.tagName('app-voter')).get(1)
it('should process Disagree vote', () => {
const agreeButton1 = element.all(by.tagName('app-voter')).get(1)
.all(by.tagName('button')).get(1);
agreeButton1.click().then(function() {
let voteLabel = element(by.tagName('app-vote-taker'))
agreeButton1.click().then(() => {
const voteLabel = element(by.tagName('app-vote-taker'))
.element(by.tagName('h3')).getText();
expect(voteLabel).toBe('Agree: 1, Disagree: 1');
});
@ -157,31 +155,31 @@ describe('Component Communication Cookbook Tests', function () {
// Can't run timer tests in protractor because
// interaction w/ zones causes all tests to freeze & timeout.
xdescribe('Parent calls child via local var', function() {
xdescribe('Parent calls child via local var', () => {
countDownTimerTests('countdown-parent-lv');
});
xdescribe('Parent calls ViewChild', function() {
xdescribe('Parent calls ViewChild', () => {
countDownTimerTests('countdown-parent-vc');
});
function countDownTimerTests(parentTag: string) {
// #docregion countdown-timer-tests
// ...
it('timer and parent seconds should match', function () {
let parent = element(by.tagName(parentTag));
let message = parent.element(by.tagName('app-countdown-timer')).getText();
it('timer and parent seconds should match', () => {
const parent = element(by.tagName(parentTag));
const message = parent.element(by.tagName('app-countdown-timer')).getText();
browser.sleep(10); // give `seconds` a chance to catchup with `message`
let seconds = parent.element(by.className('seconds')).getText();
const seconds = parent.element(by.className('seconds')).getText();
expect(message).toContain(seconds);
});
it('should stop the countdown', function () {
let parent = element(by.tagName(parentTag));
let stopButton = parent.all(by.tagName('button')).get(1);
it('should stop the countdown', () => {
const parent = element(by.tagName(parentTag));
const stopButton = parent.all(by.tagName('button')).get(1);
stopButton.click().then(function() {
let message = parent.element(by.tagName('app-countdown-timer')).getText();
stopButton.click().then(() => {
const message = parent.element(by.tagName('app-countdown-timer')).getText();
expect(message).toContain('Holding');
});
});
@ -190,39 +188,39 @@ describe('Component Communication Cookbook Tests', function () {
}
describe('Parent and children communicate via a service', function() {
describe('Parent and children communicate via a service', () => {
// #docregion bidirectional-service
// ...
it('should announce a mission', function () {
let missionControl = element(by.tagName('app-mission-control'));
let announceButton = missionControl.all(by.tagName('button')).get(0);
announceButton.click().then(function () {
let history = missionControl.all(by.tagName('li'));
it('should announce a mission', () => {
const missionControl = element(by.tagName('app-mission-control'));
const announceButton = missionControl.all(by.tagName('button')).get(0);
announceButton.click().then(() => {
const history = missionControl.all(by.tagName('li'));
expect(history.count()).toBe(1);
expect(history.get(0).getText()).toMatch(/Mission.* announced/);
});
});
it('should confirm the mission by Lovell', function () {
it('should confirm the mission by Lovell', () => {
testConfirmMission(1, 2, 'Lovell');
});
it('should confirm the mission by Haise', function () {
it('should confirm the mission by Haise', () => {
testConfirmMission(3, 3, 'Haise');
});
it('should confirm the mission by Swigert', function () {
it('should confirm the mission by Swigert', () => {
testConfirmMission(2, 4, 'Swigert');
});
function testConfirmMission(buttonIndex: number, expectedLogCount: number, astronaut: string) {
let _confirmedLog = ' confirmed the mission';
let missionControl = element(by.tagName('app-mission-control'));
let confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex);
confirmButton.click().then(function () {
let history = missionControl.all(by.tagName('li'));
const confirmedLog = ' confirmed the mission';
const missionControl = element(by.tagName('app-mission-control'));
const confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex);
confirmButton.click().then(() => {
const history = missionControl.all(by.tagName('li'));
expect(history.count()).toBe(expectedLogCount);
expect(history.get(expectedLogCount - 1).getText()).toBe(astronaut + _confirmedLog);
expect(history.get(expectedLogCount - 1).getText()).toBe(astronaut + confirmedLog);
});
}
// ...

View File

@ -1,5 +1,5 @@
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { AstronautComponent } from './astronaut.component';
@ -15,7 +15,7 @@ import { VersionParentComponent } from './version-parent.component';
import { VoterComponent } from './voter.component';
import { VoteTakerComponent } from './votetaker.component';
let directives: any[] = [
const directives: any[] = [
AppComponent,
AstronautComponent,
CountdownTimerComponent,
@ -30,7 +30,7 @@ let directives: any[] = [
VoteTakerComponent
];
let schemas: any[] = [];
const schemas: any[] = [];
// Include Countdown examples
// unless in e2e tests which they break.
@ -49,6 +49,6 @@ if (!/e2e/.test(location.search)) {
],
declarations: directives,
bootstrap: [ AppComponent ],
schemas: schemas
schemas
})
export class AppModule { }

View File

@ -2,7 +2,7 @@
import { Component, Input, OnDestroy } from '@angular/core';
import { MissionService } from './mission.service';
import { Subscription } from 'rxjs';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-astronaut',

View File

@ -2,8 +2,8 @@
// #docregion vc
import { AfterViewInit, ViewChild } from '@angular/core';
// #docregion lv
import { Component } from '@angular/core';
import { CountdownTimerComponent } from './countdown-timer.component';
import { Component } from '@angular/core';
import { CountdownTimerComponent } from './countdown-timer.component';
// #enddocregion lv
// #enddocregion vc

View File

@ -12,6 +12,6 @@ import { Hero } from './hero';
})
export class HeroChildComponent {
@Input() hero: Hero;
@Input('master') masterName: string;
@Input('master') masterName: string; // tslint:disable-line: no-input-rename
}
// #enddocregion

View File

@ -1,6 +1,6 @@
// #docregion
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
import { Subject } from 'rxjs';
@Injectable()
export class MissionService {

View File

@ -1,7 +1,7 @@
// #docregion
import { Component } from '@angular/core';
import { Component } from '@angular/core';
import { MissionService } from './mission.service';
import { MissionService } from './mission.service';
@Component({
selector: 'app-mission-control',
@ -34,7 +34,7 @@ export class MissionControlComponent {
}
announce() {
let mission = this.missions[this.nextMission++];
const mission = this.missions[this.nextMission++];
this.missionService.announceMission(mission);
this.history.push(`Mission "${mission}" announced`);
if (this.nextMission >= this.missions.length) { this.nextMission = 0; }

View File

@ -1,3 +1,4 @@
// tslint:disable: variable-name
// #docregion
import { Component, Input } from '@angular/core';
@ -6,13 +7,11 @@ import { Component, Input } from '@angular/core';
template: '<h3>"{{name}}"</h3>'
})
export class NameChildComponent {
private _name = '';
@Input()
get name(): string { return this._name; }
set name(name: string) {
this._name = (name && name.trim()) || '<no name set>';
}
get name(): string { return this._name; }
private _name = '';
}
// #enddocregion

View File

@ -18,14 +18,14 @@ export class VersionChildComponent implements OnChanges {
changeLog: string[] = [];
ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
let log: string[] = [];
for (let propName in changes) {
let changedProp = changes[propName];
let to = JSON.stringify(changedProp.currentValue);
const log: string[] = [];
for (const propName in changes) {
const changedProp = changes[propName];
const to = JSON.stringify(changedProp.currentValue);
if (changedProp.isFirstChange()) {
log.push(`Initial value of ${propName} set to ${to}`);
} else {
let from = JSON.stringify(changedProp.previousValue);
const from = JSON.stringify(changedProp.previousValue);
log.push(`${propName} changed from ${from} to ${to}`);
}
}

View File

@ -1,5 +1,5 @@
// #docregion
import { Component } from '@angular/core';
import { Component } from '@angular/core';
@Component({
selector: 'app-vote-taker',

View File

@ -1,16 +1,14 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor';
describe('Component Style Tests', function () {
describe('Component Style Tests', () => {
beforeAll(function () {
beforeAll(() => {
browser.get('');
});
it('scopes component styles to component view', function() {
let componentH1 = element(by.css('app-root > h1'));
let externalH1 = element(by.css('body > h1'));
it('scopes component styles to component view', () => {
const componentH1 = element(by.css('app-root > h1'));
const externalH1 = element(by.css('body > h1'));
// Note: sometimes webdriver returns the fontWeight as "normal",
// other times as "400", both of which are equal in CSS terms.
@ -19,49 +17,49 @@ describe('Component Style Tests', function () {
});
it('allows styling :host element', function() {
let host = element(by.css('app-hero-details'));
it('allows styling :host element', () => {
const host = element(by.css('app-hero-details'));
expect(host.getCssValue('borderWidth')).toEqual('1px');
});
it('supports :host() in function form', function() {
let host = element(by.css('app-hero-details'));
it('supports :host() in function form', () => {
const host = element(by.css('app-hero-details'));
host.element(by.buttonText('Activate')).click();
expect(host.getCssValue('borderWidth')).toEqual('3px');
});
it('allows conditional :host-context() styling', function() {
let h2 = element(by.css('app-hero-details h2'));
it('allows conditional :host-context() styling', () => {
const h2 = element(by.css('app-hero-details h2'));
expect(h2.getCssValue('backgroundColor')).toEqual('rgba(238, 238, 255, 1)'); // #eeeeff
});
it('styles both view and content children with /deep/', function() {
let viewH3 = element(by.css('app-hero-team h3'));
let contentH3 = element(by.css('app-hero-controls h3'));
it('styles both view and content children with /deep/', () => {
const viewH3 = element(by.css('app-hero-team h3'));
const contentH3 = element(by.css('app-hero-controls h3'));
expect(viewH3.getCssValue('fontStyle')).toEqual('italic');
expect(contentH3.getCssValue('fontStyle')).toEqual('italic');
});
it('includes styles loaded with CSS @import', function() {
let host = element(by.css('app-hero-details'));
it('includes styles loaded with CSS @import', () => {
const host = element(by.css('app-hero-details'));
expect(host.getCssValue('padding')).toEqual('10px');
});
it('processes template inline styles', function() {
let button = element(by.css('app-hero-controls button'));
let externalButton = element(by.css('body > button'));
it('processes template inline styles', () => {
const button = element(by.css('app-hero-controls button'));
const externalButton = element(by.css('body > button'));
expect(button.getCssValue('backgroundColor')).toEqual('rgba(255, 255, 255, 1)'); // #ffffff
expect(externalButton.getCssValue('backgroundColor')).not.toEqual('rgba(255, 255, 255, 1)');
});
it('processes template <link>s', function() {
let li = element(by.css('app-hero-team li:first-child'));
let externalLi = element(by.css('body > ul li'));
it('processes template <link>s', () => {
const li = element(by.css('app-hero-team li:first-child'));
const externalLi = element(by.css('body > ul li'));
expect(li.getCssValue('listStyleType')).toEqual('square');
expect(externalLi.getCssValue('listStyleType')).not.toEqual('square');

View File

@ -1,76 +1,74 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor';
describe('Dependency Injection Cookbook', function () {
describe('Dependency Injection Cookbook', () => {
beforeAll(function () {
beforeAll(() => {
browser.get('');
});
it('should render Logged in User example', function () {
let loggedInUser = element.all(by.xpath('//h3[text()="Logged in user"]')).get(0);
it('should render Logged in User example', () => {
const loggedInUser = element.all(by.xpath('//h3[text()="Logged in user"]')).get(0);
expect(loggedInUser).toBeDefined();
});
it('"Bombasto" should be the logged in user', function () {
let loggedInUser = element.all(by.xpath('//div[text()="Name: Bombasto"]')).get(0);
it('"Bombasto" should be the logged in user', () => {
const loggedInUser = element.all(by.xpath('//div[text()="Name: Bombasto"]')).get(0);
expect(loggedInUser).toBeDefined();
});
it('should render sorted heroes', function () {
let sortedHeroes = element.all(by.xpath('//h3[text()="Sorted Heroes" and position()=1]')).get(0);
it('should render sorted heroes', () => {
const sortedHeroes = element.all(by.xpath('//h3[text()="Sorted Heroes" and position()=1]')).get(0);
expect(sortedHeroes).toBeDefined();
});
it('Dr Nice should be in sorted heroes', function () {
let sortedHero = element.all(by.xpath('//sorted-heroes/[text()="Dr Nice" and position()=2]')).get(0);
it('Dr Nice should be in sorted heroes', () => {
const sortedHero = element.all(by.xpath('//sorted-heroes/[text()="Dr Nice" and position()=2]')).get(0);
expect(sortedHero).toBeDefined();
});
it('RubberMan should be in sorted heroes', function () {
let sortedHero = element.all(by.xpath('//sorted-heroes/[text()="RubberMan" and position()=3]')).get(0);
it('RubberMan should be in sorted heroes', () => {
const sortedHero = element.all(by.xpath('//sorted-heroes/[text()="RubberMan" and position()=3]')).get(0);
expect(sortedHero).toBeDefined();
});
it('Magma should be in sorted heroes', function () {
let sortedHero = element.all(by.xpath('//sorted-heroes/[text()="Magma"]')).get(0);
it('Magma should be in sorted heroes', () => {
const sortedHero = element.all(by.xpath('//sorted-heroes/[text()="Magma"]')).get(0);
expect(sortedHero).toBeDefined();
});
it('should render Hero of the Month', function () {
let heroOfTheMonth = element.all(by.xpath('//h3[text()="Hero of the month"]')).get(0);
it('should render Hero of the Month', () => {
const heroOfTheMonth = element.all(by.xpath('//h3[text()="Hero of the month"]')).get(0);
expect(heroOfTheMonth).toBeDefined();
});
it('should render Hero Bios', function () {
let heroBios = element.all(by.xpath('//h3[text()="Hero Bios"]')).get(0);
it('should render Hero Bios', () => {
const heroBios = element.all(by.xpath('//h3[text()="Hero Bios"]')).get(0);
expect(heroBios).toBeDefined();
});
it('should render Magma\'s description in Hero Bios', function () {
let magmaText = element.all(by.xpath('//textarea[text()="Hero of all trades"]')).get(0);
it('should render Magma\'s description in Hero Bios', () => {
const magmaText = element.all(by.xpath('//textarea[text()="Hero of all trades"]')).get(0);
expect(magmaText).toBeDefined();
});
it('should render Magma\'s phone in Hero Bios and Contacts', function () {
let magmaPhone = element.all(by.xpath('//div[text()="Phone #: 555-555-5555"]')).get(0);
it('should render Magma\'s phone in Hero Bios and Contacts', () => {
const magmaPhone = element.all(by.xpath('//div[text()="Phone #: 555-555-5555"]')).get(0);
expect(magmaPhone).toBeDefined();
});
it('should render Hero-of-the-Month runner-ups', function () {
let runnersUp = element(by.id('rups1')).getText();
it('should render Hero-of-the-Month runner-ups', () => {
const runnersUp = element(by.id('rups1')).getText();
expect(runnersUp).toContain('RubberMan, Dr Nice');
});
it('should render DateLogger log entry in Hero-of-the-Month', function () {
let logs = element.all(by.id('logs')).get(0).getText();
it('should render DateLogger log entry in Hero-of-the-Month', () => {
const logs = element.all(by.id('logs')).get(0).getText();
expect(logs).toContain('INFO: starting up at');
});
it('should highlight Hero Bios and Contacts container when mouseover', function () {
let target = element(by.css('div[appHighlight="yellow"]'));
let yellow = 'rgba(255, 255, 0, 1)';
it('should highlight Hero Bios and Contacts container when mouseover', () => {
const target = element(by.css('div[appHighlight="yellow"]'));
const yellow = 'rgba(255, 255, 0, 1)';
expect(target.getCssValue('background-color')).not.toEqual(yellow);
@ -81,25 +79,25 @@ describe('Dependency Injection Cookbook', function () {
browser.wait(() => target.getCssValue('background-color').then(c => c === yellow), 2000);
});
describe('in Parent Finder', function () {
let cathy1 = element(by.css('alex cathy'));
let craig1 = element(by.css('alex craig'));
let carol1 = element(by.css('alex carol p'));
let carol2 = element(by.css('barry carol p'));
describe('in Parent Finder', () => {
const cathy1 = element(by.css('alex cathy'));
const craig1 = element(by.css('alex craig'));
const carol1 = element(by.css('alex carol p'));
const carol2 = element(by.css('barry carol p'));
it('"Cathy" should find "Alex" via the component class', function () {
it('"Cathy" should find "Alex" via the component class', () => {
expect(cathy1.getText()).toContain('Found Alex via the component');
});
it('"Craig" should not find "Alex" via the base class', function () {
it('"Craig" should not find "Alex" via the base class', () => {
expect(craig1.getText()).toContain('Did not find Alex via the base');
});
it('"Carol" within "Alex" should have "Alex" parent', function () {
it('"Carol" within "Alex" should have "Alex" parent', () => {
expect(carol1.getText()).toContain('Alex');
});
it('"Carol" within "Barry" should have "Barry" parent', function () {
it('"Carol" within "Barry" should have "Barry" parent', () => {
expect(carol2.getText()).toContain('Barry');
});
});

View File

@ -1,5 +1,5 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [];

View File

@ -2,9 +2,9 @@
import { Component } from '@angular/core';
// #docregion import-services
import { LoggerService } from './logger.service';
import { LoggerService } from './logger.service';
import { UserContextService } from './user-context.service';
import { UserService } from './user.service';
import { UserService } from './user.service';
@Component({
selector: 'app-root',

View File

@ -1,26 +1,26 @@
// #docregion
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
// import { AppRoutingModule } from './app-routing.module';
// import { AppRoutingModule } from './app-routing.module';
import { LocationStrategy,
HashLocationStrategy } from '@angular/common';
import { NgModule } from '@angular/core';
HashLocationStrategy } from '@angular/common';
import { NgModule } from '@angular/core';
import { HeroData } from './hero-data';
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
import { HeroData } from './hero-data';
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
import { AppComponent } from './app.component';
import { HeroBioComponent } from './hero-bio.component';
import { AppComponent } from './app.component';
import { HeroBioComponent } from './hero-bio.component';
import { HeroBiosComponent,
HeroBiosAndContactsComponent } from './hero-bios.component';
import { HeroOfTheMonthComponent } from './hero-of-the-month.component';
import { HeroContactComponent } from './hero-contact.component';
import { HeroOfTheMonthComponent } from './hero-of-the-month.component';
import { HeroContactComponent } from './hero-contact.component';
import { HeroesBaseComponent,
SortedHeroesComponent } from './sorted-heroes.component';
import { HighlightDirective } from './highlight.directive';
SortedHeroesComponent } from './sorted-heroes.component';
import { HighlightDirective } from './highlight.directive';
import { ParentFinderComponent,
AlexComponent,
AliceComponent,
@ -30,8 +30,8 @@ import { ParentFinderComponent,
CathyComponent,
BarryComponent,
BethComponent,
BobComponent } from './parent-finder.component';
import { StorageComponent } from './storage.component';
BobComponent } from './parent-finder.component';
import { StorageComponent } from './storage.component';
const declarations = [
AppComponent,
@ -42,11 +42,11 @@ const declarations = [
ParentFinderComponent,
];
const a_components = [AliceComponent, AlexComponent ];
const componentListA = [ AliceComponent, AlexComponent ];
const b_components = [ BarryComponent, BethComponent, BobComponent ];
const componentListB = [ BarryComponent, BethComponent, BobComponent ];
const c_components = [
const componentListC = [
CarolComponent, ChrisComponent, CraigComponent,
CathyComponent
];
@ -61,9 +61,9 @@ const c_components = [
],
declarations: [
declarations,
a_components,
b_components,
c_components,
componentListA,
componentListB,
componentListC,
StorageComponent,
],
bootstrap: [ AppComponent ],

View File

@ -1,6 +1,6 @@
/* tslint:disable:one-line*/
// #docregion
import { Injectable } from '@angular/core';
import { Injectable } from '@angular/core';
import { LoggerService } from './logger.service';

View File

@ -1,7 +1,7 @@
// #docregion
import { Component, Input, OnInit } from '@angular/core';
import { HeroCacheService } from './hero-cache.service';
import { HeroCacheService } from './hero-cache.service';
// #docregion component
@Component({

View File

@ -1,9 +1,9 @@
// #docplaster
// #docregion
import { Component } from '@angular/core';
import { Component } from '@angular/core';
import { HeroService } from './hero.service';
import { LoggerService } from './logger.service';
import { HeroService } from './hero.service';
import { LoggerService } from './logger.service';
//////// HeroBiosComponent ////
// #docregion simple

View File

@ -1,7 +1,7 @@
// #docregion
import { Injectable } from '@angular/core';
import { Injectable } from '@angular/core';
import { Hero } from './hero';
import { Hero } from './hero';
import { HeroService } from './hero.service';
// #docregion service

View File

@ -3,7 +3,7 @@
import { Component, Host, Optional } from '@angular/core';
import { HeroCacheService } from './hero-cache.service';
import { LoggerService } from './logger.service';
import { LoggerService } from './logger.service';
// #docregion component
@Component({

View File

@ -3,7 +3,7 @@ import { Hero } from './hero';
export class HeroData {
createDb() {
let heroes = [
const heroes = [
new Hero(1, 'Windstorm'),
new Hero(2, 'Bombasto'),
new Hero(3, 'Magneta'),

View File

@ -1,8 +1,8 @@
// Illustrative (not used), mini-version of the actual HeroOfTheMonthComponent
// Injecting with the MinimalLogger "interface-class"
import { Component, NgModule } from '@angular/core';
import { LoggerService } from './logger.service';
import { MinimalLogger } from './minimal-logger.service';
import { LoggerService } from './logger.service';
import { MinimalLogger } from './minimal-logger.service';
// #docregion
@Component({

View File

@ -10,12 +10,12 @@ export const TITLE = new InjectionToken<string>('title');
import { Component, Inject } from '@angular/core';
import { DateLoggerService } from './date-logger.service';
import { Hero } from './hero';
import { HeroService } from './hero.service';
import { LoggerService } from './logger.service';
import { MinimalLogger } from './minimal-logger.service';
import { Hero } from './hero';
import { HeroService } from './hero.service';
import { LoggerService } from './logger.service';
import { MinimalLogger } from './minimal-logger.service';
import { RUNNERS_UP,
runnersUpFactory } from './runners-up';
runnersUpFactory } from './runners-up';
// #enddocregion hero-of-the-month
// #docregion some-hero

View File

@ -1,6 +1,6 @@
// #docregion
import { Injectable } from '@angular/core';
import { Hero } from './hero';
import { Hero } from './hero';
@Injectable({
providedIn: 'root'

View File

@ -1,5 +1,4 @@
/* tslint:disable:no-unused-variable component-selector-name one-line check-open-brace */
/* tslint:disable:*/
// tslint:disable: component-selector space-before-function-paren
// #docplaster
// #docregion
import { Component, forwardRef, Optional, SkipSelf } from '@angular/core';
@ -20,8 +19,7 @@ const DifferentParent = Parent;
// The `parentType` defaults to `Parent` when omitting the second parameter.
// #docregion provide-the-parent
export function provideParent
// #enddocregion provide-parent, provide-the-parent
// #docregion provide-parent
// #enddocregion provide-the-parent
(component: any, parentType?: any) {
return { provide: parentType || Parent, useExisting: forwardRef(() => component) };
}

View File

@ -2,7 +2,7 @@
// #docregion
import { InjectionToken } from '@angular/core';
import { Hero } from './hero';
import { Hero } from './hero';
import { HeroService } from './hero.service';
// #docregion runners-up
@ -22,5 +22,5 @@ export function runnersUpFactory(take: number) {
.join(', ');
// #docregion factory-synopsis
};
};
}
// #enddocregion factory-synopsis

View File

@ -2,8 +2,8 @@
// #docregion
import { Component, OnInit } from '@angular/core';
import { Hero } from './hero';
import { HeroService } from './hero.service';
import { Hero } from './hero';
import { HeroService } from './hero.service';
/////// HeroesBaseComponent /////
// #docregion heroes-base, injection

View File

@ -1,9 +1,9 @@
// #docplaster
// #docregion
import { Injectable } from '@angular/core';
import { Injectable } from '@angular/core';
import { LoggerService } from './logger.service';
import { UserService } from './user.service';
import { UserService } from './user.service';
// #docregion injectables, injectable
@Injectable({
@ -24,7 +24,7 @@ export class UserContextService {
// #enddocregion ctor, injectables
loadUser(userId: number) {
let user = this.userService.getUserById(userId);
const user = this.userService.getUserById(userId);
this.name = user.name;
this.role = user.role;

View File

@ -1,202 +1,196 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by, ElementFinder } from 'protractor';
describe('Dependency Injection Tests', function () {
describe('Dependency Injection Tests', () => {
let expectedMsg: string;
let expectedMsgRx: RegExp;
beforeAll(function () {
beforeAll(() => {
browser.get('');
});
describe('Cars:', function() {
describe('Cars:', () => {
it('DI car displays as expected', function () {
it('DI car displays as expected', () => {
expectedMsg = 'DI car with 4 cylinders and Flintstone tires.';
expect(element(by.css('#di')).getText()).toEqual(expectedMsg);
});
it('No DI car displays as expected', function () {
it('No DI car displays as expected', () => {
expectedMsg = 'No DI car with 4 cylinders and Flintstone tires.';
expect(element(by.css('#nodi')).getText()).toEqual(expectedMsg);
});
it('Injector car displays as expected', function () {
it('Injector car displays as expected', () => {
expectedMsg = 'Injector car with 4 cylinders and Flintstone tires.';
expect(element(by.css('#injector')).getText()).toEqual(expectedMsg);
});
it('Factory car displays as expected', function () {
it('Factory car displays as expected', () => {
expectedMsg = 'Factory car with 4 cylinders and Flintstone tires.';
expect(element(by.css('#factory')).getText()).toEqual(expectedMsg);
});
it('Simple car displays as expected', function () {
it('Simple car displays as expected', () => {
expectedMsg = 'Simple car with 4 cylinders and Flintstone tires.';
expect(element(by.css('#simple')).getText()).toEqual(expectedMsg);
});
it('Super car displays as expected', function () {
it('Super car displays as expected', () => {
expectedMsg = 'Super car with 12 cylinders and Flintstone tires.';
expect(element(by.css('#super')).getText()).toEqual(expectedMsg);
});
it('Test car displays as expected', function () {
it('Test car displays as expected', () => {
expectedMsg = 'Test car with 8 cylinders and YokoGoodStone tires.';
expect(element(by.css('#test')).getText()).toEqual(expectedMsg);
});
});
describe('Other Injections:', function() {
it('DI car displays as expected', function () {
describe('Other Injections:', () => {
it('DI car displays as expected', () => {
expectedMsg = 'DI car with 4 cylinders and Flintstone tires.';
expect(element(by.css('#car')).getText()).toEqual(expectedMsg);
});
it('Hero displays as expected', function () {
it('Hero displays as expected', () => {
expectedMsg = 'Dr Nice';
expect(element(by.css('#hero')).getText()).toEqual(expectedMsg);
});
it('Optional injection displays as expected', function () {
it('Optional injection displays as expected', () => {
expectedMsg = 'R.O.U.S.\'s? I don\'t think they exist!';
expect(element(by.css('#rodent')).getText()).toEqual(expectedMsg);
});
});
describe('Tests:', function() {
describe('Tests:', () => {
it('Tests display as expected', function () {
it('Tests display as expected', () => {
expectedMsgRx = /Tests passed/;
expect(element(by.css('#tests')).getText()).toMatch(expectedMsgRx);
});
});
describe('Provider variations:', function() {
describe('Provider variations:', () => {
it('P1 (class) displays as expected', function () {
it('P1 (class) displays as expected', () => {
expectedMsg = 'Hello from logger provided with Logger class';
expect(element(by.css('#p1')).getText()).toEqual(expectedMsg);
});
it('P3 (provide) displays as expected', function () {
it('P3 (provide) displays as expected', () => {
expectedMsg = 'Hello from logger provided with useClass:Logger';
expect(element(by.css('#p3')).getText()).toEqual(expectedMsg);
});
it('P4 (useClass:BetterLogger) displays as expected', function () {
it('P4 (useClass:BetterLogger) displays as expected', () => {
expectedMsg = 'Hello from logger provided with useClass:BetterLogger';
expect(element(by.css('#p4')).getText()).toEqual(expectedMsg);
});
it('P5 (useClass:EvenBetterLogger - dependency) displays as expected', function () {
it('P5 (useClass:EvenBetterLogger - dependency) displays as expected', () => {
expectedMsg = 'Message to Bob: Hello from EvenBetterlogger';
expect(element(by.css('#p5')).getText()).toEqual(expectedMsg);
});
it('P6a (no alias) displays as expected', function () {
it('P6a (no alias) displays as expected', () => {
expectedMsg = 'Hello OldLogger (but we want NewLogger)';
expect(element(by.css('#p6a')).getText()).toEqual(expectedMsg);
});
it('P6b (alias) displays as expected', function () {
it('P6b (alias) displays as expected', () => {
expectedMsg = 'Hello from NewLogger (via aliased OldLogger)';
expect(element(by.css('#p6b')).getText()).toEqual(expectedMsg);
});
it('P7 (useValue) displays as expected', function () {
it('P7 (useValue) displays as expected', () => {
expectedMsg = 'Silent logger says "Shhhhh!". Provided via "useValue"';
expect(element(by.css('#p7')).getText()).toEqual(expectedMsg);
});
it('P8 (useFactory) displays as expected', function () {
it('P8 (useFactory) displays as expected', () => {
expectedMsg = 'Hero service injected successfully via heroServiceProvider';
expect(element(by.css('#p8')).getText()).toEqual(expectedMsg);
});
it('P9 (InjectionToken) displays as expected', function () {
it('P9 (InjectionToken) displays as expected', () => {
expectedMsg = 'APP_CONFIG Application title is Dependency Injection';
expect(element(by.css('#p9')).getText()).toEqual(expectedMsg);
});
it('P10 (optional dependency) displays as expected', function () {
it('P10 (optional dependency) displays as expected', () => {
expectedMsg = 'Optional logger was not available';
expect(element(by.css('#p10')).getText()).toEqual(expectedMsg);
});
});
describe('User/Heroes:', function() {
it('User is Bob - unauthorized', function () {
describe('User/Heroes:', () => {
it('User is Bob - unauthorized', () => {
expectedMsgRx = /Bob, is not authorized/;
expect(element(by.css('#user')).getText()).toMatch(expectedMsgRx);
});
it('should have button', function () {
it('should have button', () => {
expect(element.all(by.cssContainingText('button', 'Next User'))
.get(0).isDisplayed()).toBe(true, '\'Next User\' button should be displayed');
});
it('unauthorized user should have multiple unauthorized heroes', function () {
let heroes = element.all(by.css('#unauthorized app-hero-list div'));
it('unauthorized user should have multiple unauthorized heroes', () => {
const heroes = element.all(by.css('#unauthorized app-hero-list div'));
expect(heroes.count()).toBeGreaterThan(0);
});
it('unauthorized user should have no secret heroes', function () {
let heroes = element.all(by.css('#unauthorized app-hero-list div'));
it('unauthorized user should have no secret heroes', () => {
const heroes = element.all(by.css('#unauthorized app-hero-list div'));
expect(heroes.count()).toBeGreaterThan(0);
let filteredHeroes = heroes.filter((elem: ElementFinder, index: number) => {
return elem.getText().then((text: string) => {
return /secret/.test(text);
});
const filteredHeroes = heroes.filter((elem: ElementFinder, index: number) => {
return elem.getText().then((text: string) => /secret/.test(text));
});
expect(filteredHeroes.count()).toEqual(0);
});
it('unauthorized user should have no authorized heroes listed', function () {
it('unauthorized user should have no authorized heroes listed', () => {
expect(element.all(by.css('#authorized app-hero-list div')).count()).toEqual(0);
});
describe('after button click', function() {
describe('after button click', () => {
beforeAll(function (done: any) {
let buttonEle = element.all(by.cssContainingText('button', 'Next User')).get(0);
beforeAll((done: any) => {
const buttonEle = element.all(by.cssContainingText('button', 'Next User')).get(0);
buttonEle.click().then(done, done);
});
it('User is Alice - authorized', function () {
it('User is Alice - authorized', () => {
expectedMsgRx = /Alice, is authorized/;
expect(element(by.css('#user')).getText()).toMatch(expectedMsgRx);
});
it('authorized user should have multiple authorized heroes ', function () {
let heroes = element.all(by.css('#authorized app-hero-list div'));
it('authorized user should have multiple authorized heroes ', () => {
const heroes = element.all(by.css('#authorized app-hero-list div'));
expect(heroes.count()).toBeGreaterThan(0);
});
it('authorized user should have multiple authorized heroes with tree-shakeable HeroesService', function () {
let heroes = element.all(by.css('#tspAuthorized app-hero-list div'));
it('authorized user should have multiple authorized heroes with tree-shakeable HeroesService', () => {
const heroes = element.all(by.css('#tspAuthorized app-hero-list div'));
expect(heroes.count()).toBeGreaterThan(0);
});
it('authorized user should have secret heroes', function () {
let heroes = element.all(by.css('#authorized app-hero-list div'));
it('authorized user should have secret heroes', () => {
const heroes = element.all(by.css('#authorized app-hero-list div'));
expect(heroes.count()).toBeGreaterThan(0);
let filteredHeroes = heroes.filter(function(elem: ElementFinder, index: number) {
return elem.getText().then(function(text: string) {
return /secret/.test(text);
});
const filteredHeroes = heroes.filter((elem: ElementFinder, index: number) => {
return elem.getText().then((text: string) => /secret/.test(text));
});
expect(filteredHeroes.count()).toBeGreaterThan(0);
});
it('authorized user should have no unauthorized heroes listed', function () {
it('authorized user should have no unauthorized heroes listed', () => {
expect(element.all(by.css('#unauthorized app-hero-list div')).count()).toEqual(0);
});
});

View File

@ -2,7 +2,7 @@
// #docregion imports
import { Component, Inject } from '@angular/core';
import { APP_CONFIG, AppConfig } from './app.config';
import { APP_CONFIG, AppConfig } from './app.config';
// #enddocregion imports
@Component({

View File

@ -1,8 +1,8 @@
// #docplaster
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserModule } from '@angular/platform-browser';
import { APP_CONFIG, HERO_DI_CONFIG } from './app.config';
import { APP_CONFIG, HERO_DI_CONFIG } from './app.config';
import { AppComponent } from './app.component';
import { CarComponent } from './car/car.component';
import { HeroesComponent } from './heroes/heroes.component';

View File

@ -7,7 +7,7 @@ import { Car, Engine, Tires } from './car';
export function simpleCar() {
// #docregion car-ctor-instantiation
// Simple car with 4 cylinders and Flintstone tires.
let car = new Car(new Engine(), new Tires());
const car = new Car(new Engine(), new Tires());
// #enddocregion car-ctor-instantiation
car.description = 'Simple';
return car;
@ -16,30 +16,31 @@ export function simpleCar() {
///////// example 2 ////////////
// #docregion car-ctor-instantiation-with-param
class Engine2 {
constructor(public cylinders: number) { }
}
class Engine2 {
constructor(public cylinders: number) { }
}
// #enddocregion car-ctor-instantiation-with-param
export function superCar() {
// #docregion car-ctor-instantiation-with-param
// #docregion car-ctor-instantiation-with-param
// Super car with 12 cylinders and Flintstone tires.
let bigCylinders = 12;
let car = new Car(new Engine2(bigCylinders), new Tires());
// #enddocregion car-ctor-instantiation-with-param
const bigCylinders = 12;
const car = new Car(new Engine2(bigCylinders), new Tires());
// #enddocregion car-ctor-instantiation-with-param
car.description = 'Super';
return car;
}
/////////// example 3 //////////
// #docregion car-ctor-instantiation-with-mocks
class MockEngine extends Engine { cylinders = 8; }
class MockTires extends Tires { make = 'YokoGoodStone'; }
// #docregion car-ctor-instantiation-with-mocks
class MockEngine extends Engine { cylinders = 8; }
class MockTires extends Tires { make = 'YokoGoodStone'; }
// #enddocregion car-ctor-instantiation-with-mocks
// #enddocregion car-ctor-instantiation-with-mocks
export function testCar() {
// #docregion car-ctor-instantiation-with-mocks
// Test car with 8 cylinders and YokoGoodStone tires.
let car = new Car(new MockEngine(), new MockTires());
const car = new Car(new MockEngine(), new MockTires());
// #enddocregion car-ctor-instantiation-with-mocks
car.description = 'Test';
return car;

View File

@ -4,7 +4,7 @@ import { Engine, Tires, Car } from './car';
// BAD pattern!
export class CarFactory {
createCar() {
let car = new Car(this.createEngine(), this.createTires());
const car = new Car(this.createEngine(), this.createTires());
car.description = 'Factory';
return car;
}

View File

@ -1,7 +1,7 @@
import { Injector } from '@angular/core';
import { Car, Engine, Tires } from './car';
import { Logger } from '../logger.service';
import { Logger } from '../logger.service';
// #docregion injector
export function useInjector() {
@ -26,14 +26,14 @@ export function useInjector() {
]
});
// #docregion injector-call
let car = injector.get(Car);
const car = injector.get(Car);
// #enddocregion injector-call, injector-create-and-call
car.description = 'Injector';
injector = Injector.create({
providers: [{ provide: Logger, deps: [] }]
});
let logger = injector.get(Logger);
const logger = injector.get(Logger);
logger.log('Injector car.drive() said: ' + car.drive());
return car;
}

View File

@ -1,15 +1,15 @@
// #docregion
import { Component } from '@angular/core';
import { Car, Engine, Tires } from './car';
import { Car as CarNoDi } from './car-no-di';
import { CarFactory } from './car-factory';
import { Car, Engine, Tires } from './car';
import { Car as CarNoDi } from './car-no-di';
import { CarFactory } from './car-factory';
import { testCar,
simpleCar,
superCar } from './car-creations';
superCar } from './car-creations';
import { useInjector } from './car-injector';
import { useInjector } from './car-injector';
@Component({
@ -27,9 +27,9 @@ import { useInjector } from './car-injector';
providers: [Car, Engine, Tires]
})
export class CarComponent {
factoryCar = (new CarFactory).createCar();
factoryCar = (new CarFactory()).createCar();
injectorCar = useInjector();
noDiCar = new CarNoDi;
noDiCar = new CarNoDi();
simpleCar = simpleCar();
superCar = superCar();
testCar = testCar();

View File

@ -1,6 +1,6 @@
// #docregion
import { Component } from '@angular/core';
import { HEROES } from './mock-heroes';
import { Component } from '@angular/core';
import { HEROES } from './mock-heroes';
@Component({
selector: 'app-hero-list',

View File

@ -1,7 +1,7 @@
// #docplaster
// #docregion
import { Component } from '@angular/core';
import { Hero } from './hero';
import { Component } from '@angular/core';
import { Hero } from './hero';
// #enddocregion
import { HeroService } from './hero.service.1';
/*

View File

@ -1,7 +1,7 @@
/* tslint:disable:one-line */
// #docregion
import { Component } from '@angular/core';
import { Hero } from './hero';
import { Component } from '@angular/core';
import { Hero } from './hero';
import { HeroService } from './hero.service';
@Component({

View File

@ -1,6 +1,6 @@
// #docregion
import { Injectable } from '@angular/core';
import { HEROES } from './mock-heroes';
import { HEROES } from './mock-heroes';
@Injectable({
providedIn: 'root',

View File

@ -1,7 +1,7 @@
// #docregion
import { Injectable } from '@angular/core';
import { HEROES } from './mock-heroes';
import { Logger } from '../logger.service';
import { HEROES } from './mock-heroes';
import { Logger } from '../logger.service';
@Injectable({
providedIn: 'root',

View File

@ -1,11 +1,11 @@
/* tslint:disable:one-line */
// #docregion
import { HeroService } from './hero.service';
import { Logger } from '../logger.service';
import { Logger } from '../logger.service';
import { UserService } from '../user.service';
// #docregion factory
let heroServiceFactory = (logger: Logger, userService: UserService) => {
const heroServiceFactory = (logger: Logger, userService: UserService) => {
return new HeroService(logger, userService.user.isAuthorized);
};
// #enddocregion factory

View File

@ -1,7 +1,7 @@
// #docregion
import { Injectable } from '@angular/core';
import { HEROES } from './mock-heroes';
import { Logger } from '../logger.service';
import { HEROES } from './mock-heroes';
import { Logger } from '../logger.service';
import { UserService } from '../user.service';
@Injectable({
@ -17,7 +17,7 @@ export class HeroService {
private isAuthorized: boolean) { }
getHeroes() {
let auth = this.isAuthorized ? 'authorized ' : 'unauthorized';
const auth = this.isAuthorized ? 'authorized ' : 'unauthorized';
this.logger.log(`Getting heroes for ${auth} user.`);
return HEROES.filter(hero => this.isAuthorized || !hero.isSecret);
}

View File

@ -1,5 +1,5 @@
// #docregion
import { Component } from '@angular/core';
import { Component } from '@angular/core';
import { heroServiceProvider } from './hero.service.provider';
@Component({

View File

@ -2,11 +2,11 @@
// #docregion
import { Component, Injector, OnInit } from '@angular/core';
import { Car, Engine, Tires } from './car/car';
import { Hero } from './heroes/hero';
import { HeroService } from './heroes/hero.service';
import { heroServiceProvider } from './heroes/hero.service.provider';
import { Logger } from './logger.service';
import { Car, Engine, Tires } from './car/car';
import { Hero } from './heroes/hero';
import { HeroService } from './heroes/hero.service';
import { heroServiceProvider } from './heroes/hero.service.provider';
import { Logger } from './logger.service';
// #docregion injector
@Component({
@ -36,7 +36,7 @@ export class InjectorComponent implements OnInit {
}
get rodent() {
let rousDontExist = `R.O.U.S.'s? I don't think they exist!`;
const rousDontExist = `R.O.U.S.'s? I don't think they exist!`;
return this.injector.get(ROUS, rousDontExist);
}
}

View File

@ -18,7 +18,7 @@ const template = '{{log}}';
@Component({
selector: 'provider-1',
template: template,
template,
// #docregion providers-1, providers-logger
providers: [Logger]
// #enddocregion providers-1, providers-logger
@ -35,7 +35,7 @@ export class Provider1Component {
@Component({
selector: 'provider-3',
template: template,
template,
providers:
// #docregion providers-3
[{ provide: Logger, useClass: Logger }]
@ -54,7 +54,7 @@ export class BetterLogger extends Logger {}
@Component({
selector: 'provider-4',
template: template,
template,
providers:
// #docregion providers-4
[{ provide: Logger, useClass: BetterLogger }]
@ -76,7 +76,7 @@ export class EvenBetterLogger extends Logger {
constructor(private userService: UserService) { super(); }
log(message: string) {
let name = this.userService.user.name;
const name = this.userService.user.name;
super.log(`Message to ${name}: ${message}`);
}
}
@ -84,7 +84,7 @@ export class EvenBetterLogger extends Logger {
@Component({
selector: 'provider-5',
template: template,
template,
providers:
// #docregion providers-5
[ UserService,
@ -107,12 +107,12 @@ export class OldLogger {
logs: string[] = [];
log(message: string) {
throw new Error('Should not call the old logger!');
};
}
}
@Component({
selector: 'provider-6a',
template: template,
template,
providers:
// #docregion providers-6a
[ NewLogger,
@ -135,7 +135,7 @@ export class Provider6aComponent {
@Component({
selector: 'provider-6b',
template: template,
template,
providers:
// #docregion providers-6b
[ NewLogger,
@ -168,7 +168,7 @@ export const SilentLogger = {
@Component({
selector: 'provider-7',
template: template,
template,
providers:
// #docregion providers-7
[{ provide: Logger, useValue: SilentLogger }]
@ -186,7 +186,7 @@ export class Provider7Component {
@Component({
selector: 'provider-8',
template: template,
template,
providers: [heroServiceProvider, Logger, UserService]
})
export class Provider8Component {
@ -202,7 +202,7 @@ export class Provider8Component {
@Component({
selector: 'provider-9',
template: template,
template,
/*
// #docregion providers-9-interface
// FAIL! Can't use interface as provider token
@ -237,11 +237,11 @@ export class Provider9Component implements OnInit {
import { Optional } from '@angular/core';
// #enddocregion import-optional
let some_message = 'Hello from the injected logger';
const someMessage = 'Hello from the injected logger';
@Component({
selector: 'provider-10',
template: template,
template,
providers: [{ provide: Logger, useValue: null }]
})
export class Provider10Component implements OnInit {
@ -249,7 +249,7 @@ export class Provider10Component implements OnInit {
// #docregion provider-10-ctor
constructor(@Optional() private logger?: Logger) {
if (this.logger) {
this.logger.log(some_message);
this.logger.log(someMessage);
}
}
// #enddocregion provider-10-ctor

View File

@ -43,7 +43,7 @@ var testResults: {pass: string; message: string};
function expect(actual: any) {
return {
toEqual: function(expected: any){
toEqual: (expected: any) => {
testResults = actual === expected ?
{pass: 'passed', message: testName} :
{pass: 'failed', message: `${testName}; expected ${actual} to equal ${expected}.`};

View File

@ -1,6 +1,6 @@
import { NgModule } from '@angular/core';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
import { RouterModule } from '@angular/router';
import { ServiceModule } from './service-and-module';
// #docregion

View File

@ -8,8 +8,8 @@ export class User {
}
// TODO: get the user; don't 'new' it.
let alice = new User('Alice', true);
let bob = new User('Bob', false);
const alice = new User('Alice', true);
const bob = new User('Bob', false);
@Injectable({
providedIn: 'root'

View File

@ -1,29 +1,27 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor';
describe('Displaying Data Tests', function () {
let _title = 'Tour of Heroes';
let _defaultHero = 'Windstorm';
describe('Displaying Data Tests', () => {
const title = 'Tour of Heroes';
const defaultHero = 'Windstorm';
beforeAll(function () {
beforeAll(() => {
browser.get('');
});
it('should display correct title: ' + _title, function () {
expect(element(by.css('h1')).getText()).toEqual(_title);
it('should display correct title: ' + title, () => {
expect(element(by.css('h1')).getText()).toEqual(title);
});
it('should have correct default hero: ' + _defaultHero, function () {
expect(element(by.css('h2')).getText()).toContain(_defaultHero);
it('should have correct default hero: ' + defaultHero, () => {
expect(element(by.css('h2')).getText()).toContain(defaultHero);
});
it('should have heroes', function () {
let heroEls = element.all(by.css('li'));
it('should have heroes', () => {
const heroEls = element.all(by.css('li'));
expect(heroEls.count()).not.toBe(0, 'should have heroes');
});
it('should display "there are many heroes!"', function () {
it('should display "there are many heroes!"', () => {
expect(element(by.css('ul ~ p')).getText()).toContain('There are many heroes!');
});
});

View File

@ -1,6 +1,6 @@
// #docregion
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';

View File

@ -1,15 +1,13 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor';
describe('Docs Style Guide', function () {
let _title = 'Authors Style Guide Sample';
describe('Docs Style Guide', () => {
const title = 'Authors Style Guide Sample';
beforeAll(function () {
beforeAll(() => {
browser.get('');
});
it('should display correct title: ' + _title, function () {
expect(element(by.css('h1')).getText()).toEqual(_title);
it('should display correct title: ' + title, () => {
expect(element(by.css('h1')).getText()).toEqual(title);
});
});

View File

@ -1,9 +1,9 @@
// #docregion
import { NgModule } from '@angular/core';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { AppComponent } from './app.component';
// #docregion class
@NgModule({

View File

@ -1,18 +1,16 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor';
/* tslint:disable:quotemark */
describe('Dynamic Component Loader', function () {
describe('Dynamic Component Loader', () => {
beforeEach(function () {
beforeEach(() => {
browser.get('');
});
it('should load ad banner', function () {
let headline = element(by.xpath("//h4[text()='Featured Hero Profile']"));
let name = element(by.xpath("//h3[text()='Bombasto']"));
let bio = element(by.xpath("//p[text()='Brave as they come']"));
it('should load ad banner', () => {
const headline = element(by.xpath("//h4[text()='Featured Hero Profile']"));
const name = element(by.xpath("//h3[text()='Bombasto']"));
const bio = element(by.xpath("//p[text()='Brave as they come']"));
expect(name).toBeDefined();
expect(headline).toBeDefined();

View File

@ -2,7 +2,7 @@
import { Component, Input, OnInit, ViewChild, ComponentFactoryResolver, OnDestroy } from '@angular/core';
import { AdDirective } from './ad.directive';
import { AdItem } from './ad-item';
import { AdItem } from './ad-item';
import { AdComponent } from './ad.component';
@Component({
@ -11,7 +11,7 @@ import { AdComponent } from './ad.component';
template: `
<div class="ad-banner-example">
<h3>Advertisements</h3>
<ng-template ad-host></ng-template>
<ng-template adHost></ng-template>
</div>
`
// #enddocregion ad-host
@ -43,8 +43,8 @@ export class AdBannerComponent implements OnInit, OnDestroy {
const viewContainerRef = this.adHost.viewContainerRef;
viewContainerRef.clear();
const componentRef = viewContainerRef.createComponent(componentFactory);
(<AdComponent>componentRef.instance).data = adItem.data;
const componentRef = viewContainerRef.createComponent<AdComponent>(componentFactory);
componentRef.instance.data = adItem.data;
}
getAds() {

View File

@ -1,8 +1,9 @@
// tslint:disable: directive-selector
// #docregion
import { Directive, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[ad-host]',
selector: '[adHost]',
})
export class AdDirective {
constructor(public viewContainerRef: ViewContainerRef) { }

View File

@ -1,9 +1,9 @@
// #docregion
import { Injectable } from '@angular/core';
import { Injectable } from '@angular/core';
import { HeroJobAdComponent } from './hero-job-ad.component';
import { HeroJobAdComponent } from './hero-job-ad.component';
import { HeroProfileComponent } from './hero-profile.component';
import { AdItem } from './ad-item';
import { AdItem } from './ad-item';
@Injectable()
export class AdService {

View File

@ -1,8 +1,8 @@
// #docregion
import { Component, OnInit } from '@angular/core';
import { AdService } from './ad.service';
import { AdItem } from './ad-item';
import { AdService } from './ad.service';
import { AdItem } from './ad-item';
@Component({
selector: 'app-root',

View File

@ -1,12 +1,12 @@
// #docregion
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { HeroJobAdComponent } from './hero-job-ad.component';
import { AdBannerComponent } from './ad-banner.component';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { HeroJobAdComponent } from './hero-job-ad.component';
import { AdBannerComponent } from './ad-banner.component';
import { HeroProfileComponent } from './hero-profile.component';
import { AdDirective } from './ad.directive';
import { AdService } from './ad.service';
import { AdDirective } from './ad.directive';
import { AdService } from './ad.service';
@NgModule({
imports: [ BrowserModule ],

View File

@ -1,7 +1,7 @@
// #docregion
import { Component, Input } from '@angular/core';
import { AdComponent } from './ad.component';
import { AdComponent } from './ad.component';
@Component({
template: `

View File

@ -1,7 +1,7 @@
// #docregion
import { Component, Input } from '@angular/core';
import { Component, Input } from '@angular/core';
import { AdComponent } from './ad.component';
import { AdComponent } from './ad.component';
@Component({
template: `

View File

@ -1,27 +1,25 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor';
/* tslint:disable:quotemark */
describe('Dynamic Form', function () {
describe('Dynamic Form', () => {
beforeAll(function () {
beforeAll(() => {
browser.get('');
});
it('should submit form', function () {
let firstNameElement = element.all(by.css('input[id=firstName]')).get(0);
it('should submit form', () => {
const firstNameElement = element.all(by.css('input[id=firstName]')).get(0);
expect(firstNameElement.getAttribute('value')).toEqual('Bombasto');
let emailElement = element.all(by.css('input[id=emailAddress]')).get(0);
let email = 'test@test.com';
const emailElement = element.all(by.css('input[id=emailAddress]')).get(0);
const email = 'test@test.com';
emailElement.sendKeys(email);
expect(emailElement.getAttribute('value')).toEqual(email);
element(by.css('select option[value="solid"]')).click();
let saveButton = element.all(by.css('button')).get(0);
saveButton.click().then(function() {
const saveButton = element.all(by.css('button')).get(0);
saveButton.click().then(() => {
expect(element(by.xpath("//strong[contains(text(),'Saved the following values')]")).isPresent()).toBe(true);
});
});

View File

@ -1,9 +1,9 @@
// #docregion
import { Component } from '@angular/core';
import { Component } from '@angular/core';
import { QuestionService } from './question.service';
import { QuestionBase } from './question-base';
import { Observable } from 'rxjs';
import { QuestionBase } from './question-base';
import { Observable } from 'rxjs';
@Component({
selector: 'app-root',

View File

@ -1,10 +1,10 @@
// #docregion
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { DynamicFormComponent } from './dynamic-form.component';
import { AppComponent } from './app.component';
import { DynamicFormComponent } from './dynamic-form.component';
import { DynamicFormQuestionComponent } from './dynamic-form-question.component';
@NgModule({

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