Compare commits
49 Commits
4.3.2
...
5.0.0-beta
Author | SHA1 | Date | |
---|---|---|---|
54ea5b6ffd | |||
0af03beaed | |||
d71fa734f5 | |||
6f45519d6f | |||
65c9e13105 | |||
9208f0beea | |||
5344be5182 | |||
5db6f38b73 | |||
d22f8f54db | |||
23146c9201 | |||
a5205c686e | |||
807648251f | |||
5c62e300e1 | |||
256bc8acdd | |||
59c23c7bd7 | |||
e03adb9edd | |||
b399cb26d9 | |||
3b588fe2b0 | |||
95635c18c7 | |||
e20cfe1bbc | |||
eb6fb5f87e | |||
ad3029e786 | |||
2a2fe11e8d | |||
7d0f2cd51e | |||
36faba1aab | |||
92179bcc64 | |||
cdb069ab0e | |||
c453b7bcfa | |||
9d97163c64 | |||
f054c8360b | |||
758848961e | |||
99b666614d | |||
3f331b53b2 | |||
375d598a9f | |||
cd67fced1c | |||
a77cf7ee37 | |||
2150b45954 | |||
9f99f4fae2 | |||
c6ad212a98 | |||
47b3ecd9a3 | |||
8c81c62d46 | |||
7e72317059 | |||
0bb8423df9 | |||
95698d93ad | |||
c649da9f0a | |||
0bf0c35bca | |||
97e6901ded | |||
30e76fcd80 | |||
44b50427d9 |
@ -41,7 +41,7 @@ jobs:
|
||||
- restore_cache:
|
||||
key: angular-{{ .Branch }}-{{ checksum "npm-shrinkwrap.json" }}
|
||||
|
||||
- run: bazel run @build_bazel_rules_typescript_node//:bin/npm install
|
||||
- run: bazel run @io_bazel_rules_typescript_node//:bin/npm install
|
||||
- run: bazel build ...
|
||||
- save_cache:
|
||||
key: angular-{{ .Branch }}-{{ checksum "npm-shrinkwrap.json" }}
|
||||
|
@ -1,12 +1,9 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
# force trusty as Google Chrome addon is not supported on Precise
|
||||
dist: trusty
|
||||
node_js:
|
||||
- '6.9.5'
|
||||
|
||||
addons:
|
||||
chrome: stable
|
||||
# firefox: "38.0"
|
||||
apt:
|
||||
sources:
|
||||
@ -56,7 +53,6 @@ env:
|
||||
- CI_MODE=docs_test
|
||||
- CI_MODE=aio
|
||||
- CI_MODE=aio_e2e
|
||||
- CI_MODE=bazel
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
@ -11,15 +11,8 @@ filegroup(
|
||||
# This won't scale in the general case.
|
||||
# TODO(alexeagle): figure out what to do
|
||||
"node_modules/typescript/**",
|
||||
"node_modules/zone.js/**",
|
||||
"node_modules/zone.js/**/*.d.ts",
|
||||
"node_modules/rxjs/**/*.d.ts",
|
||||
"node_modules/rxjs/**/*.js",
|
||||
"node_modules/@types/**/*.d.ts",
|
||||
"node_modules/tsickle/**",
|
||||
"node_modules/hammerjs/**/*.d.ts",
|
||||
"node_modules/protobufjs/**",
|
||||
"node_modules/bytebuffer/**",
|
||||
"node_modules/reflect-metadata/**",
|
||||
"node_modules/minimist/**/*.js",
|
||||
]),
|
||||
)
|
34
CHANGELOG.md
@ -1,17 +1,29 @@
|
||||
<a name="4.3.2"></a>
|
||||
## [4.3.2](https://github.com/angular/angular/compare/4.3.1...4.3.2) (2017-07-26)
|
||||
<a name="5.0.0-beta.0"></a>
|
||||
# [5.0.0-beta.0](https://github.com/angular/angular/compare/4.3.0...5.0.0-beta.0) (2017-07-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** export BrowserModule as apart of BrowserAnimationsModule ([#18263](https://github.com/angular/angular/issues/18263)) ([cbeb197](https://github.com/angular/angular/commit/cbeb197))
|
||||
* **compiler:** add equiv & disp attributes to Xliff2 ICU placeholders ([#18283](https://github.com/angular/angular/issues/18283)) ([a084619](https://github.com/angular/angular/commit/a084619)), closes [#17344](https://github.com/angular/angular/issues/17344)
|
||||
* **compiler:** allow numbers for ICU message cases in lexer ([#18095](https://github.com/angular/angular/issues/18095)) ([a8ac77b](https://github.com/angular/angular/commit/a8ac77b)), closes [#17799](https://github.com/angular/angular/issues/17799)
|
||||
* **core:** invoke error handler outside of the Angular Zone ([#18269](https://github.com/angular/angular/issues/18269)) ([a1bb9c2](https://github.com/angular/angular/commit/a1bb9c2)), closes [#17073](https://github.com/angular/angular/issues/17073) [#7774](https://github.com/angular/angular/issues/7774)
|
||||
* **platform-server:** don't clobber parse5 properties when setting ([#18237](https://github.com/angular/angular/issues/18237)) ([97135e8](https://github.com/angular/angular/commit/97135e8)), closes [#17050](https://github.com/angular/angular/issues/17050)
|
||||
* **router:** child CanActivate guard should wait for parent to complete ([#18110](https://github.com/angular/angular/issues/18110)) ([b9e32c8](https://github.com/angular/angular/commit/b9e32c8)), closes [#15670](https://github.com/angular/angular/issues/15670)
|
||||
* **router:** should throw when lazy loaded module doesn't define any routes ([#15001](https://github.com/angular/angular/issues/15001)) ([be49e0e](https://github.com/angular/angular/commit/be49e0e)), closes [#14596](https://github.com/angular/angular/issues/14596)
|
||||
* **upgrade:** throw error if trying to get injector before setting ([#18209](https://github.com/angular/angular/issues/18209)) ([1f106d7](https://github.com/angular/angular/commit/1f106d7))
|
||||
* **animations:** always camelcase style property names that contain auto styles ([d22f8f5](https://github.com/angular/angular/commit/d22f8f5)), closes [#17938](https://github.com/angular/angular/issues/17938)
|
||||
* **animations:** capture cancelled animation styles within grouped animations ([23146c9](https://github.com/angular/angular/commit/23146c9)), closes [#17170](https://github.com/angular/angular/issues/17170)
|
||||
* **animations:** do not crash animations if a nested component fires CD during CD ([5db6f38](https://github.com/angular/angular/commit/5db6f38)), closes [#18193](https://github.com/angular/angular/issues/18193)
|
||||
* **animations:** make sure @.disabled works in non-animation components ([5344be5](https://github.com/angular/angular/commit/5344be5))
|
||||
* **common:** send flushed body as error instead of null ([5c62e30](https://github.com/angular/angular/commit/5c62e30)), closes [#18181](https://github.com/angular/angular/issues/18181)
|
||||
* **compiler:** ensure jit external id arguments names are unique ([95635c1](https://github.com/angular/angular/commit/95635c1))
|
||||
* **compiler-cli:** don't generate empty <target/> when extracting xliff ([65c9e13](https://github.com/angular/angular/commit/65c9e13)), closes [#15754](https://github.com/angular/angular/issues/15754)
|
||||
* **platform-server:** provide XhrFactory for HttpClient ([8076482](https://github.com/angular/angular/commit/8076482))
|
||||
* **router:** canDeactivate guards should run from bottom to top ([e20cfe1](https://github.com/angular/angular/commit/e20cfe1)), closes [#15657](https://github.com/angular/angular/issues/15657)
|
||||
* **router:** should navigate to the same url when config changes ([eb6fb5f](https://github.com/angular/angular/commit/eb6fb5f)), closes [#15535](https://github.com/angular/angular/issues/15535)
|
||||
* **router:** should run resolvers for the same route concurrently ([ad3029e](https://github.com/angular/angular/commit/ad3029e)), closes [#14279](https://github.com/angular/angular/issues/14279)
|
||||
* **router:** terminal route in custom matcher ([b399cb2](https://github.com/angular/angular/commit/b399cb2))
|
||||
* **upgrade:** allow accessing AngularJS injector from downgraded module ([a5205c6](https://github.com/angular/angular/commit/a5205c6))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **animations:** support :increment and :decrement transition aliases ([6f45519](https://github.com/angular/angular/commit/6f45519))
|
||||
* **upgrade:** propagate touched state of NgModelController ([59c23c7](https://github.com/angular/angular/commit/59c23c7))
|
||||
* **upgrade:** support lazy-loading Angular module into AngularJS app ([30e76fc](https://github.com/angular/angular/commit/30e76fc))
|
||||
|
||||
|
||||
|
||||
@ -27,7 +39,7 @@
|
||||
* **animations:** make sure @.disabled works in non-animation components ([a5c4bb5](https://github.com/angular/angular/commit/a5c4bb5))
|
||||
* **common:** send flushed body as error instead of null ([17b7bc3](https://github.com/angular/angular/commit/17b7bc3)), closes [#18181](https://github.com/angular/angular/issues/18181)
|
||||
* **compiler:** ensure jit external id arguments names are unique ([4671168](https://github.com/angular/angular/commit/4671168))
|
||||
* **compiler-cli:** don't generate empty `<target/>` when extracting xliff ([f0476fc](https://github.com/angular/angular/commit/f0476fc)), closes [#15754](https://github.com/angular/angular/issues/15754)
|
||||
* **compiler-cli:** don't generate empty <target/> when extracting xliff ([f0476fc](https://github.com/angular/angular/commit/f0476fc)), closes [#15754](https://github.com/angular/angular/issues/15754)
|
||||
* **platform-server:** provide XhrFactory for HttpClient ([4ce29f3](https://github.com/angular/angular/commit/4ce29f3))
|
||||
* **router:** canDeactivate guards should run from bottom to top ([1ac78bf](https://github.com/angular/angular/commit/1ac78bf)), closes [#15657](https://github.com/angular/angular/issues/15657)
|
||||
* **router:** should navigate to the same url when config changes ([4340bea](https://github.com/angular/angular/commit/4340bea)), closes [#15535](https://github.com/angular/angular/issues/15535)
|
||||
|
12
WORKSPACE
@ -1,17 +1,11 @@
|
||||
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
|
||||
|
||||
git_repository(
|
||||
name = "build_bazel_rules_typescript",
|
||||
name = "io_bazel_rules_typescript",
|
||||
remote = "https://github.com/bazelbuild/rules_typescript.git",
|
||||
tag = "0.0.5",
|
||||
commit = "3a8404d",
|
||||
)
|
||||
|
||||
load("@build_bazel_rules_typescript//:defs.bzl", "node_repositories")
|
||||
load("@io_bazel_rules_typescript//:defs.bzl", "node_repositories")
|
||||
|
||||
node_repositories(package_json = "//:package.json")
|
||||
|
||||
git_repository(
|
||||
name = "build_bazel_rules_angular",
|
||||
remote = "https://github.com/bazelbuild/rules_angular.git",
|
||||
tag = "0.0.1",
|
||||
)
|
@ -124,7 +124,7 @@ For example, import Angular's `Component` decorator from the `@angular/core` lib
|
||||
|
||||
<code-example path="architecture/src/app/app.component.ts" region="import" linenums="false"></code-example>
|
||||
|
||||
You also import NgModules from Angular _libraries_ using JavaScript import statements:
|
||||
You also import NgModules_ from Angular _libraries_ using JavaScript import statements:
|
||||
|
||||
<code-example path="architecture/src/app/mini-app.ts" region="import-browser-module" linenums="false"></code-example>
|
||||
|
||||
|
@ -193,8 +193,8 @@ Angular supports most recent browsers. This includes the following specific vers
|
||||
|
||||
|
||||
|
||||
Angular's continuous integration process runs unit tests of the framework on all of these browsers for every pull request,
|
||||
using <a href="https://saucelabs.com/">SauceLabs</a> and
|
||||
Angular's continuous integration process runs unit tests of the framework on all of these browsers for every pull request,
|
||||
using <a href="https://saucelabs.com/">SauceLabs</a> and
|
||||
<a href="https://www.browserstack.com">Browserstack</a>.
|
||||
|
||||
|
||||
@ -215,7 +215,7 @@ that implement missing features in JavaScript.
|
||||
|
||||
|
||||
|
||||
A particular browser may require at least one polyfill to run _any_ Angular application.
|
||||
A particular browser may require at least one polyfill to run _any_ Angular application.
|
||||
You may need additional polyfills for specific features.
|
||||
|
||||
The tables below can help you determine which polyfills to load, depending on the browsers you target and the features you use.
|
||||
@ -241,7 +241,7 @@ These are the polyfills required to run an Angular application on each supported
|
||||
<table>
|
||||
|
||||
<tr style="vertical-align: top">
|
||||
|
||||
|
||||
<th>
|
||||
Browsers (Desktop & Mobile)
|
||||
</th>
|
||||
@ -253,7 +253,7 @@ These are the polyfills required to run an Angular application on each supported
|
||||
</tr>
|
||||
|
||||
<tr style="vertical-align: top">
|
||||
|
||||
|
||||
<td>
|
||||
Chrome, Firefox, Edge, Safari 9+
|
||||
</td>
|
||||
@ -265,7 +265,7 @@ These are the polyfills required to run an Angular application on each supported
|
||||
</tr>
|
||||
|
||||
<tr style="vertical-align: top">
|
||||
|
||||
|
||||
<td>
|
||||
Safari 7 & 8, IE10 & 11, Android 4.1+
|
||||
</td>
|
||||
@ -279,7 +279,7 @@ These are the polyfills required to run an Angular application on each supported
|
||||
</tr>
|
||||
|
||||
<tr style="vertical-align: top">
|
||||
|
||||
|
||||
<td>
|
||||
IE9
|
||||
</td>
|
||||
@ -309,7 +309,7 @@ Here are the features which may require additional polyfills:
|
||||
<table>
|
||||
|
||||
<tr style="vertical-align: top">
|
||||
|
||||
|
||||
<th>
|
||||
Feature
|
||||
</th>
|
||||
@ -325,7 +325,7 @@ Here are the features which may require additional polyfills:
|
||||
</tr>
|
||||
|
||||
<tr style="vertical-align: top">
|
||||
|
||||
|
||||
<td>
|
||||
|
||||
[Animations](guide/animations)
|
||||
@ -363,14 +363,14 @@ Here are the features which may require additional polyfills:
|
||||
</tr>
|
||||
|
||||
<tr style="vertical-align: top">
|
||||
|
||||
|
||||
<td>
|
||||
|
||||
[NgClass](api/common/NgClass) on SVG elements
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
|
||||
|
||||
[classList](guide/browser-support#classlist)
|
||||
</td>
|
||||
@ -382,17 +382,16 @@ Here are the features which may require additional polyfills:
|
||||
</tr>
|
||||
|
||||
<tr style="vertical-align: top">
|
||||
|
||||
|
||||
<td>
|
||||
|
||||
|
||||
[Http](guide/http) when sending and receiving binary data
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
|
||||
[Typed Array](guide/browser-support#typedarray)<br>
|
||||
[Blob](guide/browser-support#blob)<br>
|
||||
[FormData](guide/browser-support#formdata)
|
||||
[Typed Array](guide/browser-support#typedarray) <br>[Blob](guide/browser-support#blob)<br>[FormData](guide/browser-support#formdata)
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@ -406,7 +405,7 @@ Here are the features which may require additional polyfills:
|
||||
|
||||
|
||||
### Suggested polyfills ##
|
||||
Below are the polyfills which are used to test the framework itself. They are a good starting point for an application.
|
||||
Below are the polyfills which are used to test the framework itself. They are a good starting point for an application.
|
||||
|
||||
|
||||
<table>
|
||||
@ -543,5 +542,5 @@ Below are the polyfills which are used to test the framework itself. They are a
|
||||
|
||||
|
||||
|
||||
\* Figures are for minified and gzipped code,
|
||||
\* Figures are for minified and gzipped code,
|
||||
computed with the <a href="http://closure-compiler.appspot.com/home">closure compiler</a>.
|
||||
|
@ -29,7 +29,7 @@ import {HttpClientModule} from '@angular/common/http';
|
||||
export class MyAppModule {}
|
||||
```
|
||||
|
||||
Once you import `HttpClientModule` into your app module, you can inject `HttpClient`
|
||||
Once you import `HttpClientModule` into your app module, you can inject `HttpClient`
|
||||
into your components and services.
|
||||
|
||||
## Making a request for JSON data
|
||||
@ -56,7 +56,7 @@ export class MyComponent implements OnInit {
|
||||
|
||||
// Inject HttpClient into your component or service.
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
|
||||
ngOnInit(): void {
|
||||
// Make the HTTP request:
|
||||
this.http.get('/api/items').subscribe(data => {
|
||||
@ -72,7 +72,7 @@ export class MyComponent implements OnInit {
|
||||
|
||||
In the above example, the `data['results']` field access stands out because you use bracket notation to access the results field. If you tried to write `data.results`, TypeScript would correctly complain that the `Object` coming back from HTTP does not have a `results` property. That's because while `HttpClient` parsed the JSON response into an `Object`, it doesn't know what shape that object is.
|
||||
|
||||
You can, however, tell `HttpClient` what type the response will be, which is recommended.
|
||||
You can, however, tell `HttpClient` what type the response will be, which is recommended.
|
||||
To do so, first you define an interface with the correct shape:
|
||||
|
||||
```javascript
|
||||
@ -163,7 +163,7 @@ RxJS has a useful operator called `.retry()`, which automatically resubscribes t
|
||||
|
||||
First, import it:
|
||||
|
||||
```js
|
||||
```js
|
||||
import 'rxjs/add/operator/retry';
|
||||
```
|
||||
|
||||
@ -197,7 +197,7 @@ In addition to fetching data from the server, `HttpClient` supports mutating req
|
||||
|
||||
### Making a POST request
|
||||
|
||||
One common operation is to POST data to a server; for example when submitting a form. The code for
|
||||
One common operation is to POST data to a server; for example when submitting a form. The code for
|
||||
sending a POST request is very similar to the code for GET:
|
||||
|
||||
```javascript
|
||||
@ -261,12 +261,12 @@ The above sections detail how to use the basic HTTP functionality in `@angular/c
|
||||
|
||||
### Intercepting all requests or responses
|
||||
|
||||
A major feature of `@angular/common/http` is _interception_, the ability to declare interceptors which sit in between your application and the backend. When your application makes a request, interceptors transform it
|
||||
A major feature of `@angular/common/http` is _interception_, the ability to declare interceptors which sit in between your application and the backend. When your application makes a request, interceptors transform it
|
||||
before sending it to the server, and the interceptors can transform the response on its way back before your application sees it. This is useful for everything from authentication to logging.
|
||||
|
||||
#### Writing an interceptor
|
||||
|
||||
To implement an interceptor, you declare a class that implements `HttpInterceptor`, which
|
||||
To implement an interceptor, you declare a class that implements `HttpInterceptor`, which
|
||||
has a single `intercept()` method. Here is a simple interceptor which does nothing but forward the request through without altering it:
|
||||
|
||||
```javascript
|
||||
@ -319,7 +319,7 @@ An interceptor must pass through all events that it does not understand or inten
|
||||
|
||||
##### Ordering
|
||||
|
||||
When you provide multiple interceptors in an application, Angular applies them in the order that you
|
||||
When you provide multiple interceptors in an application, Angular applies them in the order that you
|
||||
provided them.
|
||||
|
||||
##### Immutability
|
||||
@ -335,10 +335,10 @@ If you have a need to mutate the request body, you need to copy the request body
|
||||
Since requests are immutable, they cannot be modified directly. To mutate them, use `clone()`:
|
||||
|
||||
```javascript
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpError<any>> {
|
||||
// This is a duplicate. It is exactly the same as the original.
|
||||
const dupReq = req.clone();
|
||||
|
||||
|
||||
// Change the URL and replace 'http://' with 'https://'
|
||||
const secureReq = req.clone({url: req.url.replace('http://', 'https://')});
|
||||
}
|
||||
@ -357,7 +357,7 @@ import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from '@angular/com
|
||||
@Injectable()
|
||||
export class AuthInterceptor implements HttpInterceptor {
|
||||
constructor(private auth: AuthService) {}
|
||||
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
// Get the auth header from the service.
|
||||
const authHeader = this.auth.getAuthorizationHeader();
|
||||
@ -390,7 +390,7 @@ import 'rxjs/add/operator/do';
|
||||
|
||||
export class TimingInterceptor implements HttpInterceptor {
|
||||
constructor(private auth: AuthService) {}
|
||||
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
const started = Date.now();
|
||||
return next
|
||||
@ -398,7 +398,7 @@ export class TimingInterceptor implements HttpInterceptor {
|
||||
.do(event => {
|
||||
if (event instanceof HttpResponse) {
|
||||
const elapsed = Date.now() - started;
|
||||
console.log(`Request for ${req.urlWithParams} took ${elapsed} ms.`);
|
||||
console.log(`Request for ${req.urlWithParams} took ${elapsed} ms.`);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -416,7 +416,7 @@ abstract class HttpCache {
|
||||
* Returns a cached response, if any, or null if not present.
|
||||
*/
|
||||
abstract get(req: HttpRequest<any>): HttpResponse<any>|null;
|
||||
|
||||
|
||||
/**
|
||||
* Adds or updates the response in the cache.
|
||||
*/
|
||||
@ -430,14 +430,14 @@ An interceptor can apply this cache to outgoing requests.
|
||||
@Injectable()
|
||||
export class CachingInterceptor implements HttpInterceptor {
|
||||
constructor(private cache: HttpCache) {}
|
||||
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
// Before doing anything, it's important to only cache GET requests.
|
||||
// Skip this interceptor if the request method isn't GET.
|
||||
if (req.method !== 'GET') {
|
||||
return next.handle(req);
|
||||
}
|
||||
|
||||
|
||||
// First, check the cache to see if this request exists.
|
||||
const cachedResponse = this.cache.get(req);
|
||||
if (cachedResponse) {
|
||||
@ -445,7 +445,7 @@ export class CachingInterceptor implements HttpInterceptor {
|
||||
// the request to the next handler.
|
||||
return Observable.of(cachedResponse);
|
||||
}
|
||||
|
||||
|
||||
// No cached response exists. Go to the network, and cache
|
||||
// the response when it arrives.
|
||||
return next.handle(req).do(event => {
|
||||
@ -469,17 +469,17 @@ intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>
|
||||
if (req.method !== 'GET') {
|
||||
return next.handle(req);
|
||||
}
|
||||
|
||||
|
||||
// This will be an Observable of the cached value if there is one,
|
||||
// or an empty Observable otherwise. It starts out empty.
|
||||
let maybeCachedResponse: Observable<HttpEvent<any>> = Observable.empty();
|
||||
|
||||
|
||||
// Check the cache.
|
||||
const cachedResponse = this.cache.get(req);
|
||||
if (cachedResponse) {
|
||||
maybeCachedResponse = Observable.of(cachedResponse);
|
||||
}
|
||||
|
||||
|
||||
// Create an Observable (but don't subscribe) that represents making
|
||||
// the network request and caching the value.
|
||||
const networkResponse = next.handle(req).do(event => {
|
||||
@ -488,7 +488,7 @@ intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>
|
||||
this.cache.put(req, event);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Now, combine the two and send the cached response first (if there is
|
||||
// one), and the network response second.
|
||||
return Observable.concat(maybeCachedResponse, networkResponse);
|
||||
@ -509,7 +509,7 @@ const req = new HttpRequest('POST', '/upload/file', file, {
|
||||
});
|
||||
```
|
||||
|
||||
This option enables tracking of progress events. Remember, every progress event triggers
|
||||
This option enables tracking of progress events. Remember, every progress event triggers
|
||||
change detection, so only turn them on if you intend to actually update the UI on each event.
|
||||
|
||||
Next, make the request through the `request()` method of `HttpClient`. The result will be an Observable of events, just like with interceptors:
|
||||
@ -532,11 +532,11 @@ http.request(req).subscribe(event => {
|
||||
|
||||
[Cross-Site Request Forgery (XSRF)](https://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by which the attacker can trick an authenticated user into unknowingly executing actions on your website. `HttpClient` supports a [common mechanism](https://en.wikipedia.org/wiki/Cross-site_request_forgery#Cookie-to-Header_Token) used to prevent XSRF attacks. When performing HTTP requests, an interceptor reads a token from a cookie, by default `XSRF-TOKEN`, and sets it as an HTTP header, `X-XSRF-TOKEN`. Since only code that runs on your domain could read the cookie, the backend can be certain that the HTTP request came from your client application and not an attacker.
|
||||
|
||||
By default, an interceptor sends this cookie on all mutating requests (POST, etc.)
|
||||
to relative URLs but not on GET/HEAD requests or
|
||||
By default, an interceptor sends this cookie on all mutating requests (POST, etc.)
|
||||
to relative URLs but not on GET/HEAD requests or
|
||||
on requests with an absolute URL.
|
||||
|
||||
To take advantage of this, your server needs to set a token in a JavaScript readable session cookie called `XSRF-TOKEN` on either the page load or the first GET request. On subsequent requests the server can verify that the cookie matches the `X-XSRF-TOKEN` HTTP header, and therefore be sure that only code running on your domain could have sent the request. The token must be unique for each user and must be verifiable by the server; this prevents the client from making up its own tokens. Set the token to a digest of your site's authentication
|
||||
To take advantage of this, your server needs to set a token in a JavaScript readable session cookie called `XSRF-TOKEN` on either the page load or the first GET request. On subsequent requests the server can verify that the cookie matches the `X-XSRF-TOKEN` HTTP header, and therefore be sure that only code running on your domain could have sent the request. The token must be unique for each user and must be verifiable by the server; this prevents the client from making up its own tokens. Set the token to a digest of your site's authentication
|
||||
cookie with a salt for added security.
|
||||
|
||||
In order to prevent collisions in environments where multiple Angular apps share the same domain or subdomain, give each application a unique cookie name.
|
||||
@ -598,21 +598,21 @@ it('expects a GET request', inject([HttpClient, HttpTestingController], (http: H
|
||||
http
|
||||
.get('/data')
|
||||
.subscribe(data => expect(data['name']).toEqual('Test Data'));
|
||||
|
||||
|
||||
// At this point, the request is pending, and no response has been
|
||||
// sent. The next step is to expect that the request happened.
|
||||
const req = httpMock.expectOne('/data');
|
||||
|
||||
|
||||
// If no request with that URL was made, or if multiple requests match,
|
||||
// expectOne() would throw. However this test makes only one request to
|
||||
// this URL, so it will match and return a mock request. The mock request
|
||||
// can be used to deliver a response or make assertions against the
|
||||
// request. In this case, the test asserts that the request is a GET.
|
||||
expect(req.request.method).toEqual('GET');
|
||||
|
||||
|
||||
// Next, fulfill the request by transmitting a response.
|
||||
req.flush({name: 'Test Data'});
|
||||
|
||||
|
||||
// Finally, assert that there are no outstanding requests.
|
||||
httpMock.verify();
|
||||
}));
|
||||
|
176
aio/content/guide/language-service.md
Normal file
@ -0,0 +1,176 @@
|
||||
# Angular Language Service
|
||||
|
||||
The Angular Language Service is a way to get completions, errors,
|
||||
hints, and navigation inside your Angular templates whether they
|
||||
are external in an HTML file or embedded in annotations/decorators
|
||||
in a string. The Angular Language Service autodetects that you are
|
||||
opening an Angular file, reads your `tsconfig.json` file, finds all the
|
||||
templates you have in your application, and then provides language
|
||||
services for any templates that you open.
|
||||
|
||||
|
||||
## Autocompletion
|
||||
|
||||
Autocompletion can speed up your development time by providing you with
|
||||
contextual possibilities and hints as you type. This example shows
|
||||
autocomplete in an interpolation. As you type it out,
|
||||
you can hit tab to complete.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/language-service/language-completion.gif" alt="autocompletion">
|
||||
</figure>
|
||||
|
||||
There are also completions within
|
||||
elements. Any elements you have as a component selector will
|
||||
show up in the completion list.
|
||||
|
||||
## Error checking
|
||||
|
||||
The Angular Language Service can also forewarn you of mistakes in your code.
|
||||
In this example, Angular doesn't know what `orders` is or where it comes from.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/language-service/language-error.gif" alt="error checking">
|
||||
</figure>
|
||||
|
||||
## Navigation
|
||||
|
||||
Navigation allows you to hover to
|
||||
see where a component, directive, module, etc. is from and then
|
||||
click and press F12 to go directly to its definition.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/language-service/language-navigation.gif" alt="navigation">
|
||||
</figure>
|
||||
|
||||
|
||||
## Angular Language Service in your editor
|
||||
|
||||
Angular Language Service is currently available for [Visual Studio Code](https://code.visualstudio.com/) and
|
||||
[WebStorm](https://www.jetbrains.com/webstorm).
|
||||
|
||||
### Visual Studio Code
|
||||
|
||||
In Visual Studio Code, install Angular Language Service from the store,
|
||||
which is accessible from the bottom icon on the left menu pane.
|
||||
You can also use the VS Quick Open (⌘+P) to search for the extension. When you've opened it,
|
||||
enter the following command:
|
||||
|
||||
```sh
|
||||
ext install ng-template
|
||||
```
|
||||
|
||||
Then click the install button to install the Angular Language Service.
|
||||
|
||||
|
||||
### WebStorm
|
||||
|
||||
In webstorm, you have to install the language service as a dev dependency.
|
||||
When Angular sees this dev dependency, it provides the
|
||||
language service inside of WebStorm. Webstorm then gives you
|
||||
colorization inside the template and autocomplete in addition to the Angular Language Service.
|
||||
|
||||
Here's the dev dependency
|
||||
you need to have in `package.json`:
|
||||
|
||||
```json
|
||||
|
||||
devDependencies {
|
||||
"@angular/language-service": "^4.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
Then in the terminal window at the root of your project,
|
||||
install the `devDependencies` with `npm` or `yarn`:
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
*OR*
|
||||
|
||||
```sh
|
||||
yarn
|
||||
```
|
||||
|
||||
*OR*
|
||||
|
||||
```sh
|
||||
yarn install
|
||||
```
|
||||
|
||||
|
||||
### Sublime Text
|
||||
|
||||
In [Sublime Text](https://www.sublimetext.com/), you first need an extension to allow Typescript.
|
||||
Install the latest version of typescript in a local `node_modules` directory:
|
||||
|
||||
```sh
|
||||
npm install --save-dev typescript
|
||||
```
|
||||
|
||||
Then install the Angular Language Service in the same location:
|
||||
```sh
|
||||
npm install --save-dev @angular/language-service
|
||||
```
|
||||
|
||||
Starting with TypeScript 2.3, TypeScript has a language service plugin model that the language service can use.
|
||||
|
||||
Next, in your user preferences (`Cmd+,` or `Ctrl+,`), add:
|
||||
|
||||
```json
|
||||
"typescript-tsdk": "<path to your folder>/node_modules/typescript/lib"
|
||||
```
|
||||
|
||||
|
||||
## Installing in your project
|
||||
|
||||
You can also install Angular Language Service in your project with the
|
||||
following `npm` command:
|
||||
|
||||
```sh
|
||||
npm install --save-dev @angular/language-service
|
||||
```
|
||||
Additionally, add the following to the `"compilerOptions"` section of
|
||||
your project's `tsconfig.json`.
|
||||
|
||||
```json
|
||||
"plugins": [
|
||||
{"name": "@angular/language-service"}
|
||||
]
|
||||
```
|
||||
Note that this only provides diagnostics and completions in `.ts`
|
||||
files. You need a custom sublime plugin (or modifications to the current plugin)
|
||||
for completions in HTML files.
|
||||
|
||||
|
||||
## How the Language Service works
|
||||
|
||||
When you use an editor with a language service, there's an
|
||||
editor process which starts a separate language process/service
|
||||
to which it speaks through an [RPC](https://en.wikipedia.org/wiki/Remote_procedure_call).
|
||||
Any time you type inside of the editor, it sends information to the other process to
|
||||
track the state of your project. When you trigger a completion list within a template, the editor process first parses the template into an HTML AST, or [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree). Then the Angular compiler interprets
|
||||
what module the template is part of, the scope you're in, and the component selector. Then it figures out where in the template AST your cursor is. When it determines the
|
||||
context, it can then determine what the children can be.
|
||||
|
||||
It's a little more involved if you are in an interpolation. If you have an interpolation of `{{data.---}}` inside a `div` and need the completion list after `data.---`, the compiler can't use the HTML AST to find the answer. The HTML AST can only tell the compiler that there is some text with the characters "`{{data.---}}`". That's when the template parser produces an expression AST, which resides within the template AST. The Angular Language Services then looks at `data.---` within its context and asks the TypeScript Language Service what the members of data are. TypeScript then returns the list of possibilities.
|
||||
|
||||
|
||||
For more in-depth information, see the
|
||||
[Angular Language Service API](https://github.com/angular/angular/blob/master/packages/language-service/src/types.ts)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
## More on Information
|
||||
|
||||
For more information, see [Chuck Jazdzewski's presentation](https://www.youtube.com/watch?v=ez3R0Gi4z5A&t=368s) on the Angular Language
|
||||
Service from [ng-conf](https://www.ng-conf.org/) 2017.
|
||||
|
||||
|
@ -615,7 +615,7 @@ Once the application begins, the app root injector is closed to new providers.
|
||||
|
||||
Time passes and application logic triggers lazy loading of a module.
|
||||
Angular must add the lazy-loaded module's providers to an injector somewhere.
|
||||
It can't add them to the app root injector because that injector is closed to new providers.
|
||||
It can't added them to the app root injector because that injector is closed to new providers.
|
||||
So Angular creates a new child injector for the lazy-loaded module context.
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 89 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 77 KiB |
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 135 KiB |
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 119 KiB |
Before Width: | Height: | Size: 200 KiB After Width: | Height: | Size: 320 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 132 KiB |
BIN
aio/content/images/guide/language-service/language-error.gif
Normal file
After Width: | Height: | Size: 274 KiB |
After Width: | Height: | Size: 857 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 8.8 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 7.8 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 6.9 KiB |
@ -573,14 +573,5 @@
|
||||
"website": "http://www.methotic.com",
|
||||
"bio": "Thierry is a senior consultant and trainer, specialized on Angular, and a Google Developer Expert.",
|
||||
"group": "GDE"
|
||||
},
|
||||
|
||||
"gerardsans": {
|
||||
"name": "Gerard Sans",
|
||||
"picture": "gerardsans.jpg",
|
||||
"twitter": "gerardsans",
|
||||
"website": "https://medium.com/@gerard.sans",
|
||||
"bio": "Gerard is very excited about the future of the Web and JavaScript. Always happy Computer Science Engineer and humble Google Developer Expert. He loves to share his learnings by giving talks, trainings and writing about cool technologies. He loves running AngularZone and GraphQL London, mentoring students and giving back to the community.",
|
||||
"group": "GDE"
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
"title": "Events"
|
||||
},
|
||||
{
|
||||
"url": "https://blog.angular.io/",
|
||||
"url": "https://blog.angularjs.org/",
|
||||
"title": "Blog"
|
||||
}
|
||||
],
|
||||
@ -39,7 +39,7 @@
|
||||
"title": "Events"
|
||||
},
|
||||
{
|
||||
"url": "https://blog.angular.io/",
|
||||
"url": "https://blog.angularjs.org/",
|
||||
"title": "Blog"
|
||||
}
|
||||
]
|
||||
@ -282,6 +282,11 @@
|
||||
"title": "Internationalization (i18n)",
|
||||
"tooltip": "Translate the app's template text into multiple languages."
|
||||
},
|
||||
{
|
||||
"url": "guide/language-service",
|
||||
"title": "Language Service",
|
||||
"tooltip": "Use Angular Language Service to speed up dev time."
|
||||
},
|
||||
{
|
||||
"url": "guide/security",
|
||||
"title": "Security",
|
||||
|
@ -137,7 +137,7 @@ Create a file in the `app` folder called `hero.service.ts`.
|
||||
|
||||
|
||||
The naming convention for service files is the service name in lowercase followed by `.service`.
|
||||
For a multi-word service name, use lower [dash-case](guide/glossary#dash-case).
|
||||
For a multi-word service name, use lower [dash-case](guide/glossary).
|
||||
For example, the filename for `SpecialSuperHeroService` is `special-super-hero.service.ts`.
|
||||
|
||||
</div>
|
||||
|
@ -83,7 +83,7 @@ Added hero "Zero" to confirm that the data service can handle a hero with `id==0
|
||||
Don't worry about the details of this backend substitution; you can
|
||||
skip it when you have a real web API server.
|
||||
|
||||
</div>
|
||||
div>
|
||||
|
||||
## Heroes and HTTP
|
||||
|
||||
|
@ -4,8 +4,12 @@ set -eu -o pipefail
|
||||
|
||||
readonly thisDir=$(cd $(dirname $0); pwd)
|
||||
readonly parentDir=$(dirname $thisDir)
|
||||
readonly TOKEN=${ANGULAR_PAYLOAD_FIREBASE_TOKEN:-}
|
||||
readonly PROJECT_NAME="angular-payload-size"
|
||||
|
||||
# temporarily turn on debugging - we disable it later in the script to prevent token leak
|
||||
set -x
|
||||
|
||||
source ${thisDir}/_payload-limits.sh
|
||||
|
||||
failed=false
|
||||
@ -41,21 +45,20 @@ timestamp=$(date +%s)
|
||||
payloadData="$payloadData\"timestamp\": $timestamp, "
|
||||
|
||||
# Add change source: application, dependencies, or 'application+dependencies'
|
||||
applicationChanged=false
|
||||
dependenciesChanged=false
|
||||
if [[ $(git diff --name-only $TRAVIS_COMMIT_RANGE $parentDir | grep -v aio/yarn.lock | grep -v content) ]]; then
|
||||
applicationChanged=true
|
||||
fi
|
||||
if [[ $(git diff --name-only $TRAVIS_COMMIT_RANGE $parentDir/yarn.lock) ]]; then
|
||||
dependenciesChanged=true
|
||||
yarnChanged=false
|
||||
allChangedFiles=$(git diff --name-only $TRAVIS_COMMIT_RANGE $parentDir | wc -l)
|
||||
allChangedFileNames=$(git diff --name-only $TRAVIS_COMMIT_RANGE $parentDir)
|
||||
|
||||
if [[ $allChangedFileNames == *"yarn.lock"* ]]; then
|
||||
yarnChanged=true
|
||||
fi
|
||||
|
||||
if $dependenciesChanged && $applicationChanged; then
|
||||
change='application+dependencies'
|
||||
elif $dependenciesChanged; then
|
||||
if [[ $allChangedFiles -eq 1 ]] && [[ "$yarnChanged" = true ]]; then
|
||||
# only yarn.lock changed
|
||||
change='dependencies'
|
||||
elif $applicationChanged; then
|
||||
elif [[ $allChangedFiles -gt 1 ]] && [[ "$yarnChanged" = true ]]; then
|
||||
change='application+dependencies'
|
||||
elif [[ $allChangedFiles -gt 0 ]]; then
|
||||
change='application'
|
||||
else
|
||||
# Nothing changed in aio/
|
||||
@ -73,7 +76,7 @@ if [[ "$TRAVIS_PULL_REQUEST" == "false" ]]; then
|
||||
|
||||
# WARNING: FIREBASE_TOKEN should NOT be printed.
|
||||
set +x
|
||||
firebase database:update --data "$payloadData" --project $PROJECT_NAME --confirm --token "$ANGULAR_PAYLOAD_FIREBASE_TOKEN" $dbPath
|
||||
firebase database:update --data "$payloadData" --project $PROJECT_NAME --confirm --token "$TOKEN" $dbPath
|
||||
fi
|
||||
|
||||
if [[ $failed = true ]]; then
|
||||
|
@ -19,6 +19,11 @@ const config = require('lighthouse/lighthouse-core/config/default.js');
|
||||
// Constants
|
||||
const VIEWER_URL = 'https://googlechrome.github.io/lighthouse/viewer/';
|
||||
|
||||
// Specify the path to Chrome on Travis
|
||||
if (process.env.TRAVIS) {
|
||||
process.env.LIGHTHOUSE_CHROMIUM_PATH = process.env.CHROME_BIN;
|
||||
}
|
||||
|
||||
// Run
|
||||
_main(process.argv.slice(2));
|
||||
|
||||
|
@ -632,6 +632,7 @@ describe('AppComponent', () => {
|
||||
it('should initialize the search worker', inject([SearchService], (searchService: SearchService) => {
|
||||
fixture.detectChanges(); // triggers ngOnInit
|
||||
expect(searchService.initWorker).toHaveBeenCalled();
|
||||
expect(searchService.loadIndex).toHaveBeenCalled();
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -111,8 +111,8 @@ export class AppComponent implements OnInit {
|
||||
ngOnInit() {
|
||||
// Do not initialize the search on browsers that lack web worker support
|
||||
if ('Worker' in window) {
|
||||
// Delay initialization by up to 2 seconds
|
||||
this.searchService.initWorker('app/search/search-worker.js', 2000);
|
||||
this.searchService.initWorker('app/search/search-worker.js');
|
||||
this.searchService.loadIndex();
|
||||
}
|
||||
|
||||
this.onResize(window.innerWidth);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ComponentFixture, fakeAsync, inject, TestBed, tick } from '@angular/core/testing';
|
||||
import { async, ComponentFixture, TestBed, inject } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { SearchBoxComponent } from './search-box.component';
|
||||
import { MockSearchService } from 'testing/search.service';
|
||||
@ -36,67 +36,30 @@ describe('SearchBoxComponent', () => {
|
||||
});
|
||||
|
||||
describe('initialisation', () => {
|
||||
it('should get the current search query from the location service',
|
||||
inject([LocationService], (location: MockLocationService) => fakeAsync(() => {
|
||||
it('should get the current search query from the location service', inject([LocationService], (location: MockLocationService) => {
|
||||
location.search.and.returnValue({ search: 'initial search' });
|
||||
component.ngOnInit();
|
||||
expect(location.search).toHaveBeenCalled();
|
||||
tick(300);
|
||||
expect(host.searchHandler).toHaveBeenCalledWith('initial search');
|
||||
expect(component.searchBox.nativeElement.value).toEqual('initial search');
|
||||
})));
|
||||
});
|
||||
|
||||
describe('onSearch', () => {
|
||||
it('should debounce by 300ms', fakeAsync(() => {
|
||||
component.doSearch();
|
||||
expect(host.searchHandler).not.toHaveBeenCalled();
|
||||
tick(300);
|
||||
expect(host.searchHandler).toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it('should pass through the value of the input box', fakeAsync(() => {
|
||||
const input = fixture.debugElement.query(By.css('input'));
|
||||
input.nativeElement.value = 'some query (input)';
|
||||
component.doSearch();
|
||||
tick(300);
|
||||
expect(host.searchHandler).toHaveBeenCalledWith('some query (input)');
|
||||
}));
|
||||
|
||||
it('should only send events if the search value has changed', fakeAsync(() => {
|
||||
const input = fixture.debugElement.query(By.css('input'));
|
||||
|
||||
input.nativeElement.value = 'some query';
|
||||
component.doSearch();
|
||||
tick(300);
|
||||
expect(host.searchHandler).toHaveBeenCalledTimes(1);
|
||||
|
||||
component.doSearch();
|
||||
tick(300);
|
||||
expect(host.searchHandler).toHaveBeenCalledTimes(1);
|
||||
|
||||
input.nativeElement.value = 'some other query';
|
||||
component.doSearch();
|
||||
tick(300);
|
||||
expect(host.searchHandler).toHaveBeenCalledTimes(2);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('on input', () => {
|
||||
it('should trigger a search', () => {
|
||||
it('should trigger the onSearch event', () => {
|
||||
const input = fixture.debugElement.query(By.css('input'));
|
||||
spyOn(component, 'doSearch');
|
||||
input.nativeElement.value = 'some query (input)';
|
||||
input.triggerEventHandler('input', { });
|
||||
expect(component.doSearch).toHaveBeenCalled();
|
||||
expect(host.searchHandler).toHaveBeenCalledWith('some query (input)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('on keyup', () => {
|
||||
it('should trigger a search', () => {
|
||||
it('should trigger the onSearch event', () => {
|
||||
const input = fixture.debugElement.query(By.css('input'));
|
||||
spyOn(component, 'doSearch');
|
||||
input.nativeElement.value = 'some query (keyup)';
|
||||
input.triggerEventHandler('keyup', { });
|
||||
expect(component.doSearch).toHaveBeenCalled();
|
||||
expect(host.searchHandler).toHaveBeenCalledWith('some query (keyup)');
|
||||
});
|
||||
});
|
||||
|
||||
@ -110,11 +73,28 @@ describe('SearchBoxComponent', () => {
|
||||
});
|
||||
|
||||
describe('on click', () => {
|
||||
it('should trigger a search', () => {
|
||||
it('should trigger the search event', () => {
|
||||
const input = fixture.debugElement.query(By.css('input'));
|
||||
spyOn(component, 'doSearch');
|
||||
input.nativeElement.value = 'some query (click)';
|
||||
input.triggerEventHandler('click', { });
|
||||
expect(component.doSearch).toHaveBeenCalled();
|
||||
expect(host.searchHandler).toHaveBeenCalledWith('some query (click)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('event filtering', () => {
|
||||
it('should only send events if the search value has changed', () => {
|
||||
const input = fixture.debugElement.query(By.css('input'));
|
||||
|
||||
input.nativeElement.value = 'some query';
|
||||
input.triggerEventHandler('input', { });
|
||||
expect(host.searchHandler).toHaveBeenCalledTimes(1);
|
||||
|
||||
input.triggerEventHandler('input', { });
|
||||
expect(host.searchHandler).toHaveBeenCalledTimes(1);
|
||||
|
||||
input.nativeElement.value = 'some other query';
|
||||
input.triggerEventHandler('input', { });
|
||||
expect(host.searchHandler).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -26,11 +26,10 @@ import 'rxjs/add/operator/distinctUntilChanged';
|
||||
})
|
||||
export class SearchBoxComponent implements OnInit {
|
||||
|
||||
private searchDebounce = 300;
|
||||
private searchSubject = new Subject<string>();
|
||||
|
||||
@ViewChild('searchBox') searchBox: ElementRef;
|
||||
@Output() onSearch = this.searchSubject.distinctUntilChanged().debounceTime(this.searchDebounce);
|
||||
@Output() onSearch = this.searchSubject.distinctUntilChanged();
|
||||
@Output() onFocus = new EventEmitter<string>();
|
||||
|
||||
constructor(private locationService: LocationService) { }
|
||||
|
@ -1,62 +1,24 @@
|
||||
import { ReflectiveInjector, NgZone } from '@angular/core';
|
||||
import { fakeAsync, tick } from '@angular/core/testing';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { SearchService } from './search.service';
|
||||
import { WebWorkerClient } from 'app/shared/web-worker';
|
||||
|
||||
describe('SearchService', () => {
|
||||
|
||||
let injector: ReflectiveInjector;
|
||||
let service: SearchService;
|
||||
let sendMessageSpy: jasmine.Spy;
|
||||
let mockWorker: WebWorkerClient;
|
||||
|
||||
beforeEach(() => {
|
||||
sendMessageSpy = jasmine.createSpy('sendMessage').and.returnValue(Observable.of({}));
|
||||
mockWorker = { sendMessage: sendMessageSpy } as any;
|
||||
spyOn(WebWorkerClient, 'create').and.returnValue(mockWorker);
|
||||
|
||||
injector = ReflectiveInjector.resolveAndCreate([
|
||||
SearchService,
|
||||
{ provide: NgZone, useFactory: () => new NgZone({ enableLongStackTrace: false }) }
|
||||
]);
|
||||
service = injector.get(SearchService);
|
||||
});
|
||||
|
||||
describe('initWorker', () => {
|
||||
it('should create the worker and load the index after the specified delay', fakeAsync(() => {
|
||||
service.initWorker('some/url', 100);
|
||||
expect(WebWorkerClient.create).not.toHaveBeenCalled();
|
||||
expect(mockWorker.sendMessage).not.toHaveBeenCalled();
|
||||
tick(100);
|
||||
expect(WebWorkerClient.create).toHaveBeenCalledWith('some/url', jasmine.any(NgZone));
|
||||
expect(mockWorker.sendMessage).toHaveBeenCalledWith('load-index');
|
||||
}));
|
||||
describe('loadIndex', () => {
|
||||
it('should send a "load-index" message to the worker');
|
||||
it('should connect the `ready` property to the response to the "load-index" message');
|
||||
});
|
||||
|
||||
describe('search', () => {
|
||||
beforeEach(() => {
|
||||
// We must initialize the service before calling search
|
||||
service.initWorker('some/url', 100);
|
||||
});
|
||||
|
||||
it('should trigger a `loadIndex` synchronously', () => {
|
||||
service.search('some query');
|
||||
expect(mockWorker.sendMessage).toHaveBeenCalledWith('load-index');
|
||||
});
|
||||
|
||||
it('should send a "query-index" message to the worker', () => {
|
||||
service.search('some query');
|
||||
expect(mockWorker.sendMessage).toHaveBeenCalledWith('query-index', 'some query');
|
||||
});
|
||||
|
||||
it('should push the response to the `searchResults` observable', () => {
|
||||
const mockSearchResults = { results: ['a', 'b'] };
|
||||
(mockWorker.sendMessage as jasmine.Spy).and.returnValue(Observable.of(mockSearchResults));
|
||||
let searchResults: any;
|
||||
service.searchResults.subscribe(results => searchResults = results);
|
||||
service.search('some query');
|
||||
expect(searchResults).toEqual(mockSearchResults);
|
||||
});
|
||||
it('should send a "query-index" message to the worker');
|
||||
it('should push the response to the `searchResults` observable');
|
||||
});
|
||||
});
|
||||
|
@ -7,10 +7,8 @@ can be found in the LICENSE file at http://angular.io/license
|
||||
import { NgZone, Injectable, Type } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { ReplaySubject } from 'rxjs/ReplaySubject';
|
||||
import 'rxjs/add/observable/race';
|
||||
import 'rxjs/add/observable/timer';
|
||||
import 'rxjs/add/operator/publishLast';
|
||||
import 'rxjs/add/operator/concatMap';
|
||||
import 'rxjs/add/operator/publish';
|
||||
import { WebWorkerClient } from 'app/shared/web-worker';
|
||||
|
||||
export interface SearchResults {
|
||||
@ -29,50 +27,26 @@ export interface SearchResult {
|
||||
|
||||
@Injectable()
|
||||
export class SearchService {
|
||||
private searchesSubject = new ReplaySubject<string>(1);
|
||||
searchResults: Observable<SearchResults>;
|
||||
private worker: WebWorkerClient;
|
||||
private ready: Observable<boolean>;
|
||||
private resultsSubject = new ReplaySubject<SearchResults>(1);
|
||||
readonly searchResults = this.resultsSubject.asObservable();
|
||||
|
||||
constructor(private zone: NgZone) {}
|
||||
|
||||
/**
|
||||
* Initialize the search engine. We offer an `initDelay` to prevent the search initialisation from delaying the
|
||||
* initial rendering of the web page. Triggering a search will override this delay and cause the index to be
|
||||
* loaded immediately.
|
||||
*
|
||||
* @param workerUrl the url of the WebWorker script that runs the searches
|
||||
* @param initDelay the number of milliseconds to wait before we load the WebWorker and generate the search index
|
||||
*/
|
||||
initWorker(workerUrl: string, initDelay: number) {
|
||||
const searchResults = Observable
|
||||
// Wait for the initDelay or the first search
|
||||
.race(
|
||||
Observable.timer(initDelay),
|
||||
this.searchesSubject.first()
|
||||
)
|
||||
.concatMap(() => {
|
||||
// Create the worker and load the index
|
||||
const worker = WebWorkerClient.create(workerUrl, this.zone);
|
||||
return worker.sendMessage('load-index').concatMap(() =>
|
||||
// Once the index has loaded, switch to listening to the searches coming in
|
||||
this.searchesSubject.switchMap((query) =>
|
||||
// Each search gets switched to a web worker message, whose results are returned via an observable
|
||||
worker.sendMessage<SearchResults>('query-index', query)
|
||||
)
|
||||
);
|
||||
}).publish();
|
||||
|
||||
// Connect to the observable to kick off the timer
|
||||
searchResults.connect();
|
||||
|
||||
// Expose the connected observable to the rest of the world
|
||||
this.searchResults = searchResults;
|
||||
initWorker(workerUrl) {
|
||||
this.worker = new WebWorkerClient(new Worker(workerUrl), this.zone);
|
||||
}
|
||||
|
||||
loadIndex() {
|
||||
const ready = this.ready = this.worker.sendMessage<boolean>('load-index').publishLast();
|
||||
// trigger the index to be loaded immediately
|
||||
ready.connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a search query to the index.
|
||||
* The results will appear on the `searchResults` observable.
|
||||
*/
|
||||
search(query: string) {
|
||||
this.searchesSubject.next(query);
|
||||
this.ready.concatMap(ready => {
|
||||
return this.worker.sendMessage('query-index', query) as Observable<SearchResults>;
|
||||
}).subscribe(results => this.resultsSubject.next(results));
|
||||
}
|
||||
}
|
||||
|
@ -16,11 +16,7 @@ export interface WebWorkerMessage {
|
||||
export class WebWorkerClient {
|
||||
private nextId = 0;
|
||||
|
||||
static create(workerUrl: string, zone: NgZone) {
|
||||
return new WebWorkerClient(new Worker(workerUrl), zone);
|
||||
}
|
||||
|
||||
private constructor(private worker: Worker, private zone: NgZone) {
|
||||
constructor(private worker: Worker, private zone: NgZone) {
|
||||
}
|
||||
|
||||
sendMessage<T>(type: string, payload?: any): Observable<T> {
|
||||
|
Before Width: | Height: | Size: 178 KiB After Width: | Height: | Size: 187 KiB |
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 67 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.8 KiB |
@ -1,19 +0,0 @@
|
||||
# Docs releases
|
||||
|
||||
This document explains how to update the documentation examples after an Angular release. This is only needed for major and minor versions.
|
||||
|
||||
All the packages for the docs' examples are specified in `/aio/tools/examples/shared/package.json`
|
||||
|
||||
**1)** So within the `shared` folder, you need to issue the following command:
|
||||
|
||||
```
|
||||
$ yarn upgrade-interactive --tilde
|
||||
```
|
||||
|
||||
There, select all the packages that are updated on the new Angular release.
|
||||
|
||||
**2)** Changes to the tsconfig.json? There is one to update at `/aio/tools/examples/shared/boilerplate/src/tsconfig.json`
|
||||
|
||||
**3)** The file `/aio/tools/examples/shared/boilerplate/src/systemjs.config.web.js` contains the configuration for plunkers. It has some hardcoded versions that could be updated.
|
||||
|
||||
**4)** As in step 3, more hardcoded versions at `/aio/tools/plunker-builder/translator/rules/indexHtml.js`
|
@ -12,19 +12,19 @@
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@angular/animations": "~4.3.1",
|
||||
"@angular/common": "~4.3.1",
|
||||
"@angular/compiler": "~4.3.1",
|
||||
"@angular/compiler-cli": "~4.3.1",
|
||||
"@angular/core": "~4.3.1",
|
||||
"@angular/forms": "~4.3.1",
|
||||
"@angular/http": "~4.3.1",
|
||||
"@angular/platform-browser": "~4.3.1",
|
||||
"@angular/platform-browser-dynamic": "~4.3.1",
|
||||
"@angular/platform-server": "~4.3.1",
|
||||
"@angular/router": "~4.3.1",
|
||||
"@angular/tsc-wrapped": "~4.3.1",
|
||||
"@angular/upgrade": "~4.3.1",
|
||||
"@angular/animations": "~4.2.0",
|
||||
"@angular/common": "~4.2.0",
|
||||
"@angular/compiler": "~4.2.0",
|
||||
"@angular/compiler-cli": "~4.2.0",
|
||||
"@angular/core": "~4.2.0",
|
||||
"@angular/forms": "~4.2.0",
|
||||
"@angular/http": "~4.2.0",
|
||||
"@angular/platform-browser": "~4.2.0",
|
||||
"@angular/platform-browser-dynamic": "~4.2.0",
|
||||
"@angular/platform-server": "~4.2.0",
|
||||
"@angular/router": "~4.2.0",
|
||||
"@angular/tsc-wrapped": "~4.2.0",
|
||||
"@angular/upgrade": "~4.2.0",
|
||||
"angular-in-memory-web-api": "~0.3.2",
|
||||
"core-js": "^2.4.1",
|
||||
"rxjs": "^5.1.0",
|
||||
|
@ -20,7 +20,11 @@ exports.config = {
|
||||
|
||||
// Capabilities to be passed to the webdriver instance.
|
||||
capabilities: {
|
||||
'browserName': 'chrome'
|
||||
'browserName': 'chrome',
|
||||
// For Travis
|
||||
chromeOptions: {
|
||||
binary: process.env.CHROME_BIN
|
||||
}
|
||||
},
|
||||
|
||||
// Framework to use. Jasmine is recommended.
|
||||
|
@ -1,12 +1,10 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
# yarn v0.25.3
|
||||
# node v7.8.0
|
||||
|
||||
|
||||
"@angular/animations@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-4.3.1.tgz#1f7e0bb803efc21c608246e6765a1c647f3d1a5f"
|
||||
"@angular/animations@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-4.2.0.tgz#e964fc56c9621f28679f24d5e69026e2d1571425"
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
|
||||
@ -76,79 +74,79 @@
|
||||
optionalDependencies:
|
||||
node-sass "^4.3.0"
|
||||
|
||||
"@angular/common@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/common/-/common-4.3.1.tgz#260f487a7cdca326c436bd3ea9515c797de2ff72"
|
||||
"@angular/common@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/common/-/common-4.2.0.tgz#5df34718bcefc49918bfcb2683f6c19720b66a61"
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
|
||||
"@angular/compiler-cli@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-4.3.1.tgz#00b41afb6faeb4aef561b8427804ac8880aff63c"
|
||||
"@angular/compiler-cli@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-4.2.0.tgz#bd6f6b71f003df48a8f86184a8c16533afdf23ec"
|
||||
dependencies:
|
||||
"@angular/tsc-wrapped" "4.3.1"
|
||||
"@angular/tsc-wrapped" "4.2.0"
|
||||
minimist "^1.2.0"
|
||||
reflect-metadata "^0.1.2"
|
||||
|
||||
"@angular/compiler@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-4.3.1.tgz#3a24d49ecf01ac2b6e07f63e378b8ff8e257fe09"
|
||||
"@angular/compiler@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-4.2.0.tgz#a21df81995b210f822ffd70b57247d474876fbed"
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
|
||||
"@angular/core@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/core/-/core-4.3.1.tgz#a9d0a7d644b96260674269b689a04feea632a8d3"
|
||||
"@angular/core@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/core/-/core-4.2.0.tgz#8bf57d01379c2a9e29476ad569dec9e20d5b17dc"
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
|
||||
"@angular/forms@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-4.3.1.tgz#33914da2cb146430ff901471e682c76654622dfe"
|
||||
"@angular/forms@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-4.2.0.tgz#cb3ae69172e254452fa77578605ebc1bb72138c9"
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
|
||||
"@angular/http@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/http/-/http-4.3.1.tgz#e4f661f746711e88ecbea76a3c905babf97d315a"
|
||||
"@angular/http@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/http/-/http-4.2.0.tgz#484af53639e04a68834c5167a1955d2d0cde8e1c"
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
|
||||
"@angular/platform-browser-dynamic@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.3.1.tgz#84034da60a82ef36e7effda7b3ade6e645b330b3"
|
||||
"@angular/platform-browser-dynamic@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.2.0.tgz#b84c05616bd824e15b52b2b85c47b58b25d0158a"
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
|
||||
"@angular/platform-browser@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-4.3.1.tgz#db727b06eed64bda5defec71815db26a4da2f690"
|
||||
"@angular/platform-browser@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-4.2.0.tgz#dfd782e7ebacba1bbe2ae0556d5d7fb012f2a6cd"
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
|
||||
"@angular/platform-server@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-server/-/platform-server-4.3.1.tgz#3b915fc4013c6a947a8c147b4db0279b025936eb"
|
||||
"@angular/platform-server@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-server/-/platform-server-4.2.0.tgz#90add7fcd9c4f568a31058c9d93a9aca09eb4c58"
|
||||
dependencies:
|
||||
parse5 "^3.0.1"
|
||||
tslib "^1.7.1"
|
||||
xhr2 "^0.1.4"
|
||||
|
||||
"@angular/router@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/router/-/router-4.3.1.tgz#5219d44526156d816065841127610165a015b450"
|
||||
"@angular/router@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/router/-/router-4.2.0.tgz#7cda9a23621ee41b466eced8bb4cbb62237ba6b9"
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
|
||||
"@angular/tsc-wrapped@4.3.1", "@angular/tsc-wrapped@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/tsc-wrapped/-/tsc-wrapped-4.3.1.tgz#f6616a4d2a3bbec1cded664fd1f526edce99ef41"
|
||||
"@angular/tsc-wrapped@4.2.0", "@angular/tsc-wrapped@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/tsc-wrapped/-/tsc-wrapped-4.2.0.tgz#e62ce9953c27ba96e4c6daf117f10e31169ccea2"
|
||||
dependencies:
|
||||
tsickle "^0.21.0"
|
||||
|
||||
"@angular/upgrade@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/upgrade/-/upgrade-4.3.1.tgz#db80f9d428e4801b0a41618df71bf899882f39c8"
|
||||
"@angular/upgrade@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/upgrade/-/upgrade-4.2.0.tgz#2766c8a8bb507146b6d38bed2c379f77bbc70335"
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
|
||||
|
@ -21,7 +21,7 @@ you run the first build.
|
||||
|
||||
The `WORKSPACE` file indicates that our root directory is a
|
||||
Bazel project. It contains the version of the Bazel rules we
|
||||
use to execute build steps, from `build_bazel_rules_typescript`.
|
||||
use to execute build steps, from `io_bazel_rules_typescript`.
|
||||
The sources on [GitHub] are published from Google's internal
|
||||
repository (google3).
|
||||
|
||||
@ -29,7 +29,7 @@ That repository defines dependencies on specific versions of
|
||||
all the tools. You can run the tools Bazel installed, for
|
||||
example rather than `npm install` (which depends on whatever
|
||||
version you have installed on your machine), you can
|
||||
`bazel run @build_bazel_rules_typescript_node//:bin/npm install`.
|
||||
`bazel run @io_bazel_rules_typescript_node//:bin/npm install`.
|
||||
|
||||
Bazel accepts a lot of options. We check in some options in the
|
||||
`.bazelrc` file. See the [bazelrc doc]. For example, if you don't
|
||||
|
@ -3,7 +3,7 @@
|
||||
Please see [Using git with Angular repositories](https://docs.google.com/document/d/1h8nijFSaa1jG_UE8v4WP7glh5qOUXnYtAtJh_gwOQHI/edit)
|
||||
for details about how we maintain a linear commit history, and the rules for committing.
|
||||
|
||||
As a contributor, just read the instructions in [CONTRIBUTING.md](../CONTRIBUTING.md) and send a pull request.
|
||||
As a contributor, just read the instructions in [CONTRIBUTING.md](CONTRIBUTING.md) and send a pull request.
|
||||
Someone with committer access will do the rest.
|
||||
|
||||
# Change approvals
|
||||
|
@ -21,7 +21,7 @@
|
||||
"version": "1.10.21-alpha"
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "6.0.84"
|
||||
"version": "4.0.22-alpha"
|
||||
},
|
||||
"@types/q": {
|
||||
"version": "0.0.32"
|
||||
@ -177,9 +177,6 @@
|
||||
"asap": {
|
||||
"version": "2.0.3"
|
||||
},
|
||||
"ascli": {
|
||||
"version": "1.0.1"
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.3"
|
||||
},
|
||||
@ -1482,9 +1479,6 @@
|
||||
"builtin-modules": {
|
||||
"version": "1.1.1"
|
||||
},
|
||||
"bytebuffer": {
|
||||
"version": "5.0.1"
|
||||
},
|
||||
"bytes": {
|
||||
"version": "2.1.0"
|
||||
},
|
||||
@ -1603,9 +1597,6 @@
|
||||
"colors": {
|
||||
"version": "1.1.2"
|
||||
},
|
||||
"colour": {
|
||||
"version": "0.7.1"
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.5"
|
||||
},
|
||||
@ -3509,9 +3500,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"long": {
|
||||
"version": "3.2.0"
|
||||
},
|
||||
"longest": {
|
||||
"version": "1.0.1"
|
||||
},
|
||||
@ -3937,9 +3925,6 @@
|
||||
"options": {
|
||||
"version": "0.0.6"
|
||||
},
|
||||
"optjs": {
|
||||
"version": "3.2.2"
|
||||
},
|
||||
"orchestrator": {
|
||||
"version": "0.3.7",
|
||||
"dependencies": {
|
||||
@ -4095,14 +4080,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"protobufjs": {
|
||||
"version": "5.0.0",
|
||||
"dependencies": {
|
||||
"glob": {
|
||||
"version": "5.0.15"
|
||||
}
|
||||
}
|
||||
},
|
||||
"protractor": {
|
||||
"version": "4.0.14",
|
||||
"dependencies": {
|
||||
|
49
npm-shrinkwrap.json
generated
@ -33,9 +33,9 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-1.10.21-alpha.tgz"
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "6.0.84",
|
||||
"from": "@types/node@6.0.84",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.84.tgz"
|
||||
"version": "4.0.22-alpha",
|
||||
"from": "@types/node@latest",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-4.0.22-alpha.tgz"
|
||||
},
|
||||
"@types/q": {
|
||||
"version": "0.0.32",
|
||||
@ -287,11 +287,6 @@
|
||||
"from": "asap@>=2.0.3 <2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.3.tgz"
|
||||
},
|
||||
"ascli": {
|
||||
"version": "1.0.1",
|
||||
"from": "ascli@>=1.0.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz"
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.3",
|
||||
"from": "asn1@>=0.2.3 <0.3.0",
|
||||
@ -2310,11 +2305,6 @@
|
||||
"from": "builtin-modules@>=1.0.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz"
|
||||
},
|
||||
"bytebuffer": {
|
||||
"version": "5.0.1",
|
||||
"from": "bytebuffer@>=5.0.0 <6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz"
|
||||
},
|
||||
"bytes": {
|
||||
"version": "2.1.0",
|
||||
"from": "bytes@2.1.0",
|
||||
@ -2505,11 +2495,6 @@
|
||||
"from": "colors@>=1.1.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz"
|
||||
},
|
||||
"colour": {
|
||||
"version": "0.7.1",
|
||||
"from": "colour@>=0.7.1 <0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/colour/-/colour-0.7.1.tgz"
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.5",
|
||||
"from": "combined-stream@>=1.0.5 <1.1.0",
|
||||
@ -5591,11 +5576,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"long": {
|
||||
"version": "3.2.0",
|
||||
"from": "long@>=3.0.0 <4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz"
|
||||
},
|
||||
"longest": {
|
||||
"version": "1.0.1",
|
||||
"from": "longest@>=1.0.1 <2.0.0",
|
||||
@ -6275,11 +6255,6 @@
|
||||
"from": "options@>=0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz"
|
||||
},
|
||||
"optjs": {
|
||||
"version": "3.2.2",
|
||||
"from": "optjs@>=3.2.2 <3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/optjs/-/optjs-3.2.2.tgz"
|
||||
},
|
||||
"orchestrator": {
|
||||
"version": "0.3.7",
|
||||
"from": "orchestrator@>=0.3.0 <0.4.0",
|
||||
@ -6533,18 +6508,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"protobufjs": {
|
||||
"version": "5.0.0",
|
||||
"from": "protobufjs@5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-5.0.0.tgz",
|
||||
"dependencies": {
|
||||
"glob": {
|
||||
"version": "5.0.15",
|
||||
"from": "glob@>=5.0.10 <6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"protractor": {
|
||||
"version": "4.0.14",
|
||||
"from": "protractor@4.0.14",
|
||||
@ -7829,9 +7792,9 @@
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz"
|
||||
},
|
||||
"typescript": {
|
||||
"version": "2.3.4",
|
||||
"from": "typescript@2.3.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.3.4.tgz"
|
||||
"version": "2.3.2",
|
||||
"from": "typescript@>=2.3.0 <2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.3.2.tgz"
|
||||
},
|
||||
"ua-parser-js": {
|
||||
"version": "0.7.10",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "4.3.2",
|
||||
"version": "5.0.0-beta.0",
|
||||
"private": true,
|
||||
"branchPattern": "2.0.*",
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
@ -36,7 +36,7 @@
|
||||
"@types/fs-extra": "0.0.22-alpha",
|
||||
"@types/hammerjs": "^2.0.33",
|
||||
"@types/jasmine": "^2.2.22-alpha",
|
||||
"@types/node": "^6.0.84",
|
||||
"@types/node": "^4.0.22-alpha",
|
||||
"@types/selenium-webdriver": "^2.53.35",
|
||||
"@types/systemjs": "^0.19.32",
|
||||
"angular": "^1.5.0",
|
||||
@ -78,7 +78,6 @@
|
||||
"nan": "^2.4.0",
|
||||
"node-uuid": "1.4.x",
|
||||
"parse5": "^3.0.1",
|
||||
"protobufjs": "^5.0.0",
|
||||
"protractor": "^4.0.14",
|
||||
"react": "^0.14.0",
|
||||
"rewire": "^2.3.3",
|
||||
|
@ -24,8 +24,14 @@ export function parseTransitionExpr(
|
||||
function parseInnerTransitionStr(
|
||||
eventStr: string, expressions: TransitionMatcherFn[], errors: string[]) {
|
||||
if (eventStr[0] == ':') {
|
||||
eventStr = parseAnimationAlias(eventStr, errors);
|
||||
const result = parseAnimationAlias(eventStr, errors);
|
||||
if (typeof result == 'function') {
|
||||
expressions.push(result);
|
||||
return;
|
||||
}
|
||||
eventStr = result as string;
|
||||
}
|
||||
|
||||
const match = eventStr.match(/^(\*|[-\w]+)\s*(<?[=-]>)\s*(\*|[-\w]+)$/);
|
||||
if (match == null || match.length < 4) {
|
||||
errors.push(`The provided transition expression "${eventStr}" is not supported`);
|
||||
@ -43,12 +49,16 @@ function parseInnerTransitionStr(
|
||||
}
|
||||
}
|
||||
|
||||
function parseAnimationAlias(alias: string, errors: string[]): string {
|
||||
function parseAnimationAlias(alias: string, errors: string[]): string|TransitionMatcherFn {
|
||||
switch (alias) {
|
||||
case ':enter':
|
||||
return 'void => *';
|
||||
case ':leave':
|
||||
return '* => void';
|
||||
case ':increment':
|
||||
return (fromState: any, toState: any): boolean => parseFloat(toState) > parseFloat(fromState);
|
||||
case ':decrement':
|
||||
return (fromState: any, toState: any): boolean => parseFloat(toState) < parseFloat(fromState);
|
||||
default:
|
||||
errors.push(`The transition alias value "${alias}" is not supported`);
|
||||
return '* => *';
|
||||
|
@ -707,7 +707,7 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe
|
||||
* ])
|
||||
* ```
|
||||
*
|
||||
* ### Transition Aliases (`:enter` and `:leave`)
|
||||
* ### Using :enter and :leave
|
||||
*
|
||||
* Given that enter (insertion) and leave (removal) animations are so common, the `transition`
|
||||
* function accepts both `:enter` and `:leave` values which are aliases for the `void => *` and `*
|
||||
@ -717,12 +717,88 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe
|
||||
* transition(":enter", [
|
||||
* style({ opacity: 0 }),
|
||||
* animate(500, style({ opacity: 1 }))
|
||||
* ])
|
||||
* ]),
|
||||
* transition(":leave", [
|
||||
* animate(500, style({ opacity: 0 }))
|
||||
* ])
|
||||
* ```
|
||||
*
|
||||
* ### Using :increment and :decrement
|
||||
* In addition to the :enter and :leave transition aliases, the :increment and :decrement aliases
|
||||
* can be used to kick off a transition when a numeric value has increased or decreased in value.
|
||||
*
|
||||
* ```
|
||||
* import {group, animate, query, transition, style, trigger} from '@angular/animations';
|
||||
* import {Component} from '@angular/core';
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'banner-carousel-component',
|
||||
* styles: [`
|
||||
* .banner-container {
|
||||
* position:relative;
|
||||
* height:500px;
|
||||
* overflow:hidden;
|
||||
* }
|
||||
* .banner-container > .banner {
|
||||
* position:absolute;
|
||||
* left:0;
|
||||
* top:0;
|
||||
* font-size:200px;
|
||||
* line-height:500px;
|
||||
* font-weight:bold;
|
||||
* text-align:center;
|
||||
* width:100%;
|
||||
* }
|
||||
* `],
|
||||
* template: `
|
||||
* <button (click)="previous()">Previous</button>
|
||||
* <button (click)="next()">Next</button>
|
||||
* <hr>
|
||||
* <div [@bannerAnimation]="selectedIndex" class="banner-container">
|
||||
* <div class="banner"> {{ banner }} </div>
|
||||
* </div>
|
||||
* `
|
||||
* animations: [
|
||||
* trigger('bannerAnimation', [
|
||||
* transition(":increment", group([
|
||||
* query(':enter', [
|
||||
* style({ left: '100%' }),
|
||||
* animate('0.5s ease-out', style('*'))
|
||||
* ]),
|
||||
* query(':leave', [
|
||||
* animate('0.5s ease-out', style({ left: '-100%' }))
|
||||
* ])
|
||||
* ])),
|
||||
* transition(":decrement", group([
|
||||
* query(':enter', [
|
||||
* style({ left: '-100%' }),
|
||||
* animate('0.5s ease-out', style('*'))
|
||||
* ]),
|
||||
* query(':leave', [
|
||||
* animate('0.5s ease-out', style({ left: '100%' }))
|
||||
* ])
|
||||
* ])),
|
||||
* ])
|
||||
* ]
|
||||
* })
|
||||
* class BannerCarouselComponent {
|
||||
* allBanners: string[] = ['1', '2', '3', '4'];
|
||||
* selectedIndex: number = 0;
|
||||
*
|
||||
* get banners() {
|
||||
* return [this.allBanners[this.selectedIndex]];
|
||||
* }
|
||||
*
|
||||
* previous() {
|
||||
* this.selectedIndex = Math.max(this.selectedIndex - 1, 0);
|
||||
* }
|
||||
*
|
||||
* next() {
|
||||
* this.selectedIndex = Math.min(this.selectedIndex + 1, this.allBanners.length - 1);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* {@example core/animation/ts/dsl/animation_example.ts region='Component'}
|
||||
*
|
||||
* @experimental Animation support is experimental.
|
||||
|
@ -1,5 +1,5 @@
|
||||
package(default_visibility=["//visibility:public"])
|
||||
load("@build_bazel_rules_typescript//:defs.bzl", "ts_library")
|
||||
load("@io_bazel_rules_typescript//:defs.bzl", "ts_library")
|
||||
|
||||
ts_library(
|
||||
name = "common",
|
@ -17,7 +17,7 @@ import {HttpHandler} from './backend';
|
||||
import {HttpHeaders} from './headers';
|
||||
import {HttpParams} from './params';
|
||||
import {HttpRequest} from './request';
|
||||
import {HttpEvent, HttpResponse} from './response';
|
||||
import {HttpEvent, HttpEventType, HttpResponse} from './response';
|
||||
|
||||
|
||||
/**
|
||||
@ -1024,14 +1024,14 @@ export class HttpClient {
|
||||
|
||||
/**
|
||||
* Construct a JSONP request for the given URL and name of the callback parameter.
|
||||
*
|
||||
*
|
||||
* @return an `Observable` of the response object as an `Object`
|
||||
*/
|
||||
jsonp(url: string, callbackParam: string): Observable<Object>;
|
||||
|
||||
/**
|
||||
* Construct a JSONP request for the given URL and name of the callback parameter.
|
||||
*
|
||||
*
|
||||
* @return an `Observable` of the response object as type `T`.
|
||||
*/
|
||||
jsonp<T>(url: string, callbackParam: string): Observable<T>;
|
||||
|
@ -11,7 +11,7 @@ import {Observable} from 'rxjs/Observable';
|
||||
|
||||
import {HttpHandler} from './backend';
|
||||
import {HttpRequest} from './request';
|
||||
import {HttpEvent} from './response';
|
||||
import {HttpEvent, HttpResponse} from './response';
|
||||
|
||||
/**
|
||||
* Intercepts `HttpRequest` and handles them.
|
||||
|
@ -12,6 +12,7 @@ import {Observable} from 'rxjs/Observable';
|
||||
import {Observer} from 'rxjs/Observer';
|
||||
|
||||
import {HttpBackend, HttpHandler} from './backend';
|
||||
import {HttpInterceptor} from './interceptor';
|
||||
import {HttpRequest} from './request';
|
||||
import {HttpErrorResponse, HttpEvent, HttpEventType, HttpResponse} from './response';
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Inject, ModuleWithProviders, NgModule, Optional} from '@angular/core';
|
||||
import {Inject, ModuleWithProviders, NgModule, Optional, forwardRef} from '@angular/core';
|
||||
|
||||
import {HttpBackend, HttpHandler} from './backend';
|
||||
import {HttpClient} from './client';
|
||||
@ -150,4 +150,4 @@ export class HttpClientModule {
|
||||
],
|
||||
})
|
||||
export class HttpClientJsonpModule {
|
||||
}
|
||||
}
|
@ -6,6 +6,9 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {empty} from 'rxjs/observable/empty';
|
||||
|
||||
import {HttpHeaders} from './headers';
|
||||
|
||||
/**
|
||||
|
@ -1,79 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import commonjs from 'rollup-plugin-commonjs';
|
||||
import * as path from 'path';
|
||||
|
||||
import 'reflect-metadata';
|
||||
|
||||
var m = /^\@angular\/((\w|\-)+)(\/(\w|\d|\/|\-)+)?$/;
|
||||
var location = normalize('../../dist/packages-dist') + '/';
|
||||
var rxjsLocation = normalize('../../node_modules/rxjs');
|
||||
var tslibLocation = normalize('../../node_modules/tslib');
|
||||
var esm = 'esm/';
|
||||
|
||||
var locations = {
|
||||
'tsc-wrapped': normalize('../../dist/tools/@angular') + '/',
|
||||
'compiler-cli': normalize('../../dist/packages') + '/'
|
||||
};
|
||||
|
||||
var esm_suffixes = {};
|
||||
|
||||
function normalize(fileName) {
|
||||
return path.resolve(__dirname, fileName);
|
||||
}
|
||||
|
||||
function resolve(id, from) {
|
||||
// console.log('Resolve id:', id, 'from', from)
|
||||
if (id == '@angular/tsc-wrapped') {
|
||||
// Hack to restrict the import to not include the index of @angular/tsc-wrapped so we don't
|
||||
// rollup tsickle.
|
||||
return locations['tsc-wrapped'] + 'tsc-wrapped/src/collector.js';
|
||||
}
|
||||
var match = m.exec(id);
|
||||
if (match) {
|
||||
var packageName = match[1];
|
||||
var esm_suffix = esm_suffixes[packageName] || '';
|
||||
var loc = locations[packageName] || location;
|
||||
var r = loc !== location && (loc + esm_suffix + packageName + (match[3] || '/index') + '.js') ||
|
||||
loc + packageName + '/@angular/' + packageName + '.es5.js';
|
||||
// console.log('** ANGULAR MAPPED **: ', r);
|
||||
return r;
|
||||
}
|
||||
if (id && id.startsWith('rxjs/')) {
|
||||
const resolved = `${rxjsLocation}${id.replace('rxjs', '')}.js`;
|
||||
return resolved;
|
||||
}
|
||||
if (id == 'tslib') {
|
||||
return tslibLocation + '/tslib.es6.js';
|
||||
}
|
||||
}
|
||||
|
||||
// hack to get around issues with default exports
|
||||
var banner = `ts['default'] = ts['default'] || ts; fs['default'] = fs['default'] || fs;`;
|
||||
|
||||
export default {
|
||||
entry: '../../dist/packages-dist/compiler-cli/src/ngc.js',
|
||||
dest: './browser-bundle.umd.js',
|
||||
format: 'umd',
|
||||
moduleName: 'ng.compiler_cli_browser',
|
||||
exports: 'named',
|
||||
external: [
|
||||
'fs',
|
||||
'path',
|
||||
'typescript',
|
||||
'reflect-metadata',
|
||||
],
|
||||
globals: {
|
||||
'typescript': 'ts',
|
||||
'path': 'path',
|
||||
'fs': 'fs',
|
||||
},
|
||||
banner: banner,
|
||||
plugins: [{resolveId: resolve}, commonjs()]
|
||||
}
|
@ -9,7 +9,7 @@
|
||||
"ng-xi18n": "./src/extract_i18n.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/tsc-wrapped": "4.3.2",
|
||||
"@angular/tsc-wrapped": "5.0.0-beta.0",
|
||||
"reflect-metadata": "^0.1.2",
|
||||
"minimist": "^1.2.0"
|
||||
},
|
||||
|