Compare commits

..

159 Commits

Author SHA1 Message Date
f34fdaf162 docs(changelog): update change log to beta 9 2016-03-09 14:56:08 -08:00
6c951a5e67 chore(release): bump version to beta.9 2016-03-09 14:45:22 -08:00
45fd6f0a41 feat(transformers): change 'Missing Identifier' to be an error
Closes #7403
2016-03-08 00:08:36 +00:00
75ae4a9159 ci(publish-build-artifacts.sh): skip all the work for builds other than upstream/master
Closes #7413
2016-03-05 19:54:22 +00:00
37d18d0112 ci(travis): publish artifacts only from the upstream/master jobs 2016-03-05 19:54:22 +00:00
773fe8f8c5 ci(travis): simplify job status reporting 2016-03-05 19:54:22 +00:00
4da2b19ea0 docs(CONTRIBUTING.md): clarify the difference between build and ci commit message scopes 2016-03-05 19:54:22 +00:00
9f3547e35d ci(travis): fix typo in webhooks config 2016-03-05 19:54:22 +00:00
5a79358727 ci(travis): fix indentation in of the .travis.yaml 2016-03-05 19:54:22 +00:00
85bfbc13c1 ci(travis): clean up matrix environmental variables
remove all unnecessary ones.
2016-03-05 19:54:22 +00:00
1a01af9e68 ci(travis): clean up, reorganize and document before_install and install scripts
Functionally this should be a noop change.
2016-03-05 19:54:22 +00:00
dd95e901df ci(travis): remove bogus environmental variable 2016-03-05 19:54:22 +00:00
9782d8c32e ci(travis): better document before_cache script 2016-03-05 19:54:22 +00:00
80764c6f71 ci(travis): use gcc v4.8 to compile npm native modules on Node v4 and v5 2016-03-05 19:54:22 +00:00
d9e78e4fa8 build(analytics): allow build analytics to take previous exit code as the first argument 2016-03-05 19:54:22 +00:00
10fedd0dfc ci(analytics): correctly report CI job errors as errors 2016-03-05 19:54:22 +00:00
315e73c47c ci(analytics): report Travis ID without the build number prefix
We need to track latency of individual jobs over time, but don't care to know what's the build ID that these jobs are associted with.
2016-03-05 19:54:22 +00:00
912717ff31 ci(analytics): fix TRAVIS_PULL_REQUEST reporting
process.env.TRAVIS_PULL_REQUEST contains a string and not a boolean value, so we need
to compare it to a string literal rather than do boolean arithmetics.
2016-03-05 19:54:22 +00:00
b857fd1eeb Revert "feat(transformers): collect provider information"
This reverts commit 81beb1c788.

Broke Google3.
2016-03-04 13:51:26 -08:00
6dce4f49c2 feat(router): Added method to get current instruction
This method delegates to the root router to get the current complete instruction.
2016-03-04 02:10:58 -08:00
15e16148f4 feat(dart/transform): Create standalone transformers for phases
Create transformers that allow specifying transformer actions on
specific libraries.

* angular2/transform/codegen: Generates all necessary code.
* angular2/transform/reflection_rewriter: Replaces `bootstrap` calls in
  application entry points to remove transitive dart:mirrors import,
  resulting in smaller code size & faster execution.
* angular2/transform/deferred_rewriter: Rewrites deferred imports and
  `loadLibrary` calls to initialize Angular2 and preserve deferred
  operation.

Proper configuration of these three transformers can replace the single
angular2 transformer, resulting in significant performance gains for
builds of large angular2 apps.

Update angular2 itself to declare the codegen transformer, since it has
neither deferred imports nor application entry points.

Remove the undocumented & unused quick_transformer.
2016-03-04 09:52:44 +00:00
ae49085481 fix(angular_1_router): Renamed require statements after TypeScript files are transpiled
The require function was causing failures when bundled using Browserify and SystemJS

Closes #7049
2016-03-04 09:31:24 +00:00
11e8aa26f6 feat(angular1_router): Add ng-link-active class to active ng-link
Closes #6882
2016-03-04 09:30:58 +00:00
81beb1c788 feat(transformers): collect provider information 2016-03-04 01:19:25 -08:00
6402d61f69 chore: fix up ngClass for types/export missing public API
Closes #7202
2016-03-04 08:03:55 +00:00
5a59e44765 chore(test): migrate Dart tests to package:test
Instead of running with karma and the karma-dart shim, run dart
tests directly using the new package:test runner. This migrates
away from package:unittest.

Fixes a couple tests, mostly associated with depending on absolute
URLs or editing the test providers after an injector had already
been created.

Remove karma-dart and associated files. Change gupfiles to run tests
via `pub run test` instead.
2016-03-04 02:27:44 +00:00
7455b907d1 Revert "feat(dart): Add a dev-mode check for undeclared lifecycle interfaces"
This reverts commit a3d7629134.

Needs co-ordination with google3 changes.
2016-03-03 18:00:18 -08:00
579b890446 chore(dart) Update dev dependency on code_tranformers
Closes https://github.com/angular/angular/issues/6665
2016-03-03 23:12:02 +00:00
19a08f3a43 feat(compiler): Added spans to HTML parser errors
Allows using the HTML parser in contexts errors are reported in a development tool such as an editor.
2016-03-03 22:51:57 +00:00
a3d7629134 feat(dart): Add a dev-mode check for undeclared lifecycle interfaces
Add a check in `ReflectionCapabilities#interfaces` which determines if
the passed-in type implements a Lifecycle Interface but does not declare
that it does so.

See https://goo.gl/b07Kii for details.

Closes #6849
2016-03-03 22:45:50 +00:00
bc9644e86e chore: add github issue / pr template 2016-03-03 14:06:11 -08:00
a10c02cb41 feat(iterable_differ): support immutable lists
Closes #7127
2016-03-03 18:29:01 +00:00
9936e347ff chore(build): Remove circular dependency in Angular 2 ES5 output.
Remove couple of circular dependency between modules in Angular 2 ES5 output caused by exception_handler.ts and router_providers.ts.

Fix the build/checkCircularDependency gulp task to call madge properly to detect the circular deps.

Closes #7287
2016-03-03 17:42:33 +00:00
7d44b8230e fix(router): support outlets within dynamic components
Fixes internal b/27294172
2016-03-03 06:49:29 -08:00
75343eb340 feat(router): add regex matchers
@petebacondarwin deserves credit for most of this commit.

This allows you to specify a regex and serializer function instead
of the path DSL in your route declaration.

```
@RouteConfig([
  { regex: '[a-z]+.[0-9]+',
    serializer: (params) => `{params.a}.params.b}`,
    component: MyComponent }
])
class Component {}
```

Closes #7325
Closes #7126
2016-03-02 16:08:19 -08:00
2548ce86db fix(angular1_router): rename router component binding to $router
The current router is passed to the current component via a binding.
To indicate that this is an angular provided object, this commit
renames the binding to `$router`.

BREAKING CHANGE:

The recently added binding of the current router to the current component
has been renamed from `router` to `$router`.

So now the recommended set up for your bindings in your routed component
is:

```js
{
  ...
  bindings: {
    $router: '<'
  }
}
```
2016-03-02 16:08:19 -08:00
5586c29492 fix(angular1_router): support templateUrl components 2016-03-02 16:08:19 -08:00
1174473e9c fix(angular1_router): rename router component binding to $router
The current router is passed to the current component via a binding.
To indicate that this is an angular provided object, this commit
renames the binding to `$router`.

BREAKING CHANGE:

The recently added binding of the current router to the current component
has been renamed from `router` to `$router`.

So now the recommended set up for your bindings in your routed component
is:

```js
{
  ...
  bindings: {
    $router: '<'
  }
}
```
2016-03-02 16:08:19 -08:00
1d49b3e36b fix(build): Use fixed version of Chromium Canary that will be updated manually instead of automatically using the latest Chrome canary 2016-03-02 15:35:07 -08:00
2830df4190 docs(changelog): update change log to beta 8 2016-03-02 11:32:38 -08:00
143cf89b5f chore(release): bump version to beta. 2016-03-02 11:31:25 -08:00
69c1694900 fix(WebWorker): Make MessageBus EventEmitter synchronous 2016-03-01 23:23:17 +00:00
01fe7f5fac fix(WebWorker): Fix PostMessageBusSink and Source undefined error.
Closes #7156
2016-03-01 14:40:09 -08:00
9aedef208f fix(test): fix a broken test 2016-03-01 14:38:55 -08:00
39b6e0efba feat(transformers): collect information for CompileDiDependencyMetadata 2016-03-01 13:28:36 -08:00
f60fa14767 feat(core): drop ChangeDetectionStrategy.OnPushObserve
BREAKING CHANGE:

`OnPushObserve` was an experimental
feature for Dart and had
conceptual performance problems,
as setting up observables is slow.
Use `OnPush` instead.
2016-03-01 13:28:10 -08:00
d900f5c075 chore(tests): lengthen timeout for templateUrl test
This was flaking on Travis occasionally because the TestComponentBuilder
is actually doing an XHR, and it was slow on the Edge browser.

Closes #7293
2016-03-01 21:01:56 +00:00
391a9edabb chore(build): use Chromium in Travis for JS tests 2016-03-01 11:24:44 -08:00
28a78117eb refactor(dart/transform): Migrates tests to use package:test
Our transformer unit tests currently use package:guinness, which uses
package:unittest under the covers. package:unittest has been updated and
renamed package:test, so for simplicity migrate test to use package:test
syntax.
2016-03-01 19:20:06 +00:00
eeb594c010 fix(dart/payload): Fix runtime error in hello_world payload app
The hello_world app used to measure Dart payload size was broken by a
change in
7ae23adaff.

This breakage does not materially affect the size of the generated code,
(before fix: 298819, after fix: 298825), and since it was a runtime
error it was not noticed & not a problem.

Update the app to work again.

Closes #7358
2016-03-01 18:51:12 +00:00
0bb10d6bb6 feat(transformers): makes the map of resolved identifiers configurable
Closes #7359
2016-03-01 17:57:19 +00:00
59629a0801 feat(i18n): added i18nPlural and i18nSelect pipes
Closes #7268
2016-03-01 16:40:48 +00:00
b5e6319fa9 feat(core): add more debug APIs to inspect the application form a browser
Adds `window.getAllAngularRootElements()`
Adds `ng.coreTokens.ApplicationRef`
Adds `ng.coreTokens.Ngzone`

Closes #7045
Closes #7161
2016-03-01 16:01:28 +00:00
c9a3df970b feat(di): drop support for injecting types with generics in Dart
BREAKING CHANGE:
In Dart we used to support injecting types with generics. As this feature is hard to implement with the upcoming codegen we are dropping it.

Merge cl/115454020 in G3 with this change.
Closes #7262
2016-03-01 05:43:49 +00:00
f72f137261 ci(dart): Uncomment dart.dev build and make it required
Workaround for https://github.com/dart-lang/dartdoc/issues/1099: remove
the `--input=.` parameter to `dartdoc`.

Closes #6823, #6410

Closes #6958
2016-03-01 01:21:27 +00:00
ee3c580e88 fix(transformers): replace an error with a warning when cannot resolve a symbol 2016-02-29 16:03:13 -08:00
05c185a7b1 fix(transformers): record reflection info about abstract classes
Closes #7347
2016-02-29 22:57:22 +00:00
b47f80ec76 fix(Router): Query strings are copied for HashLocationStrategy
b/27210802 P1

Closes #7298
2016-02-29 18:44:56 +00:00
ebd438ff5e fix(change_detection): allow to destroy OnPush components inside of a host event.
Closes #7192
2016-02-29 18:44:13 +00:00
331b9c1317 fix(transformers): special case types some built-in types, so they can be resolved 2016-02-29 10:34:34 -08:00
4a93f58b8b fix(web_worker): wait for bindings in kitchen sink spec 2016-02-26 10:34:32 -08:00
ebe531bf92 feat(transformers): collect data needed for the template compiler
Closes #7299
2016-02-26 17:56:40 +00:00
1779caf5f8 fix(core): support ngFor that has an ngIf as last node
Fixes #6304
Closes #6878
2016-02-25 23:42:17 +00:00
6ef2121e6a feat(pipes): add ReplacePipe for string manipulation
add commonly used pipe that is missing  from framework.

feat(pipes): add ReplacePipe for string manipulation

add commonly used pipe that is missing  from framework.

feat(pipes): add ReplacePipe for string manipulation

add commonly used pipe that is missing  from framework.

feat(pipes): add ReplacePipe for string manipulation

add commonly used pipe that is missing  from framework.

feat(pipes): add ReplacePipe for string manipulation

add commonly used pipe that is missing  from framework.

feat(pipes): add ReplacePipe for string manipulation

add commonly used pipe that is missing  from framework.

feat(pipes): add ReplacePipe for string manipulation

add commonly used pipe that is missing  from framework.

feat(pipes): add ReplacePipe for string manipulation

add commonly used pipe that is missing  from framework.

feat(pipes): add ReplacePipe for string manipulation

add commonly used pipe that is missing  from framework.

feat(pipes): add ReplacePipe for string manipulation

add commonly used pipe that is missing  from framework.

feat(pipes): add ReplacePipe for string manipulation

add commonly used pipe that is missing  from framework.

feat(pipes): add ReplacePipe for string manipulation

add commonly used pipe that is missing  from framework.

feat(pipes): add ReplacePipe for string manipulation

add commonly used pipe that is missing  from framework.

feat(pipes): add ReplacePipe for string manipulation

add commonly used pipe that is missing  from framework.
2016-02-25 22:56:13 +00:00
38cb526f60 feat(forms/validators): pattern validator
Adding static pattern validation method to Validators

Adding a directive for the pattern validator

Applying clang-format rules to modified files

Updating public api spec for new pattern validator

Adding pattern validator to public api guard tool

For #5411

Closes #5561
2016-02-25 22:39:15 +00:00
f6a8d04c32 fix(web_workers): make waitForElementText function more stable 2016-02-25 14:28:20 -08:00
4b3b5d7c53 cleanup(build): fix jsserve not to throw 2016-02-25 13:39:02 -08:00
abff302e52 chore(tests): fix broken test from using the wrong xit
Also, run clang-format.
2016-02-25 10:50:59 -08:00
e1f6679c75 chore: make incremental dart tests work again 2016-02-25 10:26:11 -08:00
aaafdf03ce test(testing): add a test to ensure fakeAsync works with angular2/testing 2016-02-25 10:22:18 -08:00
ee298baa1b test(router): disable a flaky test 2016-02-25 10:21:53 -08:00
d1abada5b7 build(travis): do not cache npm modules 2016-02-24 17:56:55 -08:00
ab36ea097b fix(differ): clean up stale identity change refs
Closes #7193
2016-02-24 17:56:55 -08:00
8bb66a5eb3 chore: noImplicitAny fixes 2016-02-24 15:29:00 -08:00
cfc1e56dd8 refact(angular1_router): make the $$router binding one-time
This binding is never going to change so we can make it a one-time binding

Closes #6978
2016-02-24 21:15:34 +00:00
a1c3be21ec fix(angular1_router): rename $route service to $rootRouter
The singleton service that represents the top level router was called
`$router` but this is confusing since there are actually lots of routers,
which depend upon where you are in the DOM. This is similar to the situation
with scopes.

This commit clarifies this singleton by renaming it to `$rootRouter`.

BREAKING CHANGE:

The `$router` injectable service has been renamed to `$rootRouter`
2016-02-24 21:15:34 +00:00
edad8e3f56 fix(angular1_router): rename router component binding to $router
The current router is passed to the current component via a binding.
To indicate that this is an angular provided object, this commit
renames the binding to `$router`.

BREAKING CHANGE:

The recently added binding of the current router to the current component
has been renamed from `router` to `$router`.

So now the recommended set up for your bindings in your routed component
is:

```js
{
  ...
  bindings: {
    $router: '<'
  }
}
```
2016-02-24 21:15:34 +00:00
d4a4d81173 fix(angular1_router): support templateUrl components 2016-02-24 21:15:34 +00:00
e7470d557d feat(core): Add QueryList.forEach to public api. 2016-02-19 19:23:46 -05:00
b634a25ae0 feat(core): Add QueryList#forEach 2016-02-19 19:23:46 -05:00
c1a0af514f feat(test): add withProviders for per test providers
Closes #5128
2016-02-19 19:23:46 -05:00
c6afea61f1 fix(DomRenderer): correctly handle namespaced attributes 2016-02-19 19:23:46 -05:00
ce10fe92b2 chore(travis): remove problematic chromebeta target for now 2016-02-19 10:43:59 -08:00
b81b1fb81c revert: fix(change_detection): allow to destroy OnPush components inside of a host event
This reverts commit 280b86ec55.
2016-02-19 10:34:03 -08:00
280b86ec55 fix(change_detection): allow to destroy OnPush components inside of a host event. 2016-02-18 17:54:24 -08:00
2f5a2ba671 docs(changelog): update changelog to beta.7 2016-02-18 13:28:10 -08:00
c45ec6f1be chore(release): bump version to beta.7 2016-02-18 13:28:08 -08:00
530470e0ce chore(travis): add an integration hook for the angular hubot daemon 2016-02-17 16:38:42 -08:00
ce72ccf9e8 build(npm): bump zone.js version to 0.15.5 2016-02-17 16:28:50 -08:00
46d9c87ddc build(package): bump rxjs to 5.0.0-beta.2
Closes #7001
2016-02-17 16:28:50 -08:00
d736c31fea test(material): disable problematic e2e test
LGTM from Victor and Jeff in person :)
2016-02-17 16:18:33 -08:00
a7e9bc97f6 ci(typescript): add typescript_next build
Install typescript@next before build.js and test.typings.
Restore the regular version before travis caches node_modules/.

Fixes #6368
2016-02-16 17:29:29 -08:00
265703b950 fix(typing): Remove re-export of the Promise built-in type.
Instead, ts2dart can add the 'dart:async' import whenever
Promise is used.

Fixes #6468
2016-02-12 20:45:41 -08:00
ae275fa4e4 chore(ts2dart): update ts2dart to 0.7.24 2016-02-12 20:45:35 -08:00
3478d5d450 fix(angular_1_router): Added DI string tokens
Closes #4269

Closes #7031
2016-02-12 21:15:34 +00:00
e72dc16dbe docs(changelog): update changelog to beta.6 2016-02-11 16:03:00 -08:00
40a043275d chore(release): bump version to beta.6 2016-02-11 15:59:34 -08:00
f161b5cc28 chore(zone.js): update to 0.5.14 2016-02-11 14:39:41 -08:00
117d57e121 Revert "chore(zone.js) : update to 0.5.14"
This reverts commit 3dcce706fd.
2016-02-11 14:38:06 -08:00
3dcce706fd chore(zone.js) : update to 0.5.14 2016-02-11 14:15:59 -08:00
efb89b83e1 Revert "fix(DomRenderer): correctly handle namespaced attributes"
This reverts commit 61cf499b0b.
2016-02-11 13:44:16 -08:00
3d96c2337f Revert "feat(svg): Provide support for SVG foreignObject by adding xhtml namespace"
This reverts commit eb688f2c8e.
2016-02-11 13:39:02 -08:00
19cfb4eb12 fix(build): publish typings directory to our npm snapshot branch 2016-02-11 11:35:43 -08:00
3d715a2f7b fix(typings): publish es6 typings rather than postinstall.
Despite local testing, multiple users failed to run the postinstall to install typings.
Instead, we can distribute the typings we installed locally.

This is an alternative to #7003.
This also reverts rxjs to beta.1 since we have errors using beta.2, being addressed
in #7001.

Fixes #7000
2016-02-11 11:04:42 -08:00
c7261c295c docs(changelog): update change log to beta.5 2016-02-10 16:31:01 -08:00
1a26f8edd6 chore(release): bump version to beta.5 2016-02-10 16:30:18 -08:00
fc887774da fix(release): need to depend on latest rxjs and zone.js
The version in our package.json gets copied to the one we publish, and users need the latest of these.
2016-02-10 16:29:58 -08:00
7cbf88a691 docs(changelog): update change log to beta.4 2016-02-10 16:04:17 -08:00
1cb1c139cf chore(release): bump version to beta.4 2016-02-10 16:04:17 -08:00
1fd924f7d5 refactor(dart/transform): Simplify deferred rewriting
Simplify the DeferredRewriting and move package:quiver to a dev
dependency.

Closes #6994
2016-02-10 23:43:57 +00:00
eb688f2c8e feat(svg): Provide support for SVG foreignObject by adding xhtml namespace
Closes #6192
2016-02-10 23:23:34 +00:00
61cf499b0b fix(DomRenderer): correctly handle namespaced attributes
Closes #6363
2016-02-10 22:34:13 +00:00
f1f5b45361 feat(typings): install es6-shim typings to a location users can reference.
This makes the upgrade to beta.4 as simple as adding one reference tag, only when --target=es5
Implements option 3 from https://docs.google.com/document/d/1vgepQPkuHS4P3rzANQpoMIDIXe0Rl9Z2QyTtb8dpMoI/edit
2016-02-10 14:13:27 -08:00
50548fb565 fix(forms): use strict runtimeType checks instead of instanceof
Currently, validators extending built-in validators are treated as built-in.
This can result in an error when both a real built-in validator and a custom one are applied to the same element.

Closes #6981
2016-02-10 09:23:59 -08:00
8f47aa3530 fix(forms): add RadioButtonValueAccessor to the list of default value accessors 2016-02-09 15:28:08 -08:00
df7885c9f5 fix(router): Added route data to normalized async route
Closes #6802
2016-02-09 22:08:45 +00:00
0f10624b08 fix(ngFor): update view locals if identity changes
Closes #6923
2016-02-09 22:06:06 +00:00
6f1ef33e32 fix(router): fix url path for star segment in path recognizer
Url path of star segments should equal the original path.

If you register the route `/app/*location` and invoke a url like `/app/foo/bar`
the PathRecognizer should return a url path equal to the invoked url.

Before this patch, everything after `foo` was ignored, which resulted in a
redirect to `/app/foo` which was probably not intended (at least in the angular
1.5 component router).

Closes #6976
2016-02-09 21:45:06 +00:00
231773ea76 fix(compiler): use event names for matching directives
Closes #6870
2016-02-09 13:16:08 -08:00
e725542703 fix(forms): add support for radio buttons
Closes #6877
2016-02-09 19:47:50 +00:00
2337469753 style(angular1_router): license year updated
Closes #6219
2016-02-08 17:21:00 -08:00
55122cd57a fix(angular1-router): add missing wrapper methods
Closes #6763
Closes #6861
Closes #6861
2016-02-08 17:19:50 -08:00
7e0f02f96e fix(upgrade): fix infinite $rootScope.$digest()
Fixes #6385
Closes #6386
2016-02-08 17:18:52 -08:00
e7ad03cba6 fix(core): add detail to dehydrated detector exception
- at least I know what component is causing the error with this. without it the exception is so generic that it's not useful.

Closes #6939
2016-02-08 17:17:37 -08:00
74be3d3fde fix(core): mute mode printing in console in prod mode
Closes #6873
2016-02-08 17:16:19 -08:00
a15ca23469 refactor(mock): update variable names in directive_resolver_mock.ts
Closes #5056
2016-02-08 17:11:43 -08:00
de77700da0 fix(di): throw if a token uses more than 20 dependencies.
Fixes #6690
Closes #6869
2016-02-08 16:21:57 -08:00
e73fee7156 fix(router): fixed the location wrapper for angular1
In angular2 `Location.path()` returns the complete path including query string. In angular1 the query parameters are missing. Similar to this `Location.go` does accept two parameters (path *and query*).

Closes #6943
2016-02-08 16:18:26 -08:00
72ab35bceb test(angular1_router): test that location handles query strings
See 6698
2016-02-08 16:18:13 -08:00
0f22dce036 feat(angular1_router): allow component to bind to router 2016-02-08 16:18:13 -08:00
c6036435f0 fix(router): don't prepend / unnecessarily to Location paths
Closes #6729
Closes #5502
2016-02-08 16:18:13 -08:00
d86be245b8 fix(angular1-router): add support for using the component helper
In Angular 1.5 there is a new helper method for creating component directives.
See https://docs.angularjs.org/guide/component for more information about components.

These kind of directives only match the `E` element form and the previously component
router only created HTML that matched directives that matched the `A` attribute form.

This commit changes the `<ng-outlet>` directive so that it generates custom HTML
elements rather divs with custom attributes to trigger the relevant component to
appear in the DOM.

Going forward, Angular 1.5 users are encouraged to create their router components
using the following style:

```
myModule.componnet('component-name', {
  // component definition object
});
```

Closes angular/angular.js#13860
Closes #6076
Closes #5278

BREAKING CHANGE:

The component router now creates custom element HTML rather than custom attribute
HTML, in order to create a new component. So rather than

```html
<div custom-component></div>
```

it now creates

```html
<custom-component></custom-component>
```

If you defined you router components using the `directive()` helper and
specified the `restrict` properties such that element matching was not allowed,
e.g. `restrict: 'A'` then these components will no longer be instantiated
by the component router and the outlet will be empty.

The fix is to include `E` in the `restrict` property.

`restrict: 'EA'`

Note that this does not affect directives that did not specify the `restrict`
property as the default for this property is already `EA`.
2016-02-08 16:18:13 -08:00
a26053d3ff docs(cheatsheet): fix Dart cheatsheet
Also deletes an extraneous period from the JS cheatsheet.

Closes #5936
2016-02-08 15:58:22 -08:00
24d5b665e1 docs(HostBindingMetadata): Removed brackets from host bindings in HostBindingsMetadata example
Closes #6941
2016-02-08 15:55:49 -08:00
aa98fad338 docs: fix typo in 01_templates.md
Replace `an` with `a`

Closes #6842
2016-02-08 15:52:30 -08:00
9cb6dbbbab docs: fixed typo in documentation
Closes #6842
2016-02-08 15:30:15 -08:00
e21718faa9 docs(router): Updated inconsistencies in router docs
Closes #6805
2016-02-08 22:31:04 +00:00
b0f7d59e64 revert: chore: update the version of ts2dart
This reverts commit 22929a1671.

This commits makes our build red without the other commit that was already reverted.

More info at: https://github.com/angular/angular/pull/6825#issuecomment-181592303
2016-02-08 14:13:00 -08:00
b86829f492 revert: feat(transformers): collect information about di dependencies and providers
This reverts commit 86c40f8474.

Reason: new issues were discovered during the g3sync. @vsavkin is working on fixing them.
2016-02-08 12:15:03 -08:00
22929a1671 chore: update the version of ts2dart
Closes #6804
2016-02-05 21:56:33 +00:00
86c40f8474 feat(transformers): collect information about di dependencies and providers 2016-02-05 21:56:33 +00:00
16b521794c fix(build): don't try to copy .d.ts files into the npm distro
Fixes #6921
2016-02-05 11:53:15 -08:00
2a70f4e4c7 fix(typings): Don't expose typing dependencies to users.
This resolves Duplicate Identifier issues seen by many users,
at the expense of more typings installation required in some
cases.

Removes the quickstart hack of placing all needed dependencies
typings files in our distribution. Removes dependencies on
nodejs from angular2/core.

Fixes #5973
Fixes #5807
Fixes #6266

Angular now depends on es6-promise and es6-collections
(and a handful of manual typings) rather than all of es6-shim.

Fixes #5242

We previously had an undocumented breaking change, this is now
documented in this commit.

Fixes #6817

BREAKING CHANGE:

Transitive typings are no longer included in the distribution.
You may need to install typings in your project using
http://github.com/typings/typings

Users now must rely on getting typings from:
- one of the peerDependencies, such as rxjs, which exposes
  typings via the moduleResolution=node mechanism.
  (see https://github.com/Microsoft/TypeScript/wiki/Typings-for-npm-packages)
  This happens automatically.
- Using --target ES5 now requires manual installation of
  es6-promise and es6-collections typings.
- Using some angular APIs may introduce a dependency on eg. nodejs
  or jasmine, and those typings need manual installation as well.

Closes #6267
2016-02-04 22:42:40 +00:00
2f31c4c1c5 chore(typings): use mainline DefinitelyTyped repo rather than a fork.
The upstream Jasmine typings don't define a type for the global
object with Jasmine methods polluting it, so just use any.

Also zone.js has a different name upstream.
2016-02-04 22:42:40 +00:00
1435763383 chore(deps): update ts2dart and zone.js 2016-02-04 22:42:40 +00:00
05238df89b docs(changelog): add missing breaking change for beta.3 2016-02-03 11:43:17 -08:00
772d60d9fe docs(changelog): changelog for beta.3 2016-02-03 10:36:06 -08:00
24086bf0bb chore(release): bump version to beta.3 2016-02-03 10:36:06 -08:00
9b0e10e9a7 fix(compiler): fix interpolation regexp
- Fix the interpolation regexp to match newline characters (i.e. `\n` and `\r`)

Closes #6056
2016-02-03 15:21:55 +00:00
995a9e0cf8 fix(router): fix incorrect url param value coercion of 1 to true
seriliazeParams is coercing a value of 1 to true, which causes the value to be completey dropped.
Change the test from double equals to triple equals to prevent this from happening.

Closes #5346

Closes #6286
2016-02-03 15:00:24 +00:00
b55f1764b5 fix(Headers): serializable toJSON
fixes #6073

Closes #6714
2016-02-03 14:03:01 +00:00
5e9daed2e8 docs(http.ts): Fix MockBackend examples using backend.connections observer
Properly format observer examples.
2016-02-03 05:58:40 -08:00
aa8c5aa2e2 docs(http): fix example usage of MockBackend 2016-02-03 05:57:08 -08:00
f2c7946cca chore(http): make all typings explicit 2016-02-03 05:31:40 -08:00
da1fcfd820 fix(WebWorkers): Fix flaky WebWorker test
Closes #6851
2016-02-03 05:30:11 -08:00
dbeff6f548 style(ReflectionCapabilities) _zipTypesAndAnnotations, not _zipTypesAndAnnotaions
I was stepping through the Reflector and came across this little guy.

Closes #6535
2016-02-03 03:57:06 +00:00
26e60d658a fix(async): handle synchronous initial value in async pipe
Closes #5996
2016-02-03 03:56:52 +00:00
c2ceb7fba4 fix(Validators): fix Validators.required marking number zero as invalid
Closes #6617
2016-02-03 03:34:42 +00:00
4bfe49cd42 docs(core): update QueryList's onChange to changes.subscribe 2016-02-02 19:23:03 -08:00
428 changed files with 7259 additions and 4402 deletions

38
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,38 @@
**Note: for support questions, please use one of these channels:** https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question. This repository's issues are reserved for feature requests and bug reports.
* **I'm submitting a ... **
[ ] bug report
[ ] feature request
[ ] support request => Please do not submit support request here, see note at the top of this template.
* **Do you want to request a *feature* or report a *bug*?**
* **What is the current behavior?**
* **If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem** via
https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5).
* **What is the expected behavior?**
* **What is the motivation / use case for changing the behavior?**
* **Please tell us about your environment:**
- Angular version: 2.0.0-beta.X
- Browser: [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
- Language: [all | TypeScript X.X | ES6/7 | ES5 | Dart]
* **Other information** (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow, gitter, etc)

24
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,24 @@
* **Please check if the PR fulfills these requirements**
- [ ] The commit message follows our guidelines: https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit-message-format
- [ ] Tests for the changes have been added (for bug fixes / features)
- [ ] Docs have been added / updated (for bug fixes / features)
* **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...)
* **What is the current behavior?** (You can also link to an open issue here)
* **What is the new behavior (if this is a feature change)?**
* **Does this PR introduce a breaking change?** (What changes might users need to make in their application due to this PR?)
* **Other information**:

1
.gitignore vendored
View File

@ -22,6 +22,7 @@ tmp
*.js.map
# Or type definitions we mirror from github
# (NB: these lines are removed in publish-build-artifacts.sh)
**/typings/**/*.d.ts
**/typings/tsd.cached.json

View File

@ -1,7 +1,7 @@
language: node_js
sudo: false
node_js:
- '5.4.1'
- '5.4.1'
branches:
except:
@ -9,73 +9,94 @@ branches:
cache:
directories:
- node_modules
- $HOME/.pub-cache
- $HOME/.chrome/chromium
before_cache:
# Undo the pollution of the typescript_next build before the cache is primed for future use
- if [[ "$MODE" == "typescript_next" ]]; then npm install typescript; fi
env:
global:
- KARMA_BROWSERS=DartiumWithWebPlatform
- E2E_BROWSERS=Dartium
- LOGS_DIR=/tmp/angular-build/logs
- SAUCE_USERNAME=angular-ci
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
- BROWSER_STACK_USERNAME=angularteam1
- BROWSER_STACK_ACCESS_KEY=BWCd4SynLzdDcv8xtzsB
- ARCH=linux-x64
- DART_DEV_VERSION=latest
- DART_STABLE_VERSION=latest
# Token for tsd to increase github rate limit
# See https://github.com/DefinitelyTyped/tsd#tsdrc
# This does not use http://docs.travis-ci.com/user/environment-variables/#Secure-Variables
# because those are not visible for pull requests, and those should also be reliable.
# This SSO token belongs to github account angular-github-ratelimit-token which has no access
# (password is in Valentine)
- TSDRC='{"token":"ef474500309daea53d5991b3079159a29520a40b"}'
# GITHUB_TOKEN_ANGULAR
- secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
# Use newer verison of GCC to that is required to compile native npm modules for Node v4+ on Ubuntu Precise
# more info: https://docs.travis-ci.com/user/languages/javascript-with-nodejs#Node.js-v4-(or-io.js-v3)-compiler-requirements
- CXX=g++-4.8
- KARMA_DART_BROWSERS=DartiumWithWebPlatform
# No sandbox mode is needed for Chromium in Travis, it crashes otherwise: https://sites.google.com/a/chromium.org/chromedriver/help/chrome-doesn-t-start
- KARMA_JS_BROWSERS=ChromeNoSandbox
- E2E_BROWSERS=ChromeOnTravis
- LOGS_DIR=/tmp/angular-build/logs
- SAUCE_USERNAME=angular-ci
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
- BROWSER_STACK_USERNAME=angularteam1
- BROWSER_STACK_ACCESS_KEY=BWCd4SynLzdDcv8xtzsB
- ARCH=linux-x64
- DART_DEV_VERSION=latest
- DART_STABLE_VERSION=latest
- DART_CHANNEL=stable
- DART_VERSION=$DART_STABLE_VERSION
# Token for tsd to increase github rate limit
# See https://github.com/DefinitelyTyped/tsd#tsdrc
# This does not use http://docs.travis-ci.com/user/environment-variables/#Secure-Variables
# because those are not visible for pull requests, and those should also be reliable.
# This SSO token belongs to github account angular-github-ratelimit-token which has no access
# (password is in Valentine)
- TSDRC='{"token":"ef474500309daea53d5991b3079159a29520a40b"}'
# GITHUB_TOKEN_ANGULAR
- secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
matrix:
# Order: a slower build first, so that we don't occupy an idle travis worker waiting for others to complete.
- MODE=dart DART_CHANNEL=stable DART_VERSION=$DART_STABLE_VERSION
# Disable dart dev build, which is timing out after 2h. #6823
# - MODE=dart DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
- MODE=saucelabs_required DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
- MODE=browserstack_required DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
- MODE=saucelabs_optional DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
- MODE=browserstack_optional DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
- MODE=dart_ddc DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
- MODE=js DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
- MODE=router DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
- MODE=build_only DART_CHANNEL=stable DART_VERSION=$DART_STABLE_VERSION
- MODE=lint DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
- MODE=payload DART_CHANNEL=stable DART_VERSION=$DART_STABLE_VERSION
- MODE=dart
- MODE=dart DART_CHANNEL=dev
- MODE=saucelabs_required
- MODE=browserstack_required
- MODE=saucelabs_optional
- MODE=browserstack_optional
- MODE=dart_ddc
- MODE=js
- MODE=router
- MODE=build_only
- MODE=typescript_next
- MODE=lint
- MODE=payload
matrix:
allow_failures:
- env: "MODE=saucelabs_optional DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
- env: "MODE=browserstack_optional DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
# TODO(alxhub): remove when dartdoc #1039 is in dev channel
- env: "MODE=dart DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
- env: "MODE=saucelabs_optional"
- env: "MODE=browserstack_optional"
# Tracked in https://github.com/angular/angular/issues/7050
- env: "MODE=typescript_next"
addons:
firefox: "38.0"
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
before_install:
- npm install -g npm@3.5.3
- node tools/analytics/build-analytics start ci job
- node tools/analytics/build-analytics start ci before_install
- echo ${TSDRC} > .tsdrc
- export DISPLAY=:99.0
- export GIT_SHA=$(git rev-parse HEAD)
- ./scripts/ci/init_android.sh
- ./scripts/ci/install_dart.sh ${DART_CHANNEL} ${DART_VERSION} ${ARCH}
- sh -e /etc/init.d/xvfb start
- if [[ -e SKIP_TRAVIS_TESTS ]]; then { cat SKIP_TRAVIS_TESTS ; exit 0; } fi
- '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_BRANCH}" = "master" ] && SAUCE_USERNAME="angular2-ci" && SAUCE_ACCESS_KEY="693ebc16208a-0b5b-1614-8d66-a2662f4e" || true'
- node tools/analytics/build-analytics success ci before_install
- node tools/analytics/build-analytics start ci job
- node tools/analytics/build-analytics start ci before_install
- echo ${TSDRC} > .tsdrc
- export CHROME_BIN=$HOME/.chrome/chromium/chrome-linux/chrome
- export DISPLAY=:99.0
- export GIT_SHA=$(git rev-parse HEAD)
- ./scripts/ci/init_android.sh
- sh -e /etc/init.d/xvfb start
# Use a separate SauseLabs account for upstream/master builds in order for Sauce to create a badge representing the status of just upstream/master
- '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_BRANCH}" = "master" ] && SAUCE_USERNAME="angular2-ci" && SAUCE_ACCESS_KEY="693ebc16208a-0b5b-1614-8d66-a2662f4e" || true'
- node tools/analytics/build-analytics success ci before_install
install:
- node tools/analytics/build-analytics start ci install
# Check the size of caches
# Install version of npm that we are locked against
- npm install -g npm@3.5.3
# Install version of Chromium that we are locked against
- ./scripts/ci/install_chromium.sh
# Install version of Dart based on the matrix build variables
- ./scripts/ci/install_dart.sh ${DART_CHANNEL} ${DART_VERSION} ${ARCH}
# Print the size of caches to ease debugging
- du -sh ./node_modules || true
# Install npm dependecies
# check-node-modules will exit(1) if we don't need to install
@ -84,33 +105,33 @@ install:
- node tools/analytics/build-analytics success ci install
before_script:
- node tools/analytics/build-analytics start ci before_script
- mkdir -p $LOGS_DIR
- ./scripts/ci/presubmit-queue-setup.sh
- node tools/analytics/build-analytics success ci before_script
- node tools/analytics/build-analytics start ci before_script
- mkdir -p $LOGS_DIR
- ./scripts/ci/presubmit-queue-setup.sh
- node tools/analytics/build-analytics success ci before_script
script:
- node tools/analytics/build-analytics start ci script
- ./scripts/ci/build_and_test.sh ${MODE}
- node tools/analytics/build-analytics success ci script
- node tools/analytics/build-analytics start ci script
- ./scripts/ci/build_and_test.sh ${MODE}
- node tools/analytics/build-analytics success ci script
after_script:
- node tools/analytics/build-analytics start ci after_script
- ./scripts/ci/print-logs.sh
- ./scripts/ci/after-script.sh
- ./scripts/publish/publish-build-artifacts.sh
- node tools/analytics/build-analytics success ci after_script
- if [[ $TRAVIS_TEST_RESULT -eq 0 ]]; then node tools/analytics/build-analytics success ci job; else node tools/analytics/build-analytics error ci job; fi
- node tools/analytics/build-analytics start ci after_script
- ./scripts/ci/print-logs.sh
- ./scripts/ci/after-script.sh
- ./scripts/publish/publish-build-artifacts.sh
- node tools/analytics/build-analytics success ci after_script
- tools/analytics/build-analytics $TRAVIS_TEST_RESULT ci job
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/1ef62e23078036f9cee4
# trigger Buildtime Trend Service to parse Travis CI log
- https://buildtimetrend.herokuapp.com/travis
- https://webhooks.gitter.im/e/1ef62e23078036f9cee4
# trigger Buildtime Trend Service to parse Travis CI log
- https://buildtimetrend.herokuapp.com/travis
- http://104.197.9.155:8484/hubot/travis/activity
on_success: always # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: false # default: false
on_start: never # default: never
slack:
secure: EP4MzZ8JMyNQJ4S3cd5LEPWSMjC7ZRdzt3veelDiOeorJ6GwZfCDHncR+4BahDzQAuqyE/yNpZqaLbwRWloDi15qIUsm09vgl/1IyNky1Sqc6lEknhzIXpWSalo4/T9ZP8w870EoDvM/UO+LCV99R3wS8Nm9o99eLoWVb2HIUu0=

View File

@ -1,3 +1,201 @@
<a name="2.0.0-beta.9"></a>
# 2.0.0-beta.9 (2016-03-09)
### Bug Fixes
* **angular_1_router:** Renamed require statements after TypeScript files are transpiled ([ae49085](https://github.com/angular/angular/commit/ae49085)), closes [#7049](https://github.com/angular/angular/issues/7049)
* **angular1_router:** rename `router` component binding to `$router` ([2548ce8](https://github.com/angular/angular/commit/2548ce8))
* **angular1_router:** rename `router` component binding to `$router` ([1174473](https://github.com/angular/angular/commit/1174473))
* **angular1_router:** support templateUrl components ([5586c29](https://github.com/angular/angular/commit/5586c29))
* **build:** Use fixed version of Chromium Canary that will be updated manually instead of au ([1d49b3e](https://github.com/angular/angular/commit/1d49b3e))
* **router:** support outlets within dynamic components ([7d44b82](https://github.com/angular/angular/commit/7d44b82))
### Features
* **angular1_router:** Add ng-link-active class to active ng-link ([11e8aa2](https://github.com/angular/angular/commit/11e8aa2)), closes [#6882](https://github.com/angular/angular/issues/6882)
* **compiler:** Added spans to HTML parser errors ([19a08f3](https://github.com/angular/angular/commit/19a08f3))
* **dart:** Add a dev-mode check for undeclared lifecycle interfaces ([a3d7629](https://github.com/angular/angular/commit/a3d7629)), closes [#6849](https://github.com/angular/angular/issues/6849)
* **dart/transform:** Create standalone transformers for phases ([15e1614](https://github.com/angular/angular/commit/15e1614))
* **iterable_differ:** support immutable lists ([a10c02c](https://github.com/angular/angular/commit/a10c02c)), closes [#7127](https://github.com/angular/angular/issues/7127)
* **router:** add regex matchers ([75343eb](https://github.com/angular/angular/commit/75343eb)), closes [#7325](https://github.com/angular/angular/issues/7325) [#7126](https://github.com/angular/angular/issues/7126)
* **router:** Added method to get current instruction ([6dce4f4](https://github.com/angular/angular/commit/6dce4f4))
* **transformers:** change 'Missing Identifier' to be an error ([45fd6f0](https://github.com/angular/angular/commit/45fd6f0)), closes [#7403](https://github.com/angular/angular/issues/7403)
* **transformers:** collect provider information ([81beb1c](https://github.com/angular/angular/commit/81beb1c))
### BREAKING CHANGES
* The recently added binding of the current router to the current component
has been renamed from `router` to `$router`.
So now the recommended set up for your bindings in your routed component
is:
```js
{
...
bindings: {
$router: '<'
}
}
```
* The recently added binding of the current router to the current component
has been renamed from `router` to `$router`.
So now the recommended set up for your bindings in your routed component
is:
```js
{
...
bindings: {
$router: '<'
}
}
```
<a name="2.0.0-beta.8"></a>
# 2.0.0-beta.8 (2016-03-02)
### Bug Fixes
* **angular1_router:** rename `$route` service to `$rootRouter` ([a1c3be2](https://github.com/angular/angular/commit/a1c3be2))
* **angular1_router:** rename `router` component binding to `$router` ([edad8e3](https://github.com/angular/angular/commit/edad8e3))
* **angular1_router:** support templateUrl components ([d4a4d81](https://github.com/angular/angular/commit/d4a4d81))
* **change_detection:** allow to destroy `OnPush` components inside of a host event. ([280b86e](https://github.com/angular/angular/commit/280b86e))
* **change_detection:** allow to destroy `OnPush` components inside of a host event. ([ebd438f](https://github.com/angular/angular/commit/ebd438f)), closes [#7192](https://github.com/angular/angular/issues/7192)
* **core:** support `ngFor` that has an `ngIf` as last node ([1779caf](https://github.com/angular/angular/commit/1779caf)), closes [#6304](https://github.com/angular/angular/issues/6304) [#6878](https://github.com/angular/angular/issues/6878)
* **dart/payload:** Fix runtime error in hello_world payload app ([eeb594c](https://github.com/angular/angular/commit/eeb594c)), closes [#7358](https://github.com/angular/angular/issues/7358)
* **differ:** clean up stale identity change refs ([ab36ea0](https://github.com/angular/angular/commit/ab36ea0)), closes [#7193](https://github.com/angular/angular/issues/7193)
* **DomRenderer:** correctly handle namespaced attributes ([c6afea6](https://github.com/angular/angular/commit/c6afea6))
* **Router:** Query strings are copied for HashLocationStrategy ([b47f80e](https://github.com/angular/angular/commit/b47f80e)), closes [#7298](https://github.com/angular/angular/issues/7298)
* **test:** fix a broken test ([9aedef2](https://github.com/angular/angular/commit/9aedef2))
* **transformers:** record reflection info about abstract classes ([05c185a](https://github.com/angular/angular/commit/05c185a)), closes [#7347](https://github.com/angular/angular/issues/7347)
* **transformers:** replace an error with a warning when cannot resolve a symbol ([ee3c580](https://github.com/angular/angular/commit/ee3c580))
* **transformers:** special case types some built-in types, so they can be resolved ([331b9c1](https://github.com/angular/angular/commit/331b9c1))
* **web_worker:** wait for bindings in kitchen sink spec ([4a93f58](https://github.com/angular/angular/commit/4a93f58))
* **web_workers:** make waitForElementText function more stable ([f6a8d04](https://github.com/angular/angular/commit/f6a8d04))
* **WebWorker:** Fix PostMessageBusSink and Source undefined error. ([01fe7f5](https://github.com/angular/angular/commit/01fe7f5)), closes [#7156](https://github.com/angular/angular/issues/7156)
* **WebWorker:** Make MessageBus EventEmitter synchronous ([69c1694](https://github.com/angular/angular/commit/69c1694))
### Features
* **core:** Add `QueryList.forEach` to public api. ([e7470d5](https://github.com/angular/angular/commit/e7470d5))
* **core:** Add `QueryList#forEach` ([b634a25](https://github.com/angular/angular/commit/b634a25))
* **core:** add more debug APIs to inspect the application form a browser ([b5e6319](https://github.com/angular/angular/commit/b5e6319)), closes [#7045](https://github.com/angular/angular/issues/7045) [#7161](https://github.com/angular/angular/issues/7161)
* **core:** drop `ChangeDetectionStrategy.OnPushObserve` ([f60fa14](https://github.com/angular/angular/commit/f60fa14))
* **di:** drop support for injecting types with generics in Dart ([c9a3df9](https://github.com/angular/angular/commit/c9a3df9)), closes [#7262](https://github.com/angular/angular/issues/7262)
* **forms/validators:** pattern validator ([38cb526](https://github.com/angular/angular/commit/38cb526)), closes [#5561](https://github.com/angular/angular/issues/5561)
* **i18n:** added i18nPlural and i18nSelect pipes ([59629a0](https://github.com/angular/angular/commit/59629a0)), closes [#7268](https://github.com/angular/angular/issues/7268)
* **pipes:** add ReplacePipe for string manipulation ([6ef2121](https://github.com/angular/angular/commit/6ef2121))
* **test:** add withProviders for per test providers ([c1a0af5](https://github.com/angular/angular/commit/c1a0af5)), closes [#5128](https://github.com/angular/angular/issues/5128)
* **transformers:** collect data needed for the template compiler ([ebe531b](https://github.com/angular/angular/commit/ebe531b)), closes [#7299](https://github.com/angular/angular/issues/7299)
* **transformers:** collect information for CompileDiDependencyMetadata ([39b6e0e](https://github.com/angular/angular/commit/39b6e0e))
* **transformers:** makes the map of resolved identifiers configurable ([0bb10d6](https://github.com/angular/angular/commit/0bb10d6)), closes [#7359](https://github.com/angular/angular/issues/7359)
### BREAKING CHANGES
* `OnPushObserve` was an experimental
feature for Dart and had
conceptual performance problems,
as setting up observables is slow.
Use `OnPush` instead.
* In Dart we used to support injecting types with generics. As this feature is hard to implement with the upcoming codegen we are dropping it.
Merge cl/115454020 in G3 with this change.
* The `$router` injectable service has been renamed to `$rootRouter`
* The recently added binding of the current router to the current component
has been renamed from `router` to `$router`.
So now the recommended set up for your bindings in your routed component
is:
```js
{
...
bindings: {
$router: '<'
}
}
```
<a name="2.0.0-beta.7"></a>
# 2.0.0-beta.7 (2016-02-18)
### Bug Fixes
* **angular_1_router:** Added DI string tokens ([3478d5d](https://github.com/angular/angular/commit/3478d5d)), closes [#4269](https://github.com/angular/angular/issues/4269) [#7031](https://github.com/angular/angular/issues/7031)
* **typing:** Remove re-export of the Promise built-in type. ([265703b](https://github.com/angular/angular/commit/265703b)), closes [#6468](https://github.com/angular/angular/issues/6468)
<a name="2.0.0-beta.6"></a>
# 2.0.0-beta.6 (2016-02-11)
### Bug Fixes
* **angular1-router:** add missing wrapper methods ([55122cd](https://github.com/angular/angular/commit/55122cd)), closes [#6763](https://github.com/angular/angular/issues/6763) [#6861](https://github.com/angular/angular/issues/6861) [#6861](https://github.com/angular/angular/issues/6861)
* **angular1-router:** add support for using the component helper ([d86be24](https://github.com/angular/angular/commit/d86be24)), closes [angular/angular.js#13860](https://github.com/angular/angular.js/issues/13860) [#6076](https://github.com/angular/angular/issues/6076) [#5278](https://github.com/angular/angular/issues/5278)
* **async:** handle synchronous initial value in async pipe ([26e60d6](https://github.com/angular/angular/commit/26e60d6)), closes [#5996](https://github.com/angular/angular/issues/5996)
* **build:** don't try to copy .d.ts files into the npm distro ([16b5217](https://github.com/angular/angular/commit/16b5217)), closes [#6921](https://github.com/angular/angular/issues/6921)
* **compiler:** fix interpolation regexp ([9b0e10e](https://github.com/angular/angular/commit/9b0e10e)), closes [#6056](https://github.com/angular/angular/issues/6056)
* **compiler:** use event names for matching directives ([231773e](https://github.com/angular/angular/commit/231773e)), closes [#6870](https://github.com/angular/angular/issues/6870)
* **core:** add detail to dehydrated detector exception ([e7ad03c](https://github.com/angular/angular/commit/e7ad03c)), closes [#6939](https://github.com/angular/angular/issues/6939)
* **core:** mute mode printing in console in prod mode ([74be3d3](https://github.com/angular/angular/commit/74be3d3)), closes [#6873](https://github.com/angular/angular/issues/6873)
* **di:** throw if a token uses more than 20 dependencies. ([de77700](https://github.com/angular/angular/commit/de77700)), closes [#6690](https://github.com/angular/angular/issues/6690) [#6869](https://github.com/angular/angular/issues/6869)
* **forms:** add RadioButtonValueAccessor to the list of default value accessors ([8f47aa3](https://github.com/angular/angular/commit/8f47aa3))
* **forms:** add support for radio buttons ([e725542](https://github.com/angular/angular/commit/e725542)), closes [#6877](https://github.com/angular/angular/issues/6877)
* **forms:** use strict runtimeType checks instead of instanceof ([50548fb](https://github.com/angular/angular/commit/50548fb)), closes [#6981](https://github.com/angular/angular/issues/6981)
* **Headers:** serializable toJSON ([b55f176](https://github.com/angular/angular/commit/b55f176)), closes [#6073](https://github.com/angular/angular/issues/6073) [#6714](https://github.com/angular/angular/issues/6714)
* **ngFor:** update view locals if identity changes ([0f10624](https://github.com/angular/angular/commit/0f10624)), closes [#6923](https://github.com/angular/angular/issues/6923)
* **router:** Added route data to normalized async route ([df7885c](https://github.com/angular/angular/commit/df7885c)), closes [#6802](https://github.com/angular/angular/issues/6802)
* **router:** don't prepend `/` unnecessarily to Location paths ([c603643](https://github.com/angular/angular/commit/c603643)), closes [#6729](https://github.com/angular/angular/issues/6729) [#5502](https://github.com/angular/angular/issues/5502)
* **router:** fix incorrect url param value coercion of 1 to true ([995a9e0](https://github.com/angular/angular/commit/995a9e0)), closes [#5346](https://github.com/angular/angular/issues/5346) [#6286](https://github.com/angular/angular/issues/6286)
* **router:** fix url path for star segment in path recognizer ([6f1ef33](https://github.com/angular/angular/commit/6f1ef33)), closes [#6976](https://github.com/angular/angular/issues/6976)
* **router:** fixed the location wrapper for angular1 ([e73fee7](https://github.com/angular/angular/commit/e73fee7)), closes [#6943](https://github.com/angular/angular/issues/6943)
* **typings:** Don't expose typing dependencies to users. ([2a70f4e](https://github.com/angular/angular/commit/2a70f4e)), closes [#5973](https://github.com/angular/angular/issues/5973) [#5807](https://github.com/angular/angular/issues/5807) [#6266](https://github.com/angular/angular/issues/6266) [#5242](https://github.com/angular/angular/issues/5242) [#6817](https://github.com/angular/angular/issues/6817) [#6267](https://github.com/angular/angular/issues/6267)
* **upgrade:** fix infinite $rootScope.$digest() ([7e0f02f](https://github.com/angular/angular/commit/7e0f02f)), closes [#6385](https://github.com/angular/angular/issues/6385) [#6386](https://github.com/angular/angular/issues/6386)
* **Validators:** fix Validators.required marking number zero as invalid ([c2ceb7f](https://github.com/angular/angular/commit/c2ceb7f)), closes [#6617](https://github.com/angular/angular/issues/6617)
* **WebWorkers:** Fix flaky WebWorker test ([da1fcfd](https://github.com/angular/angular/commit/da1fcfd)), closes [#6851](https://github.com/angular/angular/issues/6851)
### Features
* **angular1_router:** allow component to bind to router ([0f22dce](https://github.com/angular/angular/commit/0f22dce))
* **typings:** install es6-shim typings to a location users can reference. ([f1f5b45](https://github.com/angular/angular/commit/f1f5b45))
### BREAKING CHANGES
Transitive typings are no longer included in the distribution.
If you use `--target=es5`, you will need to add a line somewhere in your
application (for example, at the top of the `.ts` file where you call `bootstrap`):
```
///<reference path="node_modules/angular2/typings/browser.d.ts"/>
```
(Note that if your file is not in the same directory as `node_modules`, you'll
need to add one or more `../` to the start of that path.)
If you have unit tests, you need to install typings in your project using
http://github.com/typings/typings
And install typings such as `jasmine`, `angular-protractor`, or `selenium-webdriver`
to satisfy the type-checker.
If you rely on es6 APIs other than Promises and Collections, you will need to
install the es6-shim typing instead of using the <reference> tag above.
Angular previously exposed typings for the entire ES6 API.
<a name="2.0.0-beta.5"></a>
# 2.0.0-beta.5 (2016-02-10)
This release was incorrect; replaced with beta.6.
<a name="2.0.0-beta.4"></a>
# 2.0.0-beta.4 (2016-02-10)
This release was incorrect; replaced with beta.6.
<a name="2.0.0-beta.3"></a>
# 2.0.0-beta.3 (2016-02-03)
@ -27,6 +225,52 @@
* **dart/transform:** Only process deferred libs when necessary ([f56df65](https://github.com/angular/angular/commit/f56df65)), closes [#6745](https://github.com/angular/angular/issues/6745)
### BREAKING CHANGES
This is a breaking change for unit tests. The API for the DebugElement
has changed. Now, there is a DebugElement or DebugNode for every node
in the DOM, not only nodes with an ElementRef. `componentViewChildren` is
removed, and `childNodes` is a list of ElementNodes corresponding to every
child in the DOM. `query` no longer takes a scope parameter, since
the entire rendered DOM is included in the `childNodes`.
Before:
```
componentFixture.debugElement.componentViewChildren[0];
```
After
```
// Depending on the DOM structure of your component, the
// index may have changed or the first component child
// may be a sub-child.
componentFixture.debugElement.children[0];
```
Before:
```
debugElement.query(By.css('div'), Scope.all());
```
After:
```
debugElement.query(By.css('div'));
```
Before:
```
componentFixture.debugElement.elementRef;
```
After:
```
componentFixture.elementRef;
```
<a name="2.0.0-beta.2"></a>
# 2.0.0-beta.2 (2016-01-28)

View File

@ -180,8 +180,8 @@ Must be one of the following:
* **refactor**: A code change that neither fixes a bug nor adds a feature
* **perf**: A code change that improves performance
* **test**: Adding missing tests or correcting existing tests
* **build**: Changes that affect the build system, CI configuration or external dependencies (example scopes: gulp, broccoli, npm)
* **ci**: Any changes to our CI configuration files and scripts (Travis, Circle CI, BrowserStack, SauceLabs)
* **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
* **ci**: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
* **chore**: Other changes that don't modify `src` or `test` files
### Scope

View File

@ -4,7 +4,6 @@
var CIconfiguration = {
'Chrome': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
'Firefox': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
'ChromeBeta': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
'FirefoxBeta': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
'ChromeDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
'FirefoxDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}},

View File

@ -344,9 +344,8 @@ gulp.task('lint', ['build.tools'], function() {
gulp.task('build/checkCircularDependencies', function(done) {
var madge = require('madge');
var dependencyObject = madge(CONFIG.dest.js.dev.es5, {
var dependencyObject = madge([CONFIG.dest.js.dev.es5], {
format: 'cjs',
paths: [CONFIG.dest.js.dev.es5],
extensions: ['.js'],
onParseFile: function(data) { data.src = data.src.replace(/\/\* circular \*\//g, "//"); }
});
@ -468,7 +467,7 @@ gulp.task('test.js', function(done) {
gulp.task('test.dart', function(done) {
runSequence('versions.dart', 'test.transpiler.unittest', 'test.unit.dart/ci',
'test.dart.angular2_testing/ci', sequenceComplete(done));
sequenceComplete(done));
});
gulp.task('versions.dart', function() { dartSdk.logVersion(DART_SDK); });
@ -636,8 +635,7 @@ gulp.task('buildRouter.dev', function() {
gulp.task('test.unit.dart', function(done) {
printModulesWarning();
runSequence('build/tree.dart', 'build/pure-packages.dart', '!build/pubget.angular2.dart',
'!build/change_detect.dart', '!build/remove-pub-symlinks', 'build.dart.material.css',
'!test.unit.dart/karma-server', '!test.unit.dart/karma-run', function(error) {
'!build/change_detect.dart', '!build/remove-pub-symlinks', function(error) {
var watch = require('./tools/build/watch');
// if initial build failed (likely due to build or formatting step) then exit
@ -646,9 +644,10 @@ gulp.task('test.unit.dart', function(done) {
done(error);
return;
}
// treatTestErrorsAsFatal = false;
watch(['modules/angular2/**'], {ignoreInitial: true},
['!build/tree.dart', '!test.unit.dart/karma-run']);
watch(['modules/angular2/**'],
['!build/tree.dart', '!test.unit.dart/run/angular2']);
});
});
@ -790,20 +789,6 @@ gulp.task('watch.dart.dev', function(done) {
});
});
gulp.task('!test.unit.dart/karma-run', function(done) {
// run the run command in a new process to avoid duplicate logging by both server and runner from
// a single process
runKarma('karma-dart.conf.js', done);
});
gulp.task('!test.unit.dart/karma-server', function() {
var karma = require('karma');
new karma.Server({configFile: __dirname + '/karma-dart.conf.js', reporters: 'dots'}).start();
});
gulp.task('test.unit.router/ci', function(done) {
var karma = require('karma');
@ -851,20 +836,56 @@ gulp.task('test.unit.js.browserstack/ci', function(done) {
});
gulp.task('test.unit.dart/ci', function(done) {
var karma = require('karma');
var browserConf = getBrowsersFromCLI(null, true);
new karma.Server(
{
configFile: __dirname + '/karma-dart.conf.js',
singleRun: true,
reporters: ['dots'],
browsers: browserConf.browsersToRun
},
done)
.start();
runSequence('test.dart.dartium_symlink', '!test.unit.dart/run/angular2',
'!test.unit.dart/run/angular2_testing', '!test.unit.dart/run/benchpress',
sequenceComplete(done));
});
// At the moment, dart test requires dartium to be an executable on the path.
// Make a temporary directory and symlink dartium from there (just for this command)
// so that it can run.
// TODO(juliemr): this won't work with windows - remove the hack and make this platform agnostic.
var dartiumTmpdir = path.join(os.tmpdir(), 'dartium' + new Date().getTime().toString());
var dartiumPathPrefix = 'PATH=$PATH:' + dartiumTmpdir + ' ';
gulp.task(
'test.dart.dartium_symlink',
shell.task(['mkdir ' + dartiumTmpdir, 'ln -s $DARTIUM_BIN ' + dartiumTmpdir + '/dartium']));
gulp.task('!test.unit.dart/run/angular2', function() {
var pubtest = require('./tools/build/pubtest');
return pubtest({
dir: path.join(CONFIG.dest.dart, 'angular2'),
dartiumTmpdir: dartiumTmpdir,
command: DART_SDK.PUB,
files: '**/*_spec.dart',
bunchFiles: true,
useExclusiveTests: true
});
});
gulp.task('!test.unit.dart/run/angular2_testing', function() {
var pubtest = require('./tools/build/pubtest');
return pubtest({
dir: path.join(CONFIG.dest.dart, 'angular2_testing'),
dartiumTmpdir: dartiumTmpdir,
command: DART_SDK.PUB,
files: '**/*_test.dart',
useExclusiveTests: true
});
});
gulp.task('!test.unit.dart/run/benchpress', function() {
var pubtest = require('./tools/build/pubtest');
return pubtest({
dir: path.join(CONFIG.dest.dart, 'benchpress'),
dartiumTmpdir: dartiumTmpdir,
command: DART_SDK.PUB,
files: '**/*_spec.dart',
useExclusiveTests: true
});
});
gulp.task('test.unit.cjs/ci', function(done) {
runJasmineTests(['dist/js/cjs/{angular2,benchpress}/test/**/*_spec.js'], done);
@ -931,24 +952,6 @@ gulp.task('test.server.dart', runServerDartTests(gulp, gulpPlugins, {dest: 'dist
gulp.task('test.transpiler.unittest',
function(done) { runJasmineTests(['tools/transpiler/unittest/**/*.js'], done); });
// At the moment, dart test requires dartium to be an executable on the path.
// Make a temporary directory and symlink dartium from there (just for this command)
// so that it can run.
var dartiumTmpdir = path.join(os.tmpdir(), 'dartium' + new Date().getTime().toString());
gulp.task('test.dart.angular2_testing/ci', ['build/pubspec.dart'], function(done) {
runSequence('test.dart.angular2_testing_symlink', 'test.dart.angular2_testing',
sequenceComplete(done));
});
gulp.task(
'test.dart.angular2_testing_symlink',
shell.task(['mkdir ' + dartiumTmpdir, 'ln -s $DARTIUM_BIN ' + dartiumTmpdir + '/dartium']));
gulp.task('test.dart.angular2_testing',
shell.task(['PATH=$PATH:' + dartiumTmpdir + ' pub run test -p dartium'],
{'cwd': 'dist/dart/angular2_testing'}));
// -----------------
// Pre-test checks
@ -981,15 +984,16 @@ gulp.task('!pre.test.typings.layoutNodeModule', ['build.js.cjs'], function() {
.pipe(gulp.dest(path.join(tmpdir, 'node_modules')));
});
gulp.task('!pre.test.typings.copyTypingsSpec', function() {
return gulp.src(['typing_spec/*.ts'], {base: 'typing_spec'}).pipe(gulp.dest(path.join(tmpdir)));
return gulp.src(['typing_spec/*.ts'], {base: 'typing_spec'}).pipe(gulp.dest(tmpdir));
});
gulp.task('test.typings',
['!pre.test.typings.layoutNodeModule', '!pre.test.typings.copyTypingsSpec'], function() {
var tsc = require('gulp-typescript');
return gulp.src([tmpdir + '/**'])
return gulp.src([tmpdir + '/*.ts'])
.pipe(tsc({
target: 'ES5',
target: 'ES6',
module: 'commonjs',
experimentalDecorators: true,
noImplicitAny: true,
@ -1017,6 +1021,7 @@ gulp.task('build/pure-packages.dart/standalone', function() {
'modules_dart/**/*',
'!modules_dart/**/*.proto',
'!modules_dart/**/packages{,/**}',
'!modules_dart/**/.packages',
'!modules_dart/payload{,/**}',
'!modules_dart/transform{,/**}',
])

View File

@ -1,86 +0,0 @@
// This module provides a customFileHandler for karma
// that serves files with urls like /packages_<timestamp>/...
// with maximum cache.
// We are using these urls when we spawn isolates
// so that the isolates don't reload files every time.
var common = require('karma/lib/middleware/common');
var fs = require('fs');
var DART_EVAL_PATH_RE = /.*\/packages_\d+\/(.*)$/;
module.exports = createFactory;
function createFactory(proxyPaths) {
return {
'framework:dart-evalcache': ['factory', dartEvalCacheFactory]
};
function dartEvalCacheFactory(emitter, logger, customFileHandlers) {
var filesPromise = new common.PromiseContainer();
emitter.on('file_list_modified', function(files) {
filesPromise.set(Promise.resolve(files));
});
var serveFile = common.createServeFile(fs);
var log = logger.create('dart-evalcache');
customFileHandlers.push({
urlRegex: DART_EVAL_PATH_RE,
handler: handler
});
// See source_files handler
function handler(request, response, fa, fb, basePath) {
return filesPromise.then(function(files) {
try {
var requestedFilePath = mapUrlToFile(request.url, proxyPaths, basePath, log);
// TODO(vojta): change served to be a map rather then an array
var file = findByPath(files.served, requestedFilePath);
if (file) {
serveFile(file.contentPath || file.path, response, function() {
common.setHeavyCacheHeaders(response);
}, file.content);
} else {
response.writeHead(404);
response.end('Not found');
}
} catch (e) {
log.error(e.stack);
response.writeHead(500);
response.end('Error', e.stack);
}
});
}
};
}
function mapUrlToFile(url, proxyPaths, basePath, log) {
var originalUrl = url;
url = url.indexOf('?') > -1 ? url.substring(0, url.indexOf('?')) : url;
var match = DART_EVAL_PATH_RE.exec(url);
var packagePath = match[1];
var result = null;
var lastProxyFromLength = 0;
Object.keys(proxyPaths).forEach(function(proxyFrom) {
if (startsWith(packagePath, proxyFrom) && proxyFrom.length > lastProxyFromLength) {
lastProxyFromLength = proxyFrom.length;
result = proxyPaths[proxyFrom] + packagePath.substring(proxyFrom.length);
}
});
return basePath + '/' + result;
}
function startsWith(string, subString) {
return string.length >= subString.length && string.slice(0, subString.length) === subString;
}
function findByPath(files, path) {
for (var i = 0; i < files.length; i++) {
if (files[i].path === path) {
return files[i];
}
}
return null;
}

View File

@ -1,82 +0,0 @@
var browserProvidersConf = require('./browser-providers.conf.js');
var packageSources = {
// Dependencies installed with `pub install`.
'unittest': 'packages/unittest',
'guinness': 'packages/guinness',
'matcher': 'packages/matcher',
'stack_trace': 'packages/stack_trace',
'collection': 'packages/collection',
'path': 'packages/path',
'observe': 'packages/observe',
'quiver': 'packages/quiver',
'intl': 'packages/intl',
'smoke': 'packages/smoke',
'logging': 'packages/logging',
'utf': 'packages/utf',
// Local dependencies, transpiled from the source.
'angular2': 'dist/dart/angular2/lib',
'angular2/test/': 'dist/dart/angular2/test/',
'http': 'dist/dart/http/lib',
'angular2_material': 'dist/dart/angular2_material/lib',
'benchpress': 'dist/dart/benchpress/lib',
'examples': 'dist/dart/examples/lib'
};
var proxyPaths = {};
Object.keys(packageSources).map(function(packageName) {
var filePath = packageSources[packageName];
proxyPaths['/packages/'+packageName] = '/base/'+filePath;
});
// Karma configuration
// Generated on Thu Sep 25 2014 11:52:02 GMT-0700 (PDT)
module.exports = function(config) {
config.set({
frameworks: ['dart-unittest', 'dart-evalcache'],
files: [
// Init and configure guiness.
{pattern: 'test-init.dart', included: true},
// Unit test files needs to be included.
{pattern: 'dist/dart/**/*_spec.dart', included: true, watched: false},
// Karma-dart via the dart-unittest framework generates
// `__adapter_unittest.dart` that imports these files.
{pattern: 'dist/dart/**', included: false, watched: false},
// Dependencies, installed with `pub install`.
{pattern: 'packages/**/*.dart', included: false, watched: false},
// Init and configure guiness.
{pattern: 'test-main.dart', included: true},
{pattern: 'modules/**/test/**/static_assets/**', included: false, watched: false},
],
exclude: [
'dist/dart/**/packages/**',
'modules/angular1_router/**'
],
karmaDartImports: {
guinness: 'package:guinness/guinness_html.dart'
},
// Map packages to the correct urls where Karma serves them.
proxies: proxyPaths,
customLaunchers: browserProvidersConf.customLaunchers,
browsers: ['DartiumWithWebPlatform'],
port: 9877,
plugins: [
require('karma-dart'),
require('karma-chrome-launcher'),
require('karma-sauce-launcher'),
require('./karma-dart-evalcache')(packageSources)
]
});
};

View File

@ -4,17 +4,20 @@ var fs = require('fs');
var ts = require('typescript');
var files = [
'lifecycle_annotations_impl.ts',
'utils.ts',
'url_parser.ts',
'route_recognizer.ts',
'route_config_impl.ts',
'async_route_handler.ts',
'sync_route_handler.ts',
'component_recognizer.ts',
'lifecycle/lifecycle_annotations_impl.ts',
'lifecycle/route_lifecycle_reflector.ts',
'route_config/route_config_impl.ts',
'route_config/route_config_normalizer.ts',
'rules/route_handlers/async_route_handler.ts',
'rules/route_handlers/sync_route_handler.ts',
'rules/rules.ts',
'rules/rule_set.ts',
'rules/route_paths/route_path.ts',
'rules/route_paths/param_route_path.ts',
'rules/route_paths/regex_route_path.ts',
'instruction.ts',
'path_recognizer.ts',
'route_config_nomalizer.ts',
'route_lifecycle_reflector.ts',
'route_registry.ts',
'router.ts'
];
@ -48,9 +51,10 @@ function main(modulesDirectory) {
*/
var IMPORT_RE = new RegExp("import \\{?([\\w\\n_, ]+)\\}? from '(.+)';?", 'g');
var INJECT_RE = new RegExp("@Inject\\(ROUTER_PRIMARY_COMPONENT\\)", 'g');
var IMJECTABLE_RE = new RegExp("@Injectable\\(\\)", 'g');
var INJECTABLE_RE = new RegExp("@Injectable\\(\\)", 'g');
var REQUIRE_RE = new RegExp("require\\('(.*?)'\\);", 'g');
function transform(contents) {
contents = contents.replace(INJECT_RE, '').replace(IMJECTABLE_RE, '');
contents = contents.replace(INJECT_RE, '').replace(INJECTABLE_RE, '');
contents = contents.replace(IMPORT_RE, function (match, imports, includePath) {
//TODO: remove special-case
if (isFacadeModule(includePath) || includePath === './router_outlet') {
@ -58,10 +62,15 @@ function transform(contents) {
}
return match;
});
return ts.transpile(contents, {
contents = ts.transpile(contents, {
target: ts.ScriptTarget.ES5,
module: ts.ModuleKind.CommonJS
});
// Rename require functions from transpiled imports
contents = contents.replace(REQUIRE_RE, 'routerRequire(\'$1\');');
return contents;
}
function isFacadeModule(modulePath) {

View File

@ -12,9 +12,9 @@
<script src="../../dist/angular_1_router.js"></script>
<script>
angular.module('myApp', ['ngComponentRouter'])
.controller('MyCtrl', ['$router', function ($router) {
console.log($router);
$router.navigateByUrl('/')
.controller('MyCtrl', ['$rootRouter', function ($rootRouter) {
console.log($rootRouter);
$rootRouter.navigateByUrl('/')
.then(console.log.bind(console, 'resolve'), console.log.bind(console, 'reject'));
}]);
</script>

View File

@ -173,6 +173,10 @@ var StringMapWrapper = {
var List = Array;
var ListWrapper = {
toJSON: function(l) {
return JSON.stringify(l);
},
clear: function (l) {
l.length = 0;
},
@ -247,6 +251,10 @@ var ListWrapper = {
};
var StringWrapper = {
charCodeAt: function(s, i) {
return s.charCodeAt(i);
},
equals: function (s1, s2) {
return s1 === s2;
},
@ -303,8 +311,8 @@ Location.prototype.subscribe = function () {
//TODO: implement
};
Location.prototype.path = function () {
return $location.path();
return $location.url();
};
Location.prototype.go = function (url) {
return $location.path(url);
Location.prototype.go = function (path, query) {
return $location.url(path + query);
};

View File

@ -4,7 +4,7 @@ angular.module('ngComponentRouter').
// Because Angular 1 has no notion of a root component, we use an object with unique identity
// to represent this. Can be overloaded with a component name
value('$routerRootComponent', new Object()).
factory('$router', ['$q', '$location', '$$directiveIntrospector', '$browser', '$rootScope', '$injector', '$routerRootComponent', routerFactory]);
factory('$rootRouter', ['$q', '$location', '$$directiveIntrospector', '$browser', '$rootScope', '$injector', '$routerRootComponent', routerFactory]);
function routerFactory($q, $location, $$directiveIntrospector, $browser, $rootScope, $injector, $routerRootComponent) {
@ -17,7 +17,7 @@ function routerFactory($q, $location, $$directiveIntrospector, $browser, $rootSc
OpaqueToken: function () {},
Inject: function () {}
};
var require = function () {return exports;};
var routerRequire = function () {return exports;};
// When this file is processed, the line below is replaced with
// the contents of the compiled TypeScript classes.
@ -57,7 +57,7 @@ function routerFactory($q, $location, $$directiveIntrospector, $browser, $rootSc
});
var router = new RootRouter(registry, location, $routerRootComponent);
$rootScope.$watch(function () { return $location.path(); }, function (path) {
$rootScope.$watch(function () { return $location.url(); }, function (path) {
if (router.lastNavigationAttempt !== path) {
router.navigateByUrl(path);
}

View File

@ -61,8 +61,8 @@ class DirectiveIntrospectorProvider {
*
* The value for the `ngOutlet` attribute is optional.
*/
function ngOutletDirective($animate, $q: ng.IQService, $router) {
let rootRouter = $router;
function ngOutletDirective($animate, $q: ng.IQService, $rootRouter) {
let rootRouter = $rootRouter;
return {
restrict: 'AE',
@ -145,7 +145,7 @@ function ngOutletDirective($animate, $q: ng.IQService, $router) {
}
activate(instruction) {
let previousInstruction = this.currentInstruction;
this.previousInstruction = this.currentInstruction;
this.currentInstruction = instruction;
let componentName = this.controller.$$componentName = instruction.componentType;
@ -154,11 +154,14 @@ function ngOutletDirective($animate, $q: ng.IQService, $router) {
throw new Error('Component is not a string for ' + instruction.urlPath);
}
this.controller.$$routeParams = instruction.params;
this.controller.$$template = '<div ' + dashCase(componentName) + '></div>';
this.controller.$$template = '<' + dashCase(componentName) + ' $router="::$$router"></' +
dashCase(componentName) + '>';
this.controller.$$router = this.router.childRouter(instruction.componentType);
this.controller.$$outlet = this;
let newScope = scope.$new();
newScope.$$router = this.controller.$$router;
this.deferredActivation = $q.defer();
let clone = $transclude(newScope, clone => {
$animate.enter(clone, null, this.currentElement || element);
@ -167,15 +170,7 @@ function ngOutletDirective($animate, $q: ng.IQService, $router) {
this.currentElement = clone;
this.currentScope = newScope;
// TODO: prefer the other directive retrieving the controller
// by debug mode
this.currentController = this.currentElement.children().eq(0).controller(componentName);
if (this.currentController && this.currentController.$routerOnActivate) {
return this.currentController.$routerOnActivate(instruction, previousInstruction);
}
return $q.when();
return this.deferredActivation.promise;
}
}
@ -198,21 +193,32 @@ function ngOutletFillContentDirective($compile) {
link: (scope, element, attrs, ctrl) => {
let template = ctrl.$$template;
element.html(template);
let link = $compile(element.contents());
link(scope);
// TODO: move to primary directive
let componentInstance = scope[ctrl.$$componentName];
if (componentInstance) {
ctrl.$$currentComponent = componentInstance;
componentInstance.$router = ctrl.$$router;
componentInstance.$routeParams = ctrl.$$routeParams;
}
$compile(element.contents())(scope);
}
};
}
function routerTriggerDirective($q) {
return {
require: '^ngOutlet',
priority: -1000,
link: function(scope, element, attr, ngOutletCtrl) {
var promise = $q.when();
var outlet = ngOutletCtrl.$$outlet;
var currentComponent = outlet.currentController =
element.controller(ngOutletCtrl.$$componentName);
if (currentComponent.$routerOnActivate) {
promise = $q.when(currentComponent.$routerOnActivate(outlet.currentInstruction,
outlet.previousInstruction));
}
promise.then(outlet.deferredActivation.resolve, outlet.deferredActivation.reject);
}
};
}
/**
* @name ngLink
* @description
@ -225,8 +231,8 @@ function ngOutletFillContentDirective($compile) {
*
* ```js
* angular.module('myApp', ['ngComponentRouter'])
* .controller('AppController', ['$router', function($router) {
* $router.config({ path: '/user/:id', component: 'user' });
* .controller('AppController', ['$rootRouter', function($rootRouter) {
* $rootRouter.config({ path: '/user/:id', component: 'user' });
* this.user = { name: 'Brian', id: 123 };
* });
* ```
@ -237,13 +243,11 @@ function ngOutletFillContentDirective($compile) {
* </div>
* ```
*/
function ngLinkDirective($router, $parse) {
let rootRouter = $router;
function ngLinkDirective($rootRouter, $parse) {
return {require: '?^^ngOutlet', restrict: 'A', link: ngLinkDirectiveLinkFn};
function ngLinkDirectiveLinkFn(scope, element, attrs, ctrl) {
let router = (ctrl && ctrl.$$router) || rootRouter;
let router = (ctrl && ctrl.$$router) || $rootRouter;
if (!router) {
return;
}
@ -253,6 +257,16 @@ function ngLinkDirective($router, $parse) {
function getLink(params) {
instruction = router.generate(params);
scope.$watch(function() { return router.isRouteActive(instruction); },
function(active) {
if (active) {
element.addClass('ng-link-active');
} else {
element.removeClass('ng-link-active');
}
});
return './' + angular.stringifyInstruction(instruction);
}
@ -271,7 +285,7 @@ function ngLinkDirective($router, $parse) {
return;
}
$router.navigateByInstruction(instruction);
$rootRouter.navigateByInstruction(instruction);
event.preventDefault();
});
}
@ -285,13 +299,14 @@ function dashCase(str: string): string {
* A module for adding new a routing system Angular 1.
*/
angular.module('ngComponentRouter', [])
.directive('ngOutlet', ngOutletDirective)
.directive('ngOutlet', ngOutletFillContentDirective)
.directive('ngLink', ngLinkDirective);
.directive('ngOutlet', ['$animate', '$q', '$rootRouter', ngOutletDirective])
.directive('ngOutlet', ['$compile', ngOutletFillContentDirective])
.directive('ngLink', ['$rootRouter', '$parse', ngLinkDirective])
.directive('$router', ['$q', routerTriggerDirective]);
/*
* A module for inspecting controller constructors
*/
angular.module('ng')
.provider('$$directiveIntrospector', DirectiveIntrospectorProvider)
.config(compilerProviderDecorator);
.config(['$compileProvider', '$$directiveIntrospectorProvider', compilerProviderDecorator]);

View File

@ -1,4 +1,4 @@
/** @license Copyright 2014-2015 Google, Inc. http://github.com/angular/angular/LICENSE */
/** @license Copyright 2014-2016 Google, Inc. http://github.com/angular/angular/LICENSE */
(function () {
'use strict';
@ -24,12 +24,12 @@
.directive('a', anchorLinkDirective)
// Connects the legacy $routeProvider config shim to Component Router's config.
.run(['$route', '$router', function ($route, $router) {
.run(['$route', '$rootRouter', function ($route, $rootRouter) {
$route.$$subscribe(function (routeDefinition) {
if (!angular.isArray(routeDefinition)) {
routeDefinition = [routeDefinition];
}
$router.config(routeDefinition);
$rootRouter.config(routeDefinition);
});
}]);
@ -116,53 +116,41 @@
console.warn('Route for "' + path + '" should use "controllerAs".');
}
var directiveName = routeObjToRouteName(routeCopy, path);
var componentName = routeObjToRouteName(routeCopy, path);
if (!directiveName) {
if (!componentName) {
throw new Error('Could not determine a name for route "' + path + '".');
}
routeDefinition.component = directiveName;
routeDefinition.name = route.name || upperCase(directiveName);
routeDefinition.component = componentName;
routeDefinition.name = route.name || upperCase(componentName);
var directiveController = routeCopy.controller;
var directiveDefinition = {
scope: false,
var componentDefinition = {
controller: directiveController,
controllerAs: routeCopy.controllerAs,
templateUrl: routeCopy.templateUrl,
template: routeCopy.template
};
controllerAs: routeCopy.controllerAs
var directiveFactory = function () {
return directiveDefinition;
};
if (routeCopy.templateUrl) componentDefinition.templateUrl = routeCopy.templateUrl;
if (routeCopy.template) componentDefinition.template = routeCopy.template;
// if we have route resolve options, prepare a wrapper controller
if (directiveController && routeCopy.resolve) {
var originalController = directiveController;
var resolvedLocals = {};
directiveDefinition.controller = ['$injector', '$scope', function ($injector, $scope) {
componentDefinition.controller = ['$injector', '$scope', function ($injector, $scope) {
var locals = angular.extend({
$scope: $scope
}, resolvedLocals);
var ctrl = $injector.instantiate(originalController, locals);
if (routeCopy.controllerAs) {
locals.$scope[routeCopy.controllerAs] = ctrl;
}
return ctrl;
return $injector.instantiate(originalController, locals);
}];
// we take care of controllerAs in the directive controller wrapper
delete directiveDefinition.controllerAs;
// we resolve the locals in a canActivate block
directiveFactory.$canActivate = function() {
componentDefinition.$canActivate = function() {
var locals = angular.extend({}, routeCopy.resolve);
angular.forEach(locals, function(value, key) {
@ -179,7 +167,7 @@
}
// register the dynamically created directive
$compileProvider.directive(directiveName, directiveFactory);
$compileProvider.component(componentName, componentDefinition);
}
if (subscriptionFn) {
subscriptionFn(routeDefinition);
@ -253,12 +241,12 @@
}
function $routeParamsFactory($router, $rootScope) {
function $routeParamsFactory($rootRouter, $rootScope) {
// the identity of this object cannot change
var paramsObj = {};
$rootScope.$on('$routeChangeSuccess', function () {
var newParams = $router._currentInstruction && $router._currentInstruction.component.params;
var newParams = $rootRouter.currentInstruction && $rootRouter.currentInstruction.component.params;
angular.forEach(paramsObj, function (val, name) {
delete paramsObj[name];
@ -274,7 +262,7 @@
/**
* Allows normal anchor links to kick off routing.
*/
function anchorLinkDirective($router) {
function anchorLinkDirective($rootRouter) {
return {
restrict: 'E',
link: function (scope, element) {
@ -293,8 +281,8 @@
}
var href = element.attr(hrefAttrName);
if (href && $router.recognize(href)) {
$router.navigateByUrl(href);
if (href && $rootRouter.recognize(href)) {
$rootRouter.navigateByUrl(href);
event.preventDefault();
}
});

View File

@ -5,7 +5,7 @@ describe('ngOutlet animations', function () {
$animate,
$compile,
$rootScope,
$router,
$rootRouter,
$compileProvider;
beforeEach(function () {
@ -17,15 +17,18 @@ describe('ngOutlet animations', function () {
$compileProvider = _$compileProvider_;
});
inject(function (_$animate_, _$compile_, _$rootScope_, _$router_) {
inject(function (_$animate_, _$compile_, _$rootScope_, _$rootRouter_) {
$animate = _$animate_;
$compile = _$compile_;
$rootScope = _$rootScope_;
$router = _$router_;
$rootRouter = _$rootRouter_;
});
registerComponent('userCmp', {
template: '<div>hello {{userCmp.$routeParams.name}}</div>'
template: '<div>hello {{userCmp.$routeParams.name}}</div>',
$routerOnActivate: function(next) {
this.$routeParams = next.params;
}
});
});
@ -38,11 +41,11 @@ describe('ngOutlet animations', function () {
compile('<div ng-outlet></div>');
$router.config([
$rootRouter.config([
{ path: '/user/:name', component: 'userCmp' }
]);
$router.navigateByUrl('/user/brian');
$rootRouter.navigateByUrl('/user/brian');
$rootScope.$digest();
expect(elt.text()).toBe('hello brian');
@ -51,7 +54,7 @@ describe('ngOutlet animations', function () {
expect(item.event).toBe('enter');
// navigate to pete
$router.navigateByUrl('/user/pete');
$rootRouter.navigateByUrl('/user/pete');
$rootScope.$digest();
expect(elt.text()).toBe('hello pete');

View File

@ -4,7 +4,7 @@ describe('Navigation lifecycle', function () {
var elt,
$compile,
$rootScope,
$router,
$rootRouter,
$compileProvider;
beforeEach(function () {
@ -14,10 +14,10 @@ describe('Navigation lifecycle', function () {
$compileProvider = _$compileProvider_;
});
inject(function (_$compile_, _$rootScope_, _$router_) {
inject(function (_$compile_, _$rootScope_, _$rootRouter_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$router = _$router_;
$rootRouter = _$rootRouter_;
});
registerComponent('oneCmp', {
@ -38,12 +38,12 @@ describe('Navigation lifecycle', function () {
$routerOnActivate: spy
});
$router.config([
$rootRouter.config([
{ path: '/a', component: 'activateCmp' }
]);
compile('<div>outer { <div ng-outlet></div> }</div>');
$router.navigateByUrl('/a');
$rootRouter.navigateByUrl('/a');
$rootScope.$digest();
expect(spy).toHaveBeenCalled();
@ -56,12 +56,12 @@ describe('Navigation lifecycle', function () {
$routerOnActivate: spy
});
$router.config([
$rootRouter.config([
{ path: '/user/:name', component: 'userCmp' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/user/brian');
$rootRouter.navigateByUrl('/user/brian');
$rootScope.$digest();
expect(spy).toHaveBeenCalledWith(instructionFor('userCmp'), undefined);
@ -75,15 +75,15 @@ describe('Navigation lifecycle', function () {
$routerOnActivate: spy
});
$router.config([
$rootRouter.config([
{ path: '/user/:name', component: 'oneCmp' },
{ path: '/post/:id', component: 'activateCmp' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/user/brian');
$rootRouter.navigateByUrl('/user/brian');
$rootScope.$digest();
$router.navigateByUrl('/post/123');
$rootRouter.navigateByUrl('/post/123');
$rootScope.$digest();
expect(spy).toHaveBeenCalledWith(instructionFor('activateCmp'),
instructionFor('oneCmp'));
@ -98,12 +98,12 @@ describe('Navigation lifecycle', function () {
}
});
$router.config([
$rootRouter.config([
{ path: '/user', component: 'userCmp' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/user');
$rootRouter.navigateByUrl('/user');
$rootScope.$digest();
expect(injectedScope).toBeDefined();
@ -116,15 +116,15 @@ describe('Navigation lifecycle', function () {
$routerOnDeactivate: spy
});
$router.config([
$rootRouter.config([
{ path: '/a', component: 'deactivateCmp' },
{ path: '/b', component: 'oneCmp' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/a');
$rootRouter.navigateByUrl('/a');
$rootScope.$digest();
$router.navigateByUrl('/b');
$rootRouter.navigateByUrl('/b');
$rootScope.$digest();
expect(spy).toHaveBeenCalled();
});
@ -136,15 +136,15 @@ describe('Navigation lifecycle', function () {
$routerOnDeactivate: spy
});
$router.config([
$rootRouter.config([
{ path: '/user/:name', component: 'deactivateCmp' },
{ path: '/post/:id', component: 'oneCmp' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/user/brian');
$rootRouter.navigateByUrl('/user/brian');
$rootScope.$digest();
$router.navigateByUrl('/post/123');
$rootRouter.navigateByUrl('/post/123');
$rootScope.$digest();
expect(spy).toHaveBeenCalledWith(instructionFor('oneCmp'),
instructionFor('deactivateCmp'));
@ -166,15 +166,15 @@ describe('Navigation lifecycle', function () {
}
});
$router.config([
$rootRouter.config([
{ path: '/a', component: 'deactivateCmp' },
{ path: '/b', component: 'activateCmp' }
]);
compile('outer { <div ng-outlet></div> }');
$router.navigateByUrl('/a');
$rootRouter.navigateByUrl('/a');
$rootScope.$digest();
$router.navigateByUrl('/b');
$rootRouter.navigateByUrl('/b');
$rootScope.$digest();
expect(log).toEqual(['deactivate', 'activate']);
@ -203,19 +203,19 @@ describe('Navigation lifecycle', function () {
}
});
$router.config([
$rootRouter.config([
{ path: '/on-reuse/:number/...', component: 'reuseCmp' },
{ path: '/two', component: 'twoCmp', name: 'Two'}
]);
compile('outer { <div ng-outlet></div> }');
$router.navigateByUrl('/on-reuse/1/a');
$rootRouter.navigateByUrl('/on-reuse/1/a');
$rootScope.$digest();
expect(log).toEqual([]);
expect(cmpInstanceCount).toBe(1);
expect(elt.text()).toBe('outer { reuse {one} }');
$router.navigateByUrl('/on-reuse/2/b');
$rootRouter.navigateByUrl('/on-reuse/2/b');
$rootScope.$digest();
expect(log).toEqual(['reuse: on-reuse/1 -> on-reuse/2']);
expect(cmpInstanceCount).toBe(1);
@ -245,19 +245,19 @@ describe('Navigation lifecycle', function () {
}
});
$router.config([
$rootRouter.config([
{ path: '/never-reuse/:number/...', component: 'reuseCmp' },
{ path: '/two', component: 'twoCmp', name: 'Two'}
]);
compile('outer { <div ng-outlet></div> }');
$router.navigateByUrl('/never-reuse/1/a');
$rootRouter.navigateByUrl('/never-reuse/1/a');
$rootScope.$digest();
expect(log).toEqual([]);
expect(cmpInstanceCount).toBe(1);
expect(elt.text()).toBe('outer { reuse {one} }');
$router.navigateByUrl('/never-reuse/2/b');
$rootRouter.navigateByUrl('/never-reuse/2/b');
$rootScope.$digest();
expect(log).toEqual([]);
expect(cmpInstanceCount).toBe(2);
@ -274,12 +274,12 @@ describe('Navigation lifecycle', function () {
$routerOnActivate: spy
});
$router.config([
$rootRouter.config([
{ path: '/a', component: 'activateCmp' }
]);
compile('outer { <div ng-outlet></div> }');
$router.navigateByUrl('/a');
$rootRouter.navigateByUrl('/a');
$rootScope.$digest();
expect(spy).not.toHaveBeenCalled();
@ -296,12 +296,12 @@ describe('Navigation lifecycle', function () {
$routerOnActivate: activateSpy
});
$router.config([
$rootRouter.config([
{ path: '/a', component: 'activateCmp' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/a');
$rootRouter.navigateByUrl('/a');
$rootScope.$digest();
expect(canActivateSpy).toHaveBeenCalled();
@ -320,12 +320,12 @@ describe('Navigation lifecycle', function () {
$routerOnActivate: spy
});
$router.config([
$rootRouter.config([
{ path: '/a', component: 'activateCmp' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/a');
$rootRouter.navigateByUrl('/a');
$rootScope.$digest();
expect(spy).toHaveBeenCalled();
@ -341,17 +341,17 @@ describe('Navigation lifecycle', function () {
spy.$inject = ['$nextInstruction', '$http'];
$router.config([
$rootRouter.config([
{ path: '/user/:name', component: 'activateCmp' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/user/brian');
$rootRouter.navigateByUrl('/user/brian');
$rootScope.$digest();
expect(spy).toHaveBeenCalled();
var args = spy.calls.mostRecent().args;
expect(args[0].params).toEqual({name: 'brian'});
expect(args[0].params).toEqual(jasmine.objectContaining({name: 'brian'}));
expect(args[1]).toBe($http);
}));
@ -364,17 +364,17 @@ describe('Navigation lifecycle', function () {
}
});
$router.config([
$rootRouter.config([
{ path: '/a', component: 'activateCmp' },
{ path: '/b', component: 'oneCmp' }
]);
compile('outer { <div ng-outlet></div> }');
$router.navigateByUrl('/a');
$rootRouter.navigateByUrl('/a');
$rootScope.$digest();
expect(elt.text()).toBe('outer { hi }');
$router.navigateByUrl('/b');
$rootRouter.navigateByUrl('/b');
$rootScope.$digest();
expect(elt.text()).toBe('outer { hi }');
});
@ -388,17 +388,17 @@ describe('Navigation lifecycle', function () {
}
});
$router.config([
$rootRouter.config([
{ path: '/a', component: 'activateCmp' },
{ path: '/b', component: 'oneCmp' }
]);
compile('outer { <div ng-outlet></div> }');
$router.navigateByUrl('/a');
$rootRouter.navigateByUrl('/a');
$rootScope.$digest();
expect(elt.text()).toBe('outer { hi }');
$router.navigateByUrl('/b');
$rootRouter.navigateByUrl('/b');
$rootScope.$digest();
expect(elt.text()).toBe('outer { one }');
});
@ -414,12 +414,12 @@ describe('Navigation lifecycle', function () {
$routerOnActivate: spy
});
$router.config([
$rootRouter.config([
{ path: '/a', component: 'activateCmp' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/a');
$rootRouter.navigateByUrl('/a');
$rootScope.$digest();
expect(spy).toHaveBeenCalled();
@ -433,15 +433,15 @@ describe('Navigation lifecycle', function () {
$routerCanDeactivate: spy
});
$router.config([
$rootRouter.config([
{ path: '/user/:name', component: 'deactivateCmp' },
{ path: '/post/:id', component: 'oneCmp' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/user/brian');
$rootRouter.navigateByUrl('/user/brian');
$rootScope.$digest();
$router.navigateByUrl('/post/123');
$rootRouter.navigateByUrl('/post/123');
$rootScope.$digest();
expect(spy).toHaveBeenCalledWith(instructionFor('oneCmp'),
instructionFor('deactivateCmp'));

View File

@ -5,7 +5,7 @@ describe('navigation', function () {
var elt,
$compile,
$rootScope,
$router,
$rootRouter,
$compileProvider;
beforeEach(function () {
@ -15,49 +15,79 @@ describe('navigation', function () {
$compileProvider = _$compileProvider_;
});
inject(function (_$compile_, _$rootScope_, _$router_) {
inject(function (_$compile_, _$rootScope_, _$rootRouter_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$router = _$router_;
$rootRouter = _$rootRouter_;
});
registerComponent('userCmp', {
template: '<div>hello {{userCmp.$routeParams.name}}</div>'
registerDirective('userCmp', {
template: '<div>hello {{userCmp.$routeParams.name}}</div>',
$routerOnActivate: function(next) {
this.$routeParams = next.params;
}
});
registerComponent('oneCmp', {
registerDirective('oneCmp', {
template: '<div>{{oneCmp.number}}</div>',
controller: function () {this.number = 'one'}
});
registerComponent('twoCmp', {
registerDirective('twoCmp', {
template: '<div>{{twoCmp.number}}</div>',
controller: function () {this.number = 'two'}
});
registerComponent('threeCmp', {
template: '<div>{{$ctrl.number}}</div>',
controller: function () {this.number = 'three'}
});
registerComponent('getParams', {
template: '<div>{{$ctrl.params.x}}</div>',
controller: function () {
this.$routerOnActivate = function(next) {
this.params = next.params;
};
}
})
});
it('should work in a simple case', function () {
compile('<ng-outlet></ng-outlet>');
$router.config([
$rootRouter.config([
{ path: '/', component: 'oneCmp' }
]);
$router.navigateByUrl('/');
$rootRouter.navigateByUrl('/');
$rootScope.$digest();
expect(elt.text()).toBe('one');
});
it('should work with components created by the `mod.component()` helper', function () {
compile('<ng-outlet></ng-outlet>');
$rootRouter.config([
{ path: '/', component: 'threeCmp' }
]);
$rootRouter.navigateByUrl('/');
$rootScope.$digest();
expect(elt.text()).toBe('three');
});
it('should navigate between components with different parameters', function () {
$router.config([
$rootRouter.config([
{ path: '/user/:name', component: 'userCmp' }
]);
compile('<ng-outlet></ng-outlet>');
$router.navigateByUrl('/user/brian');
$rootRouter.navigateByUrl('/user/brian');
$rootScope.$digest();
expect(elt.text()).toBe('hello brian');
$router.navigateByUrl('/user/igor');
$rootRouter.navigateByUrl('/user/igor');
$rootScope.$digest();
expect(elt.text()).toBe('hello igor');
});
@ -68,7 +98,7 @@ describe('navigation', function () {
function ParentController() {
instanceCount += 1;
}
registerComponent('parentCmp', {
registerDirective('parentCmp', {
template: 'parent { <ng-outlet></ng-outlet> }',
$routeConfig: [
{ path: '/user/:name', component: 'userCmp' }
@ -76,17 +106,17 @@ describe('navigation', function () {
controller: ParentController
});
$router.config([
$rootRouter.config([
{ path: '/parent/...', component: 'parentCmp' }
]);
compile('<ng-outlet></ng-outlet>');
$router.navigateByUrl('/parent/user/brian');
$rootRouter.navigateByUrl('/parent/user/brian');
$rootScope.$digest();
expect(instanceCount).toBe(1);
expect(elt.text()).toBe('parent { hello brian }');
$router.navigateByUrl('/parent/user/igor');
$rootRouter.navigateByUrl('/parent/user/igor');
$rootScope.$digest();
expect(instanceCount).toBe(1);
expect(elt.text()).toBe('parent { hello igor }');
@ -94,6 +124,25 @@ describe('navigation', function () {
it('should work with nested outlets', function () {
registerDirective('childCmp', {
template: '<div>inner { <div ng-outlet></div> }</div>',
$routeConfig: [
{ path: '/b', component: 'oneCmp' }
]
});
$rootRouter.config([
{ path: '/a/...', component: 'childCmp' }
]);
compile('<div>outer { <div ng-outlet></div> }</div>');
$rootRouter.navigateByUrl('/a/b');
$rootScope.$digest();
expect(elt.text()).toBe('outer { inner { one } }');
});
it('should work when parent route has empty path', inject(function ($location) {
registerComponent('childCmp', {
template: '<div>inner { <div ng-outlet></div> }</div>',
$routeConfig: [
@ -101,61 +150,77 @@ describe('navigation', function () {
]
});
$router.config([
{ path: '/a/...', component: 'childCmp' }
$rootRouter.config([
{ path: '/...', component: 'childCmp' }
]);
compile('<div>outer { <div ng-outlet></div> }</div>');
$router.navigateByUrl('/a/b');
$rootRouter.navigateByUrl('/b');
$rootScope.$digest();
expect(elt.text()).toBe('outer { inner { one } }');
});
expect($location.path()).toBe('/b');
}));
it('should work with recursive nested outlets', function () {
registerComponent('recurCmp', {
registerDirective('recurCmp', {
template: '<div>recur { <div ng-outlet></div> }</div>',
$routeConfig: [
{ path: '/recur', component: 'recurCmp' },
{ path: '/end', component: 'oneCmp' }
]});
$router.config([
$rootRouter.config([
{ path: '/recur', component: 'recurCmp' },
{ path: '/', component: 'oneCmp' }
]);
compile('<div>root { <div ng-outlet></div> }</div>');
$router.navigateByUrl('/recur/recur/end');
$rootRouter.navigateByUrl('/recur/recur/end');
$rootScope.$digest();
expect(elt.text()).toBe('root { one }');
});
it('should change location path', inject(function ($location) {
$router.config([
$rootRouter.config([
{ path: '/user', component: 'userCmp' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/user');
$rootRouter.navigateByUrl('/user');
$rootScope.$digest();
expect($location.path()).toBe('/user');
}));
it('should pass through query terms to the location', inject(function ($location) {
$rootRouter.config([
{ path: '/user', component: 'userCmp' }
]);
compile('<div ng-outlet></div>');
$rootRouter.navigateByUrl('/user?x=y');
$rootScope.$digest();
expect($location.path()).toBe('/user');
expect($location.search()).toEqual({ x: 'y'});
}));
it('should change location to the canonical route', inject(function ($location) {
compile('<div ng-outlet></div>');
$router.config([
$rootRouter.config([
{ path: '/', redirectTo: ['/User'] },
{ path: '/user', component: 'userCmp', name: 'User' }
]);
$router.navigateByUrl('/');
$rootRouter.navigateByUrl('/');
$rootScope.$digest();
expect($location.path()).toBe('/user');
@ -163,7 +228,7 @@ describe('navigation', function () {
it('should change location to the canonical route with nested components', inject(function ($location) {
registerComponent('childRouter', {
registerDirective('childRouter', {
template: '<div>inner { <div ng-outlet></div> }</div>',
$routeConfig: [
{ path: '/new-child', component: 'oneCmp', name: 'NewChild'},
@ -171,7 +236,7 @@ describe('navigation', function () {
]
});
$router.config([
$rootRouter.config([
{ path: '/old-parent/old-child', redirectTo: ['/NewParent', 'NewChild'] },
{ path: '/old-parent/old-child-two', redirectTo: ['/NewParent', 'NewChildTwo'] },
{ path: '/new-parent/...', component: 'childRouter', name: 'NewParent' }
@ -179,13 +244,13 @@ describe('navigation', function () {
compile('<div ng-outlet></div>');
$router.navigateByUrl('/old-parent/old-child');
$rootRouter.navigateByUrl('/old-parent/old-child');
$rootScope.$digest();
expect($location.path()).toBe('/new-parent/new-child');
expect(elt.text()).toBe('inner { one }');
$router.navigateByUrl('/old-parent/old-child-two');
$rootRouter.navigateByUrl('/old-parent/old-child-two');
$rootScope.$digest();
expect($location.path()).toBe('/new-parent/new-child-two');
@ -194,7 +259,7 @@ describe('navigation', function () {
it('should navigate when the location path changes', inject(function ($location) {
$router.config([
$rootRouter.config([
{ path: '/one', component: 'oneCmp' }
]);
compile('<div ng-outlet></div>');
@ -206,57 +271,88 @@ describe('navigation', function () {
}));
it('should expose a "navigating" property on $router', inject(function ($q) {
it('should navigate when the location query changes', inject(function ($location) {
$rootRouter.config([
{ path: '/get/params', component: 'getParams' }
]);
compile('<div ng-outlet></div>');
$location.url('/get/params?x=y');
$rootScope.$digest();
expect(elt.text()).toBe('y');
}));
it('should expose a "navigating" property on $rootRouter', inject(function ($q) {
var defer;
registerComponent('pendingActivate', {
registerDirective('pendingActivate', {
$canActivate: function () {
defer = $q.defer();
return defer.promise;
}
});
$router.config([
$rootRouter.config([
{ path: '/pending-activate', component: 'pendingActivate' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/pending-activate');
$rootRouter.navigateByUrl('/pending-activate');
$rootScope.$digest();
expect($router.navigating).toBe(true);
expect($rootRouter.navigating).toBe(true);
defer.resolve();
$rootScope.$digest();
expect($router.navigating).toBe(false);
expect($rootRouter.navigating).toBe(false);
}));
function registerComponent(name, options) {
var controller = options.controller || function () {};
['$routerOnActivate', '$routerOnDeactivate', '$routerOnReuse', '$routerCanReuse', '$routerCanDeactivate'].forEach(function (hookName) {
if (options[hookName]) {
controller.prototype[hookName] = options[hookName];
}
});
function registerDirective(name, options) {
function factory() {
return {
template: options.template || '',
controllerAs: name,
controller: controller
controller: getController(options)
};
}
if (options.$canActivate) {
factory.$canActivate = options.$canActivate;
}
if (options.$routeConfig) {
factory.$routeConfig = options.$routeConfig;
}
applyStaticProperties(factory, options);
$compileProvider.directive(name, factory);
}
function registerComponent(name, options) {
var definition = {
template: options.template || '',
controller: getController(options),
}
applyStaticProperties(definition, options);
$compileProvider.component(name, definition);
}
function compile(template) {
elt = $compile('<div>' + template + '</div>')($rootScope);
$rootScope.$digest();
return elt;
}
function getController(options) {
var controller = options.controller || function () {};
[
'$routerOnActivate', '$routerOnDeactivate',
'$routerOnReuse', '$routerCanReuse',
'$routerCanDeactivate'
].forEach(function (hookName) {
if (options[hookName]) {
controller.prototype[hookName] = options[hookName];
}
});
return controller;
}
function applyStaticProperties(target, options) {
['$canActivate', '$routeConfig'].forEach(function(property) {
if (options[property]) {
target[property] = options[property];
}
});
}
});

View File

@ -5,7 +5,7 @@ describe('router', function () {
var elt,
$compile,
$rootScope,
$router,
$rootRouter,
$compileProvider;
beforeEach(function () {
@ -18,10 +18,10 @@ describe('router', function () {
$compileProvider = _$compileProvider_;
});
inject(function (_$compile_, _$rootScope_, _$router_) {
inject(function (_$compile_, _$rootScope_, _$rootRouter_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$router = _$router_;
$rootRouter = _$rootRouter_;
});
});
@ -44,36 +44,173 @@ describe('router', function () {
expect(elt.text()).toBe('Home');
}));
function registerComponent(name, options) {
var controller = options.controller || function () {};
it('should bind the component to the current router', inject(function($location) {
var router;
registerComponent('homeCmp', {
bindings: { $router: '=' },
controller: function($scope, $element) {
this.$routerOnActivate = function() {
router = this.$router;
};
},
template: 'Home'
});
['$onActivate', '$onDeactivate', '$onReuse', '$canReuse', '$canDeactivate'].forEach(function (hookName) {
if (options[hookName]) {
controller.prototype[hookName] = options[hookName];
registerComponent('app', {
template: '<div ng-outlet></div>',
$routeConfig: [
{ path: '/', component: 'homeCmp' }
]
});
compile('<app></app>');
$location.path('/');
$rootScope.$digest();
var homeElement = elt.find('home-cmp');
expect(homeElement.text()).toBe('Home');
expect(homeElement.isolateScope().$ctrl.$router).toBeDefined();
expect(router).toBeDefined();
}));
it('should work when an async route is provided route data', inject(function($location, $q) {
registerDirective('homeCmp', {
template: 'Home ({{homeCmp.isAdmin}})',
$routerOnActivate: function(next, prev) {
this.isAdmin = next.routeData.data.isAdmin;
}
});
registerDirective('app', {
template: '<div ng-outlet></div>',
$routeConfig: [
{ path: '/', loader: function() { return $q.when('homeCmp'); }, data: { isAdmin: true } }
]
});
compile('<app></app>');
$location.path('/');
$rootScope.$digest();
expect(elt.text()).toBe('Home (true)');
}));
it('should work with a templateUrl component', inject(function($location, $httpBackend) {
var $routerOnActivate = jasmine.createSpy('$routerOnActivate');
$httpBackend.expectGET('homeCmp.html').respond('Home');
registerComponent('homeCmp', {
templateUrl: 'homeCmp.html',
$routerOnActivate: $routerOnActivate
});
registerComponent('app', {
template: '<div ng-outlet></div>',
$routeConfig: [
{ path: '/', component: 'homeCmp' }
]
});
compile('<app></app>');
$location.path('/');
$rootScope.$digest();
$httpBackend.flush();
var homeElement = elt.find('home-cmp');
expect(homeElement.text()).toBe('Home');
expect($routerOnActivate).toHaveBeenCalled();
}));
it('should provide the current instruction', inject(function($location, $q) {
registerComponent('homeCmp', {
template: 'Home ({{homeCmp.isAdmin}})'
});
registerComponent('app', {
template: '<div ng-outlet></div>',
$routeConfig: [
{ path: '/', component: 'homeCmp', name: 'Home' }
]
});
compile('<app></app>');
$location.path('/');
$rootScope.$digest();
var instruction = $rootRouter.generate(['/Home']);
expect($rootRouter.currentInstruction).toEqual(instruction);
}));
it('should provide the root level router', inject(function($location, $q) {
registerComponent('homeCmp', {
template: 'Home ({{homeCmp.isAdmin}})',
bindings: {
$router: '<'
}
});
registerComponent('app', {
template: '<div ng-outlet></div>',
$routeConfig: [
{ path: '/', component: 'homeCmp', name: 'Home' }
]
});
compile('<app></app>');
$location.path('/');
$rootScope.$digest();
var homeElement = elt.find('home-cmp');
expect(homeElement.isolateScope().$ctrl.$router.root).toEqual($rootRouter);
}));
function registerDirective(name, options) {
function factory() {
return {
template: options.template || '',
controllerAs: name,
controller: controller
controller: getController(options)
};
}
if (options.$canActivate) {
factory.$canActivate = options.$canActivate;
}
if (options.$routeConfig) {
factory.$routeConfig = options.$routeConfig;
}
applyStaticProperties(factory, options);
$compileProvider.directive(name, factory);
}
function registerComponent(name, options) {
var definition = {
bindings: options.bindings,
controller: getController(options),
};
if (options.template) definition.template = options.template;
if (options.templateUrl) definition.templateUrl = options.templateUrl;
applyStaticProperties(definition, options);
$compileProvider.component(name, definition);
}
function compile(template) {
elt = $compile('<div>' + template + '</div>')($rootScope);
$rootScope.$digest();
return elt;
}
function getController(options) {
var controller = options.controller || function () {};
[
'$routerOnActivate', '$routerOnDeactivate',
'$routerOnReuse', '$routerCanReuse',
'$routerCanDeactivate'
].forEach(function (hookName) {
if (options[hookName]) {
controller.prototype[hookName] = options[hookName];
}
});
return controller;
}
function applyStaticProperties(target, options) {
['$canActivate', '$routeConfig'].forEach(function(property) {
if (options[property]) {
target[property] = options[property];
}
});
}
});

View File

@ -5,7 +5,7 @@ describe('ngRoute shim', function () {
var elt,
$compile,
$rootScope,
$router,
$rootRouter,
$compileProvider,
$routeProvider;
@ -18,10 +18,10 @@ describe('ngRoute shim', function () {
$routeProvider = _$routeProvider_;
});
inject(function (_$compile_, _$rootScope_, _$router_) {
inject(function (_$compile_, _$rootScope_, _$rootRouter_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$router = _$router_;
$rootRouter = _$rootRouter_;
});
});
@ -36,7 +36,7 @@ describe('ngRoute shim', function () {
compile('<ng-outlet></ng-outlet>');
$router.navigateByUrl('/');
$rootRouter.navigateByUrl('/');
$rootScope.$digest();
expect(elt.text()).toBe('one');
@ -55,7 +55,7 @@ describe('ngRoute shim', function () {
compile('root {<ng-outlet></ng-outlet>}');
$router.navigateByUrl('/');
$rootRouter.navigateByUrl('/');
$rootScope.$digest();
expect(elt.text()).toBe('root {one}');
}));
@ -76,7 +76,7 @@ describe('ngRoute shim', function () {
compile('<ng-outlet></ng-outlet>');
$router.navigateByUrl('/');
$rootRouter.navigateByUrl('/');
$rootScope.$digest();
expect(elt.text()).toBe('value: 42');
@ -94,11 +94,11 @@ describe('ngRoute shim', function () {
compile('<ng-outlet></ng-outlet>');
$router.navigateByUrl('/user/brian');
$rootRouter.navigateByUrl('/user/brian');
$rootScope.$digest();
expect(elt.text()).toBe('hello brian');
$router.navigateByUrl('/user/igor');
$rootRouter.navigateByUrl('/user/igor');
$rootScope.$digest();
expect(elt.text()).toBe('hello igor');
});
@ -115,7 +115,7 @@ describe('ngRoute shim', function () {
compile('<ng-outlet></ng-outlet>');
$router.navigateByUrl('/home/foo/bar/123');
$rootRouter.navigateByUrl('/home/foo/bar/123');
$rootScope.$digest();
expect(elt.text()).toBe('rest: foo/bar/123');
});
@ -129,7 +129,7 @@ describe('ngRoute shim', function () {
compile('root {<ng-outlet></ng-outlet>}');
$router.navigateByUrl('/home/test');
$rootRouter.navigateByUrl('/home/test');
$rootScope.$digest();
expect(elt.text()).toBe('root {}');
expect(console.warn)
@ -150,7 +150,7 @@ describe('ngRoute shim', function () {
compile('root {<ng-outlet></ng-outlet>}');
$router.navigateByUrl('/');
$rootRouter.navigateByUrl('/');
$rootScope.$digest();
expect(elt.text()).toBe('root {welcome home!}');
expect($location.path()).toBe('/home');
@ -169,7 +169,7 @@ describe('ngRoute shim', function () {
compile('root {<ng-outlet></ng-outlet>}');
$router.navigateByUrl('/somewhere');
$rootRouter.navigateByUrl('/somewhere');
$rootScope.$digest();
expect(elt.text()).toBe('root {welcome home!}');
expect($location.path()).toBe('/home');

View File

@ -5,7 +5,7 @@ describe('ngLink', function () {
var elt,
$compile,
$rootScope,
$router,
$rootRouter,
$compileProvider;
beforeEach(function () {
@ -15,10 +15,10 @@ describe('ngLink', function () {
$compileProvider = _$compileProvider_;
});
inject(function (_$compile_, _$rootScope_, _$router_) {
inject(function (_$compile_, _$rootScope_, _$rootRouter_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$router = _$router_;
$rootRouter = _$rootRouter_;
});
registerComponent('userCmp', '<div>hello {{userCmp.$routeParams.name}}</div>', function () {});
@ -28,26 +28,26 @@ describe('ngLink', function () {
it('should allow linking from the parent to the child', function () {
$router.config([
$rootRouter.config([
{ path: '/a', component: 'oneCmp' },
{ path: '/b', component: 'twoCmp', name: 'Two' }
]);
compile('<a ng-link="[\'/Two\']">link</a> | outer { <div ng-outlet></div> }');
$router.navigateByUrl('/a');
$rootRouter.navigateByUrl('/a');
$rootScope.$digest();
expect(elt.find('a').attr('href')).toBe('./b');
});
it('should allow linking from the child and the parent', function () {
$router.config([
$rootRouter.config([
{ path: '/a', component: 'oneCmp' },
{ path: '/b', component: 'twoCmp', name: 'Two' }
]);
compile('outer { <div ng-outlet></div> }');
$router.navigateByUrl('/b');
$rootRouter.navigateByUrl('/b');
$rootScope.$digest();
expect(elt.find('a').attr('href')).toBe('./b');
@ -57,13 +57,13 @@ describe('ngLink', function () {
it('should allow params in routerLink directive', function () {
registerComponent('twoLinkCmp', '<div><a ng-link="[\'/Two\', {param: \'lol\'}]">{{twoLinkCmp.number}}</a></div>', function () {this.number = 'two'});
$router.config([
$rootRouter.config([
{ path: '/a', component: 'twoLinkCmp' },
{ path: '/b/:param', component: 'twoCmp', name: 'Two' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/a');
$rootRouter.navigateByUrl('/a');
$rootScope.$digest();
expect(elt.find('a').attr('href')).toBe('./b/lol');
@ -72,13 +72,13 @@ describe('ngLink', function () {
it('should update the href of links with bound params', function () {
registerComponent('twoLinkCmp', '<div><a ng-link="[\'/Two\', {param: twoLinkCmp.number}]">{{twoLinkCmp.number}}</a></div>', function () {this.number = 'param'});
$router.config([
$rootRouter.config([
{ path: '/a', component: 'twoLinkCmp' },
{ path: '/b/:param', component: 'twoCmp', name: 'Two' }
]);
compile('<div ng-outlet></div>');
$router.navigateByUrl('/a');
$rootRouter.navigateByUrl('/a');
$rootScope.$digest();
expect(elt.find('a').attr('href')).toBe('./b/param');
@ -86,7 +86,7 @@ describe('ngLink', function () {
it('should navigate on left-mouse click when a link url matches a route', function () {
$router.config([
$rootRouter.config([
{ path: '/', component: 'oneCmp' },
{ path: '/two', component: 'twoCmp', name: 'Two'}
]);
@ -104,8 +104,8 @@ describe('ngLink', function () {
});
it('should not navigate on non-left mouse click when a link url matches a route', inject(function ($router) {
$router.config([
it('should not navigate on non-left mouse click when a link url matches a route', inject(function ($rootRouter) {
$rootRouter.config([
{ path: '/', component: 'oneCmp' },
{ path: '/two', component: 'twoCmp', name: 'Two'}
]);
@ -122,7 +122,7 @@ describe('ngLink', function () {
// See https://github.com/angular/router/issues/206
it('should not navigate a link without an href', function () {
$router.config([
$rootRouter.config([
{ path: '/', component: 'oneCmp' },
{ path: '/two', component: 'twoCmp', name: 'Two'}
]);
@ -135,6 +135,20 @@ describe('ngLink', function () {
}).not.toThrow();
});
it('should add an ng-link-active class on the current link', inject(function ($rootRouter) {
$rootRouter.config([
{ path: '/', component: 'oneCmp', name: 'One' }
]);
compile('<a ng-link="[\'/One\']">one</a> | <div ng-outlet></div>');
$rootScope.$digest();
$rootRouter.navigateByUrl('/');
$rootScope.$digest();
expect(elt.find('a').attr('class')).toBe('ng-link-active');
}));
function registerComponent(name, template, config) {
var controller = function () {};

View File

@ -22,7 +22,7 @@ function provideHelpers(fn, preInject) {
var elt,
$compile,
$rootScope,
$router,
$rootRouter,
$templateCache,
$controllerProvider;
@ -32,10 +32,10 @@ function provideHelpers(fn, preInject) {
$controllerProvider = _$controllerProvider_;
});
inject(function(_$compile_, _$rootScope_, _$router_, _$templateCache_) {
inject(function(_$compile_, _$rootScope_, _$rootRouter_, _$templateCache_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$router = _$router_;
$rootRouter = _$rootRouter_;
$templateCache = _$templateCache_;
});
@ -72,7 +72,7 @@ function provideHelpers(fn, preInject) {
fn({
registerComponent: registerComponent,
$router: $router,
$rootRouter: $rootRouter,
put: put,
compile: compile
})

View File

@ -1,12 +1,12 @@
{
"version": "v4",
"repo": "angular/DefinitelyTyped",
"repo": "DefinitelyTyped/DefinitelyTyped",
"ref": "master",
"path": "typings",
"bundle": "typings/tsd.d.ts",
"installed": {
"angularjs/angular.d.ts": {
"commit": "746b9a892629060bc853e792afff536e0ec4655e"
"commit": "6eebd5e90a1cbd6b47b0705ba72dbcd5baf846f3"
}
}
}

View File

@ -3,7 +3,7 @@ Bootstrapping
@cheatsheetIndex 0
@description
{@target ts}`import {bootstrap} from 'angular2/platform/browser';`{@endtarget}
{@target js}Available from the `ng.platform.browser` namespace.{@endtarget}
{@target js}Available from the `ng.platform.browser` namespace{@endtarget}
{@target dart}`import 'package:angular2/bootstrap.dart';`{@endtarget}
@cheatsheetItem

View File

@ -4,7 +4,7 @@ Built-in directives
@description
{@target ts}`import {NgIf, ...} from 'angular2/common';`{@endtarget}
{@target js}Available from the `ng.common` namespace{@endtarget}
{@target dart}`import 'package:angular2/common.dart';`{@endtarget}
{@target dart}Available using `platform_directives` in pubspec{@endtarget}
@cheatsheetItem
syntax:

View File

@ -4,7 +4,7 @@ Class decorators
@description
{@target ts}`import {Directive, ...} from 'angular2/core';`{@endtarget}
{@target js}Available from the `ng.core` namespace{@endtarget}
{@target dart}`import 'package:angular2/core.dart';`{@endtarget}
{@target dart}`import 'package:angular2/angular2.dart';`{@endtarget}
@cheatsheetItem
syntax(ts):

View File

@ -4,7 +4,7 @@ Dependency injection configuration
@description
{@target ts}`import {provide} from 'angular2/core';`{@endtarget}
{@target js}Available from the `ng.core` namespace{@endtarget}
{@target dart}`import 'package:angular2/core.dart';`{@endtarget}
{@target dart}`import 'package:angular2/angular2.dart';`{@endtarget}
@cheatsheetItem
syntax(ts dart):

View File

@ -4,7 +4,7 @@ Class field decorators for directives and components
@description
{@target ts}`import {Input, ...} from 'angular2/core';`{@endtarget}
{@target js}Available from the `ng.core` namespace{@endtarget}
{@target dart}`import 'package:angular2/core.dart';`{@endtarget}
{@target dart}`import 'package:angular2/angular2.dart';`{@endtarget}
@cheatsheetItem
syntax(ts dart):

View File

@ -4,7 +4,7 @@ Forms
@description
{@target ts}`import {FORM_DIRECTIVES} from 'angular2/common';`{@endtarget}
{@target js}Available from `ng.common.FORM_DIRECTIVES`{@endtarget}
{@target dart}`import 'package:angular2/common.dart';`{@endtarget}
{@target dart}Available using `platform_directives` in pubspec{@endtarget}
@cheatsheetItem
syntax:

View File

@ -4,7 +4,7 @@ Routing and navigation
@description
{@target ts}`import {RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS, ...} from 'angular2/router';`{@endtarget}
{@target js}Available from the `ng.router` namespace{@endtarget}
{@target dart}`import 'package:angular2/router.dart';`{@endtarget}
{@target dart}`import 'package:angular2/angular2.dart';`{@endtarget}
@cheatsheetItem

View File

@ -456,7 +456,7 @@ Where
* `local` is a local identifier for local variables.
* `internal` is an internal variable which the directive exports for binding.
* `key` is an attribute name usually only used to trigger a specific directive.
* `keyExpression` is an property name to which the expression will be bound to.
* `keyExpression` is a property name to which the expression will be bound to.
* `varExport` allows exporting of directive internal state as variables for further binding. If no `internal` name
is specified, the exporting is to an implicit variable.
* `microsyntax` allows you to build a simple microsyntax which can still clearly identify which expressions bind to

View File

@ -2,7 +2,7 @@ import {provide} from 'angular2/core';
import {bootstrap} from 'angular2/bootstrap';
import {UrlResolver} from 'angular2/compiler';
var MyApp;
var MyApp: any;
// #docregion url_resolver
class MyUrlResolver extends UrlResolver {

View File

@ -1,7 +1,7 @@
import {DebugElement} from 'angular2/core';
var debugElement: DebugElement;
var predicate;
var predicate: any;
// #docregion scope_all
debugElement.query(predicate);

View File

@ -2,8 +2,8 @@ import {bootstrap} from 'angular2/bootstrap';
import {NG_VALIDATORS} from 'angular2/common';
import {Provider} from 'angular2/core';
let MyApp = null;
let myValidator = null;
let MyApp: Function = null;
let myValidator: any = null;
// #docregion ng_validators
bootstrap(MyApp, [new Provider(NG_VALIDATORS, {useValue: myValidator, multi: true})]);

View File

@ -1,6 +1,6 @@
import {Component, provide} from 'angular2/core';
import {bootstrap} from 'angular2/bootstrap';
import {Observable} from 'rxjs/Observable';
import {Observable, Subscriber} from 'rxjs/Rx';
// #docregion AsyncPipe
@Component({
@ -37,8 +37,9 @@ export class AsyncPipeExample {
// #docregion AsyncPipeObservable
@Component({selector: "task-cmp", template: "Time: {{ time | async }}"})
class Task {
time = new Observable<number>(
observer => { setInterval(_ => observer.next(new Date().getTime()), 500); });
time = new Observable<number>((observer: Subscriber<number>) => {
setInterval(_ => observer.next(new Date().getTime()), 500);
});
}
// #enddocregion

View File

@ -12,7 +12,7 @@ import {bootstrap} from 'angular2/bootstrap';
})
export class LowerUpperPipeExample {
value: string;
change(value) { this.value = value; }
change(value: string) { this.value = value; }
}
// #enddocregion

View File

@ -1,6 +1,6 @@
import {Component, Attribute, Directive, Pipe} from 'angular2/core';
var CustomDirective;
var CustomDirective: Function;
// #docregion component
@Component({selector: 'greet', template: 'Hello {{name}}!', directives: [CustomDirective]})
@ -20,7 +20,7 @@ class Page {
// #docregion attributeMetadata
@Directive({selector: 'input'})
class InputAttrDirective {
constructor(@Attribute('type') type) {
constructor(@Attribute('type') type: string) {
// type would be 'text' in this example
}
}
@ -38,6 +38,6 @@ class InputDirective {
// #docregion pipe
@Pipe({name: 'lowercase'})
class Lowercase {
transform(v, args) { return v.toLowerCase(); }
transform(v: string, args: any[]) { return v.toLowerCase(); }
}
// #enddocregion

View File

@ -1,7 +1,7 @@
// #docregion enableProdMode
import {enableProdMode} from 'angular2/core';
import {bootstrap} from 'angular2/bootstrap';
import {MyComponent} from 'my_component';
import {MyComponent} from './my_component';
enableProdMode();
bootstrap(MyComponent);

View File

@ -1,6 +1,6 @@
// #docregion Observable
import {Observable} from 'rxjs/Observable';
var obs = new Observable<number>(obs => {
import {Observable, Subscriber} from 'rxjs/Rx';
var obs = new Observable<number>((obs: Subscriber<number>) => {
var i = 0;
setInterval(_ => { obs.next(++i); }, 1000);
});

View File

@ -1,10 +1,10 @@
// #docregion Observable
import {Observable} from 'rxjs/Observable';
import {Observable, Subscriber} from 'rxjs/Rx';
import 'rxjs/add/operator/map';
var obs = new Observable(obs => {
var obs = new Observable<number>((obs: Subscriber<any>) => {
var i = 0;
setInterval(_ => obs.next(++i), 1000);
});
obs.map(i => `${i} seconds elapsed`).subscribe(msg => console.log(msg));
obs.map((i: number) => `${i} seconds elapsed`).subscribe(msg => console.log(msg));
// #enddocregion

View File

@ -1,10 +1,10 @@
// #docregion Observable
import {Observable} from 'rxjs/Observable';
import {Observable, Subscriber} from 'rxjs/Rx';
import {map} from 'rxjs/operator/map';
var obs = new Observable(obs => {
var obs = new Observable<number>((sub: Subscriber<number>) => {
var i = 0;
setInterval(_ => obs.next(++i), 1000);
setInterval(_ => sub.next(++i), 1000);
});
map.call(obs, i => `${i} seconds elapsed`).subscribe(msg => console.log(msg));
map.call(obs, (i: number) => `${i} seconds elapsed`).subscribe((msg: string) => console.log(msg));
// #enddocregion

View File

@ -1,7 +1,6 @@
import {verifyNoBrowserErrors} from 'angular2/src/testing/e2e_util';
import {Promise} from 'angular2/src/facade/async';
function waitForElement(selector) {
function waitForElement(selector: string) {
var EC = (<any>protractor).ExpectedConditions;
// Waits for the element with id 'abc' to be present on the dom.
browser.wait(EC.presenceOf($(selector)), 20000);

View File

@ -1,7 +1,6 @@
import {verifyNoBrowserErrors} from 'angular2/src/testing/e2e_util';
import {Promise} from 'angular2/src/facade/async';
function waitForElement(selector) {
function waitForElement(selector: string) {
var EC = (<any>protractor).ExpectedConditions;
// Waits for the element with id 'abc' to be present on the dom.
browser.wait(EC.presenceOf($(selector)), 20000);

View File

@ -1,7 +1,6 @@
import {verifyNoBrowserErrors} from 'angular2/src/testing/e2e_util';
import {Promise} from 'angular2/src/facade/async';
function waitForElement(selector) {
function waitForElement(selector: string) {
var EC = (<any>protractor).ExpectedConditions;
// Waits for the element with id 'abc' to be present on the dom.
browser.wait(EC.presenceOf($(selector)), 20000);

View File

@ -1,7 +1,6 @@
import {verifyNoBrowserErrors} from 'angular2/src/testing/e2e_util';
import {Promise} from 'angular2/src/facade/async';
function waitForElement(selector) {
function waitForElement(selector: string) {
var EC = (<any>protractor).ExpectedConditions;
// Waits for the element with id 'abc' to be present on the dom.
browser.wait(EC.presenceOf($(selector)), 20000);

View File

@ -1,7 +1,6 @@
import {verifyNoBrowserErrors} from 'angular2/src/testing/e2e_util';
import {Promise} from 'angular2/src/facade/async';
function waitForElement(selector) {
function waitForElement(selector: string) {
var EC = (<any>protractor).ExpectedConditions;
// Waits for the element with id 'abc' to be present on the dom.
browser.wait(EC.presenceOf($(selector)), 20000);

View File

@ -73,7 +73,7 @@ describe('some component', () => {
// #docregion beforeEachProviders
describe('some component', () => {
beforeEachProviders(() => [provide(MyService, {useClass: MyMockService})]);
it('uses MyService', inject([MyService], (service) => {
it('uses MyService', inject([MyService], (service: MyMockService) => {
// service is an instance of MyMockService.
}));
});
@ -81,7 +81,7 @@ describe('some component', () => {
// #docregion afterEach
describe('some component', () => {
afterEach((done) => { db.reset().then((_) => done()); });
afterEach((done: Function) => { db.reset().then((_: any) => done()); });
it('uses the db', () => {
// This test can leave the database in a dirty state.
// The afterEach will ensure it gets reset.

View File

@ -141,7 +141,8 @@ export {URLSearchParams} from './src/http/url_search_params';
* // Send a response to the request
* connection.mockRespond(response);
* });
* });
* }
* });
*
* http.get('people.json').observer({
* next: res => {
@ -156,7 +157,8 @@ export const HTTP_PROVIDERS: any[] = [
// issue: https://github.com/angular/angular/issues/3183
provide(Http,
{
useFactory: (xhrBackend, requestOptions) => new Http(xhrBackend, requestOptions),
useFactory: (xhrBackend: XHRBackend, requestOptions: RequestOptions) =>
new Http(xhrBackend, requestOptions),
deps: [XHRBackend, RequestOptions]
}),
BrowserXhr,
@ -268,7 +270,8 @@ export const HTTP_BINDINGS = HTTP_PROVIDERS;
* // Send a response to the request
* connection.mockRespond(response);
* });
* });
* }
* });
* jsonp.get('people.json').observer({
* next: res => {
@ -283,7 +286,8 @@ export const JSONP_PROVIDERS: any[] = [
// issue: https://github.com/angular/angular/issues/3183
provide(Jsonp,
{
useFactory: (jsonpBackend, requestOptions) => new Jsonp(jsonpBackend, requestOptions),
useFactory: (jsonpBackend: JSONPBackend, requestOptions: RequestOptions) =>
new Jsonp(jsonpBackend, requestOptions),
deps: [JSONPBackend, RequestOptions]
}),
BrowserJsonp,

View File

@ -1,36 +0,0 @@
/**
* Declarations angular depends on for compilation to ES6.
* This file is also used to propagate our transitive typings
* to users.
*/
/// <reference path="../typings/zone/zone.d.ts"/>
/// <reference path="../typings/hammerjs/hammerjs.d.ts"/>
// TODO: ideally the node.d.ts reference should be scoped only for files that need and not to all
// the code including client code
/// <reference path="../typings/node/node.d.ts" />
declare var assert: any;
interface BrowserNodeGlobal {
Object: typeof Object;
Array: typeof Array;
Map: typeof Map;
Set: typeof Set;
Date: typeof Date;
RegExp: typeof RegExp;
JSON: typeof JSON;
Math: typeof Math;
assert(condition: any): void;
Reflect: any;
zone: Zone;
getAngularTestability: Function;
getAllAngularTestabilities: Function;
frameworkStabilizers: Array<Function>;
setTimeout: Function;
clearTimeout: Function;
setInterval: Function;
clearInterval: Function;
}

View File

@ -1,7 +1,52 @@
/**
* Declarations angular depends on for compilation to ES6.
* This file is also used to propagate our transitive typings
* to users.
* Subset of es6-shim typings.
* Angular should not require use of ES6 runtime but some API usages are already present.
* See https://github.com/angular/angular/issues/5242
* TODO(alexeagle): remove methods below which may not be present in targeted browser
*/
/// <reference path="../typings/es6-shim/es6-shim.d.ts"/>
/// <reference path="./globals-es6.d.ts"/>
declare type PromiseConstructor = typeof Promise;
interface String {
/**
* Returns true if the sequence of elements of searchString converted to a String is the
* same as the corresponding elements of this object (converted to a String) starting at
* position. Otherwise returns false.
*/
startsWith(searchString: string, position?: number): boolean;
/**
* Returns true if the sequence of elements of searchString converted to a String is the
* same as the corresponding elements of this object (converted to a String) starting at
* endPosition length(this). Otherwise returns false.
*/
endsWith(searchString: string, endPosition?: number): boolean;
}
interface Array<T> {
/**
* Returns the value of the first element in the array where predicate is true, and undefined
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found, find
* immediately returns that element value. Otherwise, find returns undefined.
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
find(predicate: (value: T, index: number, obj: Array<T>) => boolean, thisArg?: any): T;
/**
* Returns the this object after filling the section identified by start and end with value
* @param value value to fill array section with
* @param start index to start filling the array at. If start is negative, it is treated as
* length+start where length is the length of the array.
* @param end index to stop filling the array at. If end is negative, it is treated as
* length+end.
*/
fill(value: T, start?: number, end?: number): T[];
}
interface NumberConstructor {
/**
* Returns true if the value passed is an integer, false otherwise.
* @param number A numeric value.
*/
isInteger(number: number): boolean;
}

View File

@ -13,7 +13,6 @@ export {
} from 'angular2/src/platform/browser_common';
import {Type, isPresent, CONST_EXPR} from 'angular2/src/facade/lang';
import {Promise} from 'angular2/src/facade/promise';
import {
BROWSER_PROVIDERS,
BROWSER_APP_COMMON_PROVIDERS

View File

@ -12,7 +12,6 @@ export {
} from 'angular2/src/platform/browser_common';
import {Type, isPresent} from 'angular2/src/facade/lang';
import {Promise} from 'angular2/src/facade/promise';
import {
BROWSER_PROVIDERS,
BROWSER_APP_COMMON_PROVIDERS

View File

@ -15,7 +15,7 @@ import {MockAnimationBuilder} from 'angular2/src/mock/animation_builder_mock';
import {MockDirectiveResolver} from 'angular2/src/mock/directive_resolver_mock';
import {MockViewResolver} from 'angular2/src/mock/view_resolver_mock';
import {MockLocationStrategy} from 'angular2/src/mock/mock_location_strategy';
import {LocationStrategy} from 'angular2/src/router/location_strategy';
import {LocationStrategy} from 'angular2/src/router/location/location_strategy';
import {MockNgZone} from 'angular2/src/mock/ng_zone_mock';
import {XHRImpl} from "angular2/src/platform/browser/xhr_impl";

View File

@ -16,7 +16,7 @@ import {MockAnimationBuilder} from 'angular2/src/mock/animation_builder_mock';
import {MockDirectiveResolver} from 'angular2/src/mock/directive_resolver_mock';
import {MockViewResolver} from 'angular2/src/mock/view_resolver_mock';
import {MockLocationStrategy} from 'angular2/src/mock/mock_location_strategy';
import {LocationStrategy} from 'angular2/src/router/location_strategy';
import {LocationStrategy} from 'angular2/src/router/location/location_strategy';
import {MockNgZone} from 'angular2/src/mock/ng_zone_mock';
import {TestComponentBuilder} from 'angular2/src/testing/test_component_builder';

View File

@ -18,15 +18,16 @@ dependencies:
logging: '>=0.9.0 <0.12.0'
observe: '^0.13.1'
protobuf: '^0.5.0'
quiver: '^0.21.4'
source_span: '^1.0.0'
stack_trace: '^1.1.1'
dev_dependencies:
code_transformers: '>=0.2.9+4 <0.4.0'
transformer_test: '^0.2.0'
guinness: '^0.1.18'
guinness2: '0.0.5'
quiver: '^0.21.4'
test: '^0.12.6'
transformers:
- angular2
- angular2/transform/codegen
- $dart2js:
commandLineOptions:
- --show-package-warnings

View File

@ -5,26 +5,26 @@
*/
export {Router} from './src/router/router';
export {RouterOutlet} from './src/router/router_outlet';
export {RouterLink} from './src/router/router_link';
export {RouterOutlet} from './src/router/directives/router_outlet';
export {RouterLink} from './src/router/directives/router_link';
export {RouteParams, RouteData} from './src/router/instruction';
export {PlatformLocation} from './src/router/platform_location';
export {PlatformLocation} from './src/router/location/platform_location';
export {RouteRegistry, ROUTER_PRIMARY_COMPONENT} from './src/router/route_registry';
export {LocationStrategy, APP_BASE_HREF} from './src/router/location_strategy';
export {HashLocationStrategy} from './src/router/hash_location_strategy';
export {PathLocationStrategy} from './src/router/path_location_strategy';
export {Location} from './src/router/location';
export * from './src/router/route_config_decorator';
export {LocationStrategy, APP_BASE_HREF} from './src/router/location/location_strategy';
export {HashLocationStrategy} from './src/router/location/hash_location_strategy';
export {PathLocationStrategy} from './src/router/location/path_location_strategy';
export {Location} from './src/router/location/location';
export * from './src/router/route_config/route_config_decorator';
export * from './src/router/route_definition';
export {OnActivate, OnDeactivate, OnReuse, CanDeactivate, CanReuse} from './src/router/interfaces';
export {CanActivate} from './src/router/lifecycle_annotations';
export {CanActivate} from './src/router/lifecycle/lifecycle_annotations';
export {Instruction, ComponentInstruction} from './src/router/instruction';
export {OpaqueToken} from 'angular2/core';
export {ROUTER_PROVIDERS_COMMON} from 'angular2/src/router/router_providers_common';
export {ROUTER_PROVIDERS, ROUTER_BINDINGS} from 'angular2/src/router/router_providers';
import {RouterOutlet} from './src/router/router_outlet';
import {RouterLink} from './src/router/router_link';
import {RouterOutlet} from './src/router/directives/router_outlet';
import {RouterLink} from './src/router/directives/router_link';
import {CONST_EXPR} from './src/facade/lang';
/**

View File

@ -1,9 +1,9 @@
import {TEMPLATE_TRANSFORMS} from 'angular2/compiler';
import {Provider} from 'angular2/core';
import {RouterLinkTransform} from 'angular2/src/router/router_link_transform';
import {RouterLinkTransform} from 'angular2/src/router/directives/router_link_transform';
import {CONST_EXPR} from 'angular2/src/facade/lang';
export {RouterLinkTransform} from 'angular2/src/router/router_link_transform';
export {RouterLinkTransform} from 'angular2/src/router/directives/router_link_transform';
/**
* Enables the router link DSL.

View File

@ -52,7 +52,7 @@ export class Animation {
this.startTime = DateWrapper.toMillis(DateWrapper.now());
this._stringPrefix = DOM.getAnimationPrefix();
this.setup();
this.wait(timestamp => this.start());
this.wait((timestamp: any) => this.start());
}
wait(callback: Function) {
@ -97,7 +97,7 @@ export class Animation {
* @param styles
*/
applyStyles(styles: {[key: string]: any}): void {
StringMapWrapper.forEach(styles, (value, key) => {
StringMapWrapper.forEach(styles, (value: any, key: string) => {
var dashCaseKey = camelCaseToDashCase(key);
if (isPresent(DOM.getStyle(this.element, dashCaseKey))) {
DOM.setStyle(this.element, dashCaseKey, value.toString());

View File

@ -17,7 +17,7 @@ export class BrowserDetails {
DOM.setAttribute(div, 'style', `position: absolute; top: -9999px; left: -9999px; width: 1px;
height: 1px; transition: all 1ms linear 1ms;`);
// Firefox requires that we wait for 2 frames for some reason
this.raf(timestamp => {
this.raf((timestamp: any) => {
DOM.on(div, 'transitionend', (event: any) => {
var elapsed = Math.round(event.elapsedTime * 1000);
this.elapsedTimeIncludesDelay = elapsed == 2;
@ -37,7 +37,8 @@ class RafQueue {
currentFrameId: number;
constructor(public callback: Function, public frames: number) { this._raf(); }
private _raf() {
this.currentFrameId = DOM.requestAnimationFrame(timestamp => this._nextFrame(timestamp));
this.currentFrameId =
DOM.requestAnimationFrame((timestamp: number) => this._nextFrame(timestamp));
}
private _nextFrame(timestamp: number) {
this.frames--;

View File

@ -1,14 +1,16 @@
import {isPresent, isString, StringWrapper, isBlank, isArray} from 'angular2/src/facade/lang';
import {isPresent, isString, isArray} from 'angular2/src/facade/lang';
import {
DoCheck,
OnDestroy,
Directive,
ElementRef,
IterableDiffer,
IterableDiffers,
KeyValueDiffer,
KeyValueDiffers,
Renderer
Renderer,
IterableDiffer,
KeyValueDiffer,
CollectionChangeRecord,
KeyValueChangeRecord
} from 'angular2/core';
import {StringMapWrapper, isListLikeIterable} from 'angular2/src/facade/collection';
@ -73,66 +75,68 @@ import {StringMapWrapper, isListLikeIterable} from 'angular2/src/facade/collecti
*/
@Directive({selector: '[ngClass]', inputs: ['rawClass: ngClass', 'initialClasses: class']})
export class NgClass implements DoCheck, OnDestroy {
private _differ: any;
private _mode: string;
private _initialClasses = [];
private _rawClass;
private _iterableDiffer: IterableDiffer;
private _keyValueDiffer: KeyValueDiffer;
private _initialClasses: string[] = [];
private _rawClass: string[] | Set<string>;
constructor(private _iterableDiffers: IterableDiffers, private _keyValueDiffers: KeyValueDiffers,
private _ngEl: ElementRef, private _renderer: Renderer) {}
set initialClasses(v) {
set initialClasses(v: string) {
this._applyInitialClasses(true);
this._initialClasses = isPresent(v) && isString(v) ? v.split(' ') : [];
this._applyInitialClasses(false);
this._applyClasses(this._rawClass, false);
}
set rawClass(v) {
set rawClass(v: string | string[] | Set<string>| {[key: string]: any}) {
this._cleanupClasses(this._rawClass);
if (isString(v)) {
v = v.split(' ');
v = (<string>v).split(' ');
}
this._rawClass = v;
this._rawClass = <string[] | Set<string>>v;
this._iterableDiffer = null;
this._keyValueDiffer = null;
if (isPresent(v)) {
if (isListLikeIterable(v)) {
this._differ = this._iterableDiffers.find(v).create(null);
this._mode = 'iterable';
this._iterableDiffer = this._iterableDiffers.find(v).create(null);
} else {
this._differ = this._keyValueDiffers.find(v).create(null);
this._mode = 'keyValue';
this._keyValueDiffer = this._keyValueDiffers.find(v).create(null);
}
} else {
this._differ = null;
}
}
ngDoCheck(): void {
if (isPresent(this._differ)) {
var changes = this._differ.diff(this._rawClass);
if (isPresent(this._iterableDiffer)) {
var changes = this._iterableDiffer.diff(this._rawClass);
if (isPresent(changes)) {
if (this._mode == 'iterable') {
this._applyIterableChanges(changes);
} else {
this._applyKeyValueChanges(changes);
}
this._applyIterableChanges(changes);
}
}
if (isPresent(this._keyValueDiffer)) {
var changes = this._keyValueDiffer.diff(this._rawClass);
if (isPresent(changes)) {
this._applyKeyValueChanges(changes);
}
}
}
ngOnDestroy(): void { this._cleanupClasses(this._rawClass); }
private _cleanupClasses(rawClassVal): void {
private _cleanupClasses(rawClassVal: string[] | Set<string>| {[key: string]: any}): void {
this._applyClasses(rawClassVal, true);
this._applyInitialClasses(false);
}
private _applyKeyValueChanges(changes: any): void {
changes.forEachAddedItem((record) => { this._toggleClass(record.key, record.currentValue); });
changes.forEachChangedItem((record) => { this._toggleClass(record.key, record.currentValue); });
changes.forEachRemovedItem((record) => {
changes.forEachAddedItem(
(record: KeyValueChangeRecord) => { this._toggleClass(record.key, record.currentValue); });
changes.forEachChangedItem(
(record: KeyValueChangeRecord) => { this._toggleClass(record.key, record.currentValue); });
changes.forEachRemovedItem((record: KeyValueChangeRecord) => {
if (record.previousValue) {
this._toggleClass(record.key, false);
}
@ -140,15 +144,17 @@ export class NgClass implements DoCheck, OnDestroy {
}
private _applyIterableChanges(changes: any): void {
changes.forEachAddedItem((record) => { this._toggleClass(record.item, true); });
changes.forEachRemovedItem((record) => { this._toggleClass(record.item, false); });
changes.forEachAddedItem(
(record: CollectionChangeRecord) => { this._toggleClass(record.item, true); });
changes.forEachRemovedItem(
(record: CollectionChangeRecord) => { this._toggleClass(record.item, false); });
}
private _applyInitialClasses(isCleanup: boolean) {
this._initialClasses.forEach(className => this._toggleClass(className, !isCleanup));
}
private _applyClasses(rawClassVal: string[] | Set<string>| {[key: string]: string},
private _applyClasses(rawClassVal: string[] | Set<string>| {[key: string]: any},
isCleanup: boolean) {
if (isPresent(rawClassVal)) {
if (isArray(rawClassVal)) {
@ -156,14 +162,15 @@ export class NgClass implements DoCheck, OnDestroy {
} else if (rawClassVal instanceof Set) {
(<Set<string>>rawClassVal).forEach(className => this._toggleClass(className, !isCleanup));
} else {
StringMapWrapper.forEach(<{[k: string]: string}>rawClassVal, (expVal, className) => {
if (expVal) this._toggleClass(className, !isCleanup);
});
StringMapWrapper.forEach(<{[k: string]: any}>rawClassVal,
(expVal: any, className: string) => {
if (isPresent(expVal)) this._toggleClass(className, !isCleanup);
});
}
}
}
private _toggleClass(className: string, enabled): void {
private _toggleClass(className: string, enabled: boolean): void {
className = className.trim();
if (className.length > 0) {
if (className.indexOf(' ') > -1) {

View File

@ -10,6 +10,10 @@ import {
TrackByFn
} from 'angular2/core';
import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {
DefaultIterableDiffer,
CollectionChangeRecord
} from "../../core/change_detection/differs/default_iterable_differ";
/**
* The `NgFor` directive instantiates a template once per item from an iterable. The context for
@ -92,19 +96,19 @@ export class NgFor implements DoCheck {
}
}
private _applyChanges(changes) {
private _applyChanges(changes: DefaultIterableDiffer) {
// TODO(rado): check if change detection can produce a change record that is
// easier to consume than current.
var recordViewTuples = [];
changes.forEachRemovedItem((removedRecord) =>
var recordViewTuples: RecordViewTuple[] = [];
changes.forEachRemovedItem((removedRecord: CollectionChangeRecord) =>
recordViewTuples.push(new RecordViewTuple(removedRecord, null)));
changes.forEachMovedItem((movedRecord) =>
changes.forEachMovedItem((movedRecord: CollectionChangeRecord) =>
recordViewTuples.push(new RecordViewTuple(movedRecord, null)));
var insertTuples = this._bulkRemove(recordViewTuples);
changes.forEachAddedItem((addedRecord) =>
changes.forEachAddedItem((addedRecord: CollectionChangeRecord) =>
insertTuples.push(new RecordViewTuple(addedRecord, null)));
this._bulkInsert(insertTuples);
@ -117,9 +121,14 @@ export class NgFor implements DoCheck {
var viewRef = <EmbeddedViewRef>this._viewContainer.get(i);
viewRef.setLocal('last', i === ilen - 1);
}
changes.forEachIdentityChange((record) => {
var viewRef = <EmbeddedViewRef>this._viewContainer.get(record.currentIndex);
viewRef.setLocal('\$implicit', record.item);
});
}
private _perViewChange(view, record) {
private _perViewChange(view: EmbeddedViewRef, record: CollectionChangeRecord) {
view.setLocal('\$implicit', record.item);
view.setLocal('index', record.currentIndex);
view.setLocal('even', (record.currentIndex % 2 == 0));
@ -127,8 +136,9 @@ export class NgFor implements DoCheck {
}
private _bulkRemove(tuples: RecordViewTuple[]): RecordViewTuple[] {
tuples.sort((a, b) => a.record.previousIndex - b.record.previousIndex);
var movedTuples = [];
tuples.sort((a: RecordViewTuple, b: RecordViewTuple) =>
a.record.previousIndex - b.record.previousIndex);
var movedTuples: RecordViewTuple[] = [];
for (var i = tuples.length - 1; i >= 0; i--) {
var tuple = tuples[i];
// separate moved views from removed views.
@ -160,7 +170,7 @@ export class NgFor implements DoCheck {
class RecordViewTuple {
view: EmbeddedViewRef;
record: any;
constructor(record, view) {
constructor(record: any, view: EmbeddedViewRef) {
this.record = record;
this.view = view;
}

View File

@ -29,7 +29,7 @@ export class NgIf {
constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef) {}
set ngIf(newCondition /* boolean */) {
set ngIf(newCondition: any /* boolean */) {
if (newCondition && (isBlank(this._prevCondition) || !this._prevCondition)) {
this._prevCondition = true;
this._viewContainer.createEmbeddedView(this._templateRef);

View File

@ -7,6 +7,7 @@ import {
Renderer
} from 'angular2/core';
import {isPresent, isBlank, print} from 'angular2/src/facade/lang';
import {KeyValueChangeRecord} from "../../core/change_detection/differs/default_keyvalue_differ";
/**
* The `NgStyle` directive changes styles based on a result of expression evaluation.
@ -62,14 +63,14 @@ import {isPresent, isBlank, print} from 'angular2/src/facade/lang';
@Directive({selector: '[ngStyle]', inputs: ['rawStyle: ngStyle']})
export class NgStyle implements DoCheck {
/** @internal */
_rawStyle;
_rawStyle: {[key: string]: string};
/** @internal */
_differ: KeyValueDiffer;
constructor(private _differs: KeyValueDiffers, private _ngEl: ElementRef,
private _renderer: Renderer) {}
set rawStyle(v) {
set rawStyle(v: {[key: string]: string}) {
this._rawStyle = v;
if (isBlank(this._differ) && isPresent(v)) {
this._differ = this._differs.find(this._rawStyle).create(null);
@ -86,9 +87,12 @@ export class NgStyle implements DoCheck {
}
private _applyChanges(changes: any): void {
changes.forEachAddedItem((record) => { this._setStyle(record.key, record.currentValue); });
changes.forEachChangedItem((record) => { this._setStyle(record.key, record.currentValue); });
changes.forEachRemovedItem((record) => { this._setStyle(record.key, null); });
changes.forEachAddedItem(
(record: KeyValueChangeRecord) => { this._setStyle(record.key, record.currentValue); });
changes.forEachChangedItem(
(record: KeyValueChangeRecord) => { this._setStyle(record.key, record.currentValue); });
changes.forEachRemovedItem(
(record: KeyValueChangeRecord) => { this._setStyle(record.key, null); });
}
private _setStyle(name: string, val: string): void {

View File

@ -76,7 +76,7 @@ export class NgSwitch {
private _valueViews = new Map<any, SwitchView[]>();
private _activeViews: SwitchView[] = [];
set ngSwitch(value) {
set ngSwitch(value: any) {
// Empty the currently active ViewContainers
this._emptyAllActiveViews();
@ -93,7 +93,7 @@ export class NgSwitch {
}
/** @internal */
_onWhenValueChanged(oldWhen, newWhen, view: SwitchView): void {
_onWhenValueChanged(oldWhen: any, newWhen: any, view: SwitchView): void {
this._deregisterView(oldWhen, view);
this._registerView(newWhen, view);
@ -137,7 +137,7 @@ export class NgSwitch {
}
/** @internal */
_registerView(value, view: SwitchView): void {
_registerView(value: any, view: SwitchView): void {
var views = this._valueViews.get(value);
if (isBlank(views)) {
views = [];
@ -147,7 +147,7 @@ export class NgSwitch {
}
/** @internal */
_deregisterView(value, view: SwitchView): void {
_deregisterView(value: any, view: SwitchView): void {
// `_WHEN_DEFAULT` is used a marker for non-registered whens
if (value === _WHEN_DEFAULT) return;
var views = this._valueViews.get(value);
@ -182,7 +182,7 @@ export class NgSwitchWhen {
this._view = new SwitchView(viewContainer, templateRef);
}
set ngSwitchWhen(value) {
set ngSwitchWhen(value: any) {
this._switch._onWhenValueChanged(this._value, value, this._view);
this._value = value;
}

View File

@ -31,12 +31,34 @@ export {
NgSelectOption,
SelectControlValueAccessor
} from './forms/directives/select_control_value_accessor';
export {FORM_DIRECTIVES} from './forms/directives';
export {FORM_DIRECTIVES, RadioButtonState} from './forms/directives';
export {NG_VALIDATORS, NG_ASYNC_VALIDATORS, Validators} from './forms/validators';
export {
RequiredValidator,
MinLengthValidator,
MaxLengthValidator,
PatternValidator,
Validator
} from './forms/directives/validators';
export {FormBuilder, FORM_PROVIDERS, FORM_BINDINGS} from './forms/form_builder';
export {FormBuilder} from './forms/form_builder';
import {FormBuilder} from './forms/form_builder';
import {RadioControlRegistry} from './forms/directives/radio_control_value_accessor';
import {Type, CONST_EXPR} from 'angular2/src/facade/lang';
/**
* Shorthand set of providers used for building Angular forms.
*
* ### Example
*
* ```typescript
* bootstrap(MyApp, [FORM_PROVIDERS]);
* ```
*/
export const FORM_PROVIDERS: Type[] = CONST_EXPR([FormBuilder, RadioControlRegistry]);
/**
* See {@link FORM_PROVIDERS} instead.
*
* @deprecated
*/
export const FORM_BINDINGS = FORM_PROVIDERS;

View File

@ -8,12 +8,18 @@ import {NgForm} from './directives/ng_form';
import {DefaultValueAccessor} from './directives/default_value_accessor';
import {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
import {NumberValueAccessor} from './directives/number_value_accessor';
import {RadioControlValueAccessor} from './directives/radio_control_value_accessor';
import {NgControlStatus} from './directives/ng_control_status';
import {
SelectControlValueAccessor,
NgSelectOption
} from './directives/select_control_value_accessor';
import {RequiredValidator, MinLengthValidator, MaxLengthValidator} from './directives/validators';
import {
RequiredValidator,
MinLengthValidator,
MaxLengthValidator,
PatternValidator
} from './directives/validators';
export {NgControlName} from './directives/ng_control_name';
export {NgFormControl} from './directives/ng_form_control';
@ -23,13 +29,22 @@ export {NgFormModel} from './directives/ng_form_model';
export {NgForm} from './directives/ng_form';
export {DefaultValueAccessor} from './directives/default_value_accessor';
export {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
export {
RadioControlValueAccessor,
RadioButtonState
} from './directives/radio_control_value_accessor';
export {NumberValueAccessor} from './directives/number_value_accessor';
export {NgControlStatus} from './directives/ng_control_status';
export {
SelectControlValueAccessor,
NgSelectOption
} from './directives/select_control_value_accessor';
export {RequiredValidator, MinLengthValidator, MaxLengthValidator} from './directives/validators';
export {
RequiredValidator,
MinLengthValidator,
MaxLengthValidator,
PatternValidator
} from './directives/validators';
export {NgControl} from './directives/ng_control';
export {ControlValueAccessor} from './directives/control_value_accessor';
@ -63,9 +78,11 @@ export const FORM_DIRECTIVES: Type[] = CONST_EXPR([
NumberValueAccessor,
CheckboxControlValueAccessor,
SelectControlValueAccessor,
RadioControlValueAccessor,
NgControlStatus,
RequiredValidator,
MinLengthValidator,
MaxLengthValidator
MaxLengthValidator,
PatternValidator
]);

View File

@ -18,10 +18,10 @@ const CHECKBOX_VALUE_ACCESSOR = CONST_EXPR(new Provider(
selector:
'input[type=checkbox][ngControl],input[type=checkbox][ngFormControl],input[type=checkbox][ngModel]',
host: {'(change)': 'onChange($event.target.checked)', '(blur)': 'onTouched()'},
bindings: [CHECKBOX_VALUE_ACCESSOR]
providers: [CHECKBOX_VALUE_ACCESSOR]
})
export class CheckboxControlValueAccessor implements ControlValueAccessor {
onChange = (_) => {};
onChange = (_: any) => {};
onTouched = () => {};
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}

View File

@ -24,7 +24,7 @@ const DEFAULT_VALUE_ACCESSOR = CONST_EXPR(new Provider(
bindings: [DEFAULT_VALUE_ACCESSOR]
})
export class DefaultValueAccessor implements ControlValueAccessor {
onChange = (_) => {};
onChange = (_: any) => {};
onTouched = () => {};
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}

View File

@ -1,9 +1,14 @@
import {Validator} from './validators';
import {Control} from "../model";
export function normalizeValidator(validator: Function | Validator): Function {
export type ctrlFunc = ((c: Control) => {
[key: string]: any
});
export function normalizeValidator(validator: (ctrlFunc | Validator)): ctrlFunc {
if ((<Validator>validator).validate !== undefined) {
return (c) => (<Validator>validator).validate(c);
return (c: Control) => (<Validator>validator).validate(c);
} else {
return <Function>validator;
return <ctrlFunc>validator;
}
}

View File

@ -25,7 +25,7 @@ const NUMBER_VALUE_ACCESSOR = CONST_EXPR(new Provider(
bindings: [NUMBER_VALUE_ACCESSOR]
})
export class NumberValueAccessor implements ControlValueAccessor {
onChange = (_) => {};
onChange = (_: any) => {};
onTouched = () => {};
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}

View File

@ -0,0 +1,126 @@
import {
Directive,
ElementRef,
Renderer,
Self,
forwardRef,
Provider,
Attribute,
Input,
OnInit,
OnDestroy,
Injector,
Injectable
} from 'angular2/core';
import {
NG_VALUE_ACCESSOR,
ControlValueAccessor
} from 'angular2/src/common/forms/directives/control_value_accessor';
import {NgControl} from 'angular2/src/common/forms/directives/ng_control';
import {CONST_EXPR, looseIdentical, isPresent} from 'angular2/src/facade/lang';
import {ListWrapper} from 'angular2/src/facade/collection';
const RADIO_VALUE_ACCESSOR = CONST_EXPR(new Provider(
NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => RadioControlValueAccessor), multi: true}));
/**
* Internal class used by Angular to uncheck radio buttons with the matching name.
*/
@Injectable()
export class RadioControlRegistry {
private _accessors: any[] = [];
add(control: NgControl, accessor: RadioControlValueAccessor) {
this._accessors.push([control, accessor]);
}
remove(accessor: RadioControlValueAccessor) {
var indexToRemove = -1;
for (var i = 0; i < this._accessors.length; ++i) {
if (this._accessors[i][1] === accessor) {
indexToRemove = i;
}
}
ListWrapper.removeAt(this._accessors, indexToRemove);
}
select(accessor: RadioControlValueAccessor) {
this._accessors.forEach((c) => {
if (c[0].control.root === accessor._control.control.root && c[1] !== accessor) {
c[1].fireUncheck();
}
});
}
}
/**
* The value provided by the forms API for radio buttons.
*/
export class RadioButtonState {
constructor(public checked: boolean, public value: string) {}
}
/**
* The accessor for writing a radio control value and listening to changes that is used by the
* {@link NgModel}, {@link NgFormControl}, and {@link NgControlName} directives.
*
* ### Example
* ```
* @Component({
* template: `
* <input type="radio" name="food" [(ngModel)]="foodChicken">
* <input type="radio" name="food" [(ngModel)]="foodFish">
* `
* })
* class FoodCmp {
* foodChicken = new RadioButtonState(true, "chicken");
* foodFish = new RadioButtonState(false, "fish");
* }
* ```
*/
@Directive({
selector:
'input[type=radio][ngControl],input[type=radio][ngFormControl],input[type=radio][ngModel]',
host: {'(change)': 'onChange()', '(blur)': 'onTouched()'},
providers: [RADIO_VALUE_ACCESSOR]
})
export class RadioControlValueAccessor implements ControlValueAccessor,
OnDestroy, OnInit {
_state: RadioButtonState;
_control: NgControl;
@Input() name: string;
_fn: Function;
onChange = () => {};
onTouched = () => {};
constructor(private _renderer: Renderer, private _elementRef: ElementRef,
private _registry: RadioControlRegistry, private _injector: Injector) {}
ngOnInit(): void {
this._control = this._injector.get(NgControl);
this._registry.add(this._control, this);
}
ngOnDestroy(): void { this._registry.remove(this); }
writeValue(value: any): void {
this._state = value;
if (isPresent(value) && value.checked) {
this._renderer.setElementProperty(this._elementRef.nativeElement, 'checked', true);
}
}
registerOnChange(fn: (_: any) => {}): void {
this._fn = fn;
this.onChange = () => {
fn(new RadioButtonState(true, this._state.value));
this._registry.select(this);
};
}
fireUncheck(): void { this._fn(new RadioButtonState(false, this._state.value)); }
registerOnTouched(fn: () => {}): void { this.onTouched = fn; }
}

View File

@ -41,7 +41,7 @@ export class NgSelectOption {
})
export class SelectControlValueAccessor implements ControlValueAccessor {
value: string;
onChange = (_) => {};
onChange = (_: any) => {};
onTouched = () => {};
constructor(private _renderer: Renderer, private _elementRef: ElementRef,

View File

@ -1,5 +1,5 @@
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {isBlank, isPresent, looseIdentical} from 'angular2/src/facade/lang';
import {isBlank, isPresent, looseIdentical, hasConstructor} from 'angular2/src/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
import {ControlContainer} from './control_container';
@ -13,6 +13,7 @@ import {DefaultValueAccessor} from './default_value_accessor';
import {NumberValueAccessor} from './number_value_accessor';
import {CheckboxControlValueAccessor} from './checkbox_value_accessor';
import {SelectControlValueAccessor} from './select_control_value_accessor';
import {RadioControlValueAccessor} from './radio_control_value_accessor';
import {normalizeValidator} from './normalize_validator';
@ -31,14 +32,14 @@ export function setUpControl(control: Control, dir: NgControl): void {
dir.valueAccessor.writeValue(control.value);
// view -> model
dir.valueAccessor.registerOnChange(newValue => {
dir.valueAccessor.registerOnChange((newValue: any) => {
dir.viewToModelUpdate(newValue);
control.updateValue(newValue, {emitModelToViewChange: false});
control.markAsDirty();
});
// model -> view
control.registerOnChange(newValue => dir.valueAccessor.writeValue(newValue));
control.registerOnChange((newValue: any) => dir.valueAccessor.writeValue(newValue));
// touched
dir.valueAccessor.registerOnTouched(() => control.markAsTouched());
@ -77,15 +78,17 @@ export function selectValueAccessor(dir: NgControl,
valueAccessors: ControlValueAccessor[]): ControlValueAccessor {
if (isBlank(valueAccessors)) return null;
var defaultAccessor;
var builtinAccessor;
var customAccessor;
valueAccessors.forEach(v => {
if (v instanceof DefaultValueAccessor) {
var defaultAccessor: ControlValueAccessor;
var builtinAccessor: ControlValueAccessor;
var customAccessor: ControlValueAccessor;
valueAccessors.forEach((v: ControlValueAccessor) => {
if (hasConstructor(v, DefaultValueAccessor)) {
defaultAccessor = v;
} else if (v instanceof CheckboxControlValueAccessor || v instanceof NumberValueAccessor ||
v instanceof SelectControlValueAccessor) {
} else if (hasConstructor(v, CheckboxControlValueAccessor) ||
hasConstructor(v, NumberValueAccessor) ||
hasConstructor(v, SelectControlValueAccessor) ||
hasConstructor(v, RadioControlValueAccessor)) {
if (isPresent(builtinAccessor))
_throwError(dir, "More than one built-in value accessor matches");
builtinAccessor = v;

View File

@ -100,3 +100,32 @@ export class MaxLengthValidator implements Validator {
validate(c: Control): {[key: string]: any} { return this._validator(c); }
}
/**
* A Directive that adds the `pattern` validator to any controls marked with the
* `pattern` attribute, via the {@link NG_VALIDATORS} binding. Uses attribute value
* as the regex to validate Control value against. Follows pattern attribute
* semantics; i.e. regex must match entire Control value.
*
* ### Example
*
* ```
* <input [ngControl]="fullName" pattern="[a-zA-Z ]*">
* ```
*/
const PATTERN_VALIDATOR = CONST_EXPR(
new Provider(NG_VALIDATORS, {useExisting: forwardRef(() => PatternValidator), multi: true}));
@Directive({
selector: '[pattern][ngControl],[pattern][ngFormControl],[pattern][ngModel]',
providers: [PATTERN_VALIDATOR]
})
export class PatternValidator implements Validator {
private _validator: Function;
constructor(@Attribute("pattern") pattern: string) {
this._validator = Validators.pattern(pattern);
}
validate(c: Control): {[key: string]: any} { return this._validator(c); }
}

View File

@ -80,9 +80,10 @@ export class FormBuilder {
}
/** @internal */
_reduceControls(controlsConfig: any): {[key: string]: modelModule.AbstractControl} {
_reduceControls(controlsConfig: {[k: string]:
any}): {[key: string]: modelModule.AbstractControl} {
var controls: {[key: string]: modelModule.AbstractControl} = {};
StringMapWrapper.forEach(controlsConfig, (controlConfig, controlName) => {
StringMapWrapper.forEach(controlsConfig, (controlConfig: any, controlName: string) => {
controls[controlName] = this._createControl(controlConfig);
});
return controls;
@ -105,22 +106,4 @@ export class FormBuilder {
return this.control(controlConfig);
}
}
}
/**
* Shorthand set of providers used for building Angular forms.
*
* ### Example
*
* ```typescript
* bootstrap(MyApp, [FORM_PROVIDERS]);
* ```
*/
export const FORM_PROVIDERS: Type[] = CONST_EXPR([FormBuilder]);
/**
* See {@link FORM_PROVIDERS} instead.
*
* @deprecated
*/
export const FORM_BINDINGS = FORM_PROVIDERS;
}

View File

@ -62,7 +62,7 @@ export abstract class AbstractControl {
private _pristine: boolean = true;
private _touched: boolean = false;
private _parent: ControlGroup | ControlArray;
private _asyncValidationSubscription;
private _asyncValidationSubscription: any;
constructor(public validator: Function, public asyncValidator: Function) {}
@ -208,6 +208,16 @@ export abstract class AbstractControl {
return isPresent(this.getError(errorCode, path));
}
get root(): AbstractControl {
let x: AbstractControl = this;
while (isPresent(x._parent)) {
x = x._parent;
}
return x;
}
/** @internal */
_updateControlsErrors(): void {
this._status = this._calculateStatus();
@ -369,7 +379,8 @@ export class ControlGroup extends AbstractControl {
/** @internal */
_setParentForControls() {
StringMapWrapper.forEach(this.controls, (control, name) => { control.setParent(this); });
StringMapWrapper.forEach(
this.controls, (control: AbstractControl, name: string) => { control.setParent(this); });
}
/** @internal */
@ -378,7 +389,7 @@ export class ControlGroup extends AbstractControl {
/** @internal */
_anyControlsHaveStatus(status: string): boolean {
var res = false;
StringMapWrapper.forEach(this.controls, (control, name) => {
StringMapWrapper.forEach(this.controls, (control: AbstractControl, name: string) => {
res = res || (this.contains(name) && control.status == status);
});
return res;
@ -386,16 +397,17 @@ export class ControlGroup extends AbstractControl {
/** @internal */
_reduceValue() {
return this._reduceChildren({}, (acc, control, name) => {
acc[name] = control.value;
return acc;
});
return this._reduceChildren(
{}, (acc: {[k: string]: AbstractControl}, control: AbstractControl, name: string) => {
acc[name] = control.value;
return acc;
});
}
/** @internal */
_reduceChildren(initValue: any, fn: Function) {
var res = initValue;
StringMapWrapper.forEach(this.controls, (control, name) => {
StringMapWrapper.forEach(this.controls, (control: AbstractControl, name: string) => {
if (this._included(name)) {
res = fn(res, control, name);
}

View File

@ -1,4 +1,4 @@
import {isBlank, isPresent, CONST_EXPR} from 'angular2/src/facade/lang';
import {isBlank, isPresent, CONST_EXPR, isString} from 'angular2/src/facade/lang';
import {PromiseWrapper} from 'angular2/src/facade/promise';
import {ObservableWrapper} from 'angular2/src/facade/async';
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
@ -44,7 +44,9 @@ export class Validators {
* Validator that requires controls to have a non-empty value.
*/
static required(control: modelModule.Control): {[key: string]: boolean} {
return isBlank(control.value) || control.value == "" ? {"required": true} : null;
return isBlank(control.value) || (isString(control.value) && control.value == "") ?
{"required": true} :
null;
}
/**
@ -73,6 +75,19 @@ export class Validators {
};
}
/**
* Validator that requires a control to match a regex to its value.
*/
static pattern(pattern: string): Function {
return (control: modelModule.Control): {[key: string]: any} => {
if (isPresent(Validators.required(control))) return null;
let regex = new RegExp(`^${pattern}$`);
let v: string = control.value;
return regex.test(v) ? null :
{"pattern": {"requiredPattern": `^${pattern}$`, "actualValue": v}};
};
}
/**
* No-op validator.
*/

View File

@ -3,14 +3,6 @@
* @description
* This module provides a set of common Pipes.
*/
import {AsyncPipe} from './pipes/async_pipe';
import {UpperCasePipe} from './pipes/uppercase_pipe';
import {LowerCasePipe} from './pipes/lowercase_pipe';
import {JsonPipe} from './pipes/json_pipe';
import {SlicePipe} from './pipes/slice_pipe';
import {DatePipe} from './pipes/date_pipe';
import {DecimalPipe, PercentPipe, CurrencyPipe} from './pipes/number_pipe';
import {CONST_EXPR} from 'angular2/src/facade/lang';
export {AsyncPipe} from './pipes/async_pipe';
export {DatePipe} from './pipes/date_pipe';
@ -19,22 +11,7 @@ export {SlicePipe} from './pipes/slice_pipe';
export {LowerCasePipe} from './pipes/lowercase_pipe';
export {NumberPipe, DecimalPipe, PercentPipe, CurrencyPipe} from './pipes/number_pipe';
export {UpperCasePipe} from './pipes/uppercase_pipe';
/**
* A collection of Angular core pipes that are likely to be used in each and every
* application.
*
* This collection can be used to quickly enumerate all the built-in pipes in the `pipes`
* property of the `@Component` or `@View` decorators.
*/
export const COMMON_PIPES = CONST_EXPR([
AsyncPipe,
UpperCasePipe,
LowerCasePipe,
JsonPipe,
SlicePipe,
DecimalPipe,
PercentPipe,
CurrencyPipe,
DatePipe
]);
export {ReplacePipe} from './pipes/replace_pipe';
export {I18nPluralPipe} from './pipes/i18n_plural_pipe';
export {I18nSelectPipe} from './pipes/i18n_select_pipe';
export {COMMON_PIPES} from './pipes/common_pipes';

View File

@ -1,5 +1,5 @@
import {isBlank, isPresent, isPromise, CONST} from 'angular2/src/facade/lang';
import {Promise, ObservableWrapper, Observable, EventEmitter} from 'angular2/src/facade/async';
import {ObservableWrapper, Observable, EventEmitter} from 'angular2/src/facade/async';
import {
Pipe,
Injectable,
@ -33,7 +33,7 @@ class PromiseStrategy {
var _promiseStrategy = new PromiseStrategy();
var _observableStrategy = new ObservableStrategy();
var __unused: Promise<any>; // avoid unused import when Promise union types are erased
/**
* The `async` pipe subscribes to an Observable or Promise and returns the latest value it has
@ -81,6 +81,7 @@ export class AsyncPipe implements PipeTransform, OnDestroy {
if (isPresent(obj)) {
this._subscribe(obj);
}
this._latestReturnedValue = this._latestValue;
return this._latestValue;
}
@ -101,8 +102,8 @@ export class AsyncPipe implements PipeTransform, OnDestroy {
_subscribe(obj: Observable<any>| Promise<any>| EventEmitter<any>): void {
this._obj = obj;
this._strategy = this._selectStrategy(obj);
this._subscription =
this._strategy.createSubscription(obj, value => this._updateLatestValue(obj, value));
this._subscription = this._strategy.createSubscription(
obj, (value: Object) => this._updateLatestValue(obj, value));
}
/** @internal */

View File

@ -10,6 +10,9 @@ import {JsonPipe} from './json_pipe';
import {SlicePipe} from './slice_pipe';
import {DatePipe} from './date_pipe';
import {DecimalPipe, PercentPipe, CurrencyPipe} from './number_pipe';
import {ReplacePipe} from './replace_pipe';
import {I18nPluralPipe} from './i18n_plural_pipe';
import {I18nSelectPipe} from './i18n_select_pipe';
import {CONST_EXPR} from 'angular2/src/facade/lang';
/**
@ -28,5 +31,8 @@ export const COMMON_PIPES = CONST_EXPR([
DecimalPipe,
PercentPipe,
CurrencyPipe,
DatePipe
DatePipe,
ReplacePipe,
I18nPluralPipe,
I18nSelectPipe
]);

View File

@ -0,0 +1,62 @@
import {
CONST,
isStringMap,
StringWrapper,
isPresent,
RegExpWrapper
} from 'angular2/src/facade/lang';
import {Injectable, PipeTransform, Pipe} from 'angular2/core';
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
var interpolationExp: RegExp = RegExpWrapper.create('#');
/**
*
* Maps a value to a string that pluralizes the value properly.
*
* ## Usage
*
* expression | i18nPlural:mapping
*
* where `expression` is a number and `mapping` is an object that indicates the proper text for
* when the `expression` evaluates to 0, 1, or some other number. You can interpolate the actual
* value into the text using the `#` sign.
*
* ## Example
*
* ```
* <div>
* {{ messages.length | i18nPlural: messageMapping }}
* </div>
*
* class MyApp {
* messages: any[];
* messageMapping: any = {
* '=0': 'No messages.',
* '=1': 'One message.',
* 'other': '# messages.'
* }
* ...
* }
* ```
*
*/
@CONST()
@Pipe({name: 'i18nPlural', pure: true})
@Injectable()
export class I18nPluralPipe implements PipeTransform {
transform(value: number, args: any[] = null): string {
var key: string;
var valueStr: string;
var pluralMap: {[count: string]: string} = args[0];
if (!isStringMap(pluralMap)) {
throw new InvalidPipeArgumentException(I18nPluralPipe, pluralMap);
}
key = value === 0 || value === 1 ? `=${value}` : 'other';
valueStr = isPresent(value) ? value.toString() : '';
return StringWrapper.replaceAll(pluralMap[key], interpolationExp, valueStr);
}
}

View File

@ -0,0 +1,47 @@
import {CONST, isStringMap} from 'angular2/src/facade/lang';
import {StringMapWrapper} from 'angular2/src/facade/collection';
import {Injectable, PipeTransform, Pipe} from 'angular2/core';
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
/**
*
* Generic selector that displays the string that matches the current value.
*
* ## Usage
*
* expression | i18nSelect:mapping
*
* where `mapping` is an object that indicates the text that should be displayed
* for different values of the provided `expression`.
*
* ## Example
*
* ```
* <div>
* {{ gender | i18nSelect: inviteMap }}
* </div>
*
* class MyApp {
* gender: string = 'male';
* inviteMap: any = {
* 'male': 'Invite her.',
* 'female': 'Invite him.',
* 'other': 'Invite them.'
* }
* ...
* }
* ```
*/
@CONST()
@Pipe({name: 'i18nSelect', pure: true})
@Injectable()
export class I18nSelectPipe implements PipeTransform {
transform(value: string, args: any[] = null): string {
var mapping: {[key: string]: string} = args[0];
if (!isStringMap(mapping)) {
throw new InvalidPipeArgumentException(I18nSelectPipe, mapping);
}
return StringMapWrapper.contains(mapping, value) ? mapping[value] : mapping['other'];
}
}

View File

@ -0,0 +1,91 @@
import {
isBlank,
isString,
isNumber,
isFunction,
RegExpWrapper,
StringWrapper
} from 'angular2/src/facade/lang';
import {BaseException} from 'angular2/src/facade/exceptions';
import {Injectable, PipeTransform, Pipe} from 'angular2/core';
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
/**
* Creates a new String with some or all of the matches of a pattern replaced by
* a replacement.
*
* The pattern to be matched is specified by the 'pattern' parameter.
*
* The replacement to be set is specified by the 'replacement' parameter.
*
* An optional 'flags' parameter can be set.
*
* ### Usage
*
* expression | replace:pattern:replacement
*
* All behavior is based on the expected behavior of the JavaScript API
* String.prototype.replace() function.
*
* Where the input expression is a [String] or [Number] (to be treated as a string),
* the `pattern` is a [String] or [RegExp],
* the 'replacement' is a [String] or [Function].
*
* --Note--: The 'pattern' parameter will be converted to a RegExp instance. Make sure to escape the
* string properly if you are matching for regular expression special characters like parenthesis,
* brackets etc.
*/
@Pipe({name: 'replace'})
@Injectable()
export class ReplacePipe implements PipeTransform {
transform(value: any, args: any[]): any {
if (isBlank(args) || args.length !== 2) {
throw new BaseException('ReplacePipe requires two arguments');
}
if (isBlank(value)) {
return value;
}
if (!this._supportedInput(value)) {
throw new InvalidPipeArgumentException(ReplacePipe, value);
}
var input = value.toString();
var pattern = args[0];
var replacement = args[1];
if (!this._supportedPattern(pattern)) {
throw new InvalidPipeArgumentException(ReplacePipe, pattern);
}
if (!this._supportedReplacement(replacement)) {
throw new InvalidPipeArgumentException(ReplacePipe, replacement);
}
// template fails with literal RegExp e.g /pattern/igm
// var rgx = pattern instanceof RegExp ? pattern : RegExpWrapper.create(pattern);
if (isFunction(replacement)) {
var rgxPattern = isString(pattern) ? RegExpWrapper.create(pattern) : pattern;
return StringWrapper.replaceAllMapped(input, rgxPattern, replacement);
}
if (pattern instanceof RegExp) {
// use the replaceAll variant
return StringWrapper.replaceAll(input, pattern, replacement);
}
return StringWrapper.replace(input, pattern, replacement);
}
private _supportedInput(input: any): boolean { return isString(input) || isNumber(input); }
private _supportedPattern(pattern: any): boolean {
return isString(pattern) || pattern instanceof RegExp;
}
private _supportedReplacement(replacement: any): boolean {
return isString(replacement) || isFunction(replacement);
}
}

View File

@ -2,8 +2,10 @@ import {
isPresent,
isBlank,
normalizeBool,
normalizeBlank,
serializeEnum,
Type,
isString,
RegExpWrapper,
StringWrapper
} from 'angular2/src/facade/lang';
@ -22,7 +24,17 @@ import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/linker/i
// group 2: "event" from "(event)"
var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g;
export abstract class CompileMetadataWithType {
export abstract class CompileMetadataWithIdentifier {
static fromJson(data: {[key: string]: any}): CompileMetadataWithIdentifier {
return _COMPILE_METADATA_FROM_JSON[data['class']](data);
}
abstract toJson(): {[key: string]: any};
get identifier(): CompileIdentifierMetadata { return unimplemented(); }
}
export abstract class CompileMetadataWithType extends CompileMetadataWithIdentifier {
static fromJson(data: {[key: string]: any}): CompileMetadataWithType {
return _COMPILE_METADATA_FROM_JSON[data['class']](data);
}
@ -30,35 +42,273 @@ export abstract class CompileMetadataWithType {
abstract toJson(): {[key: string]: any};
get type(): CompileTypeMetadata { return unimplemented(); }
get identifier(): CompileIdentifierMetadata { return unimplemented(); }
}
/**
* Metadata regarding compilation of a type.
*/
export class CompileTypeMetadata {
runtime: Type;
export class CompileIdentifierMetadata implements CompileMetadataWithIdentifier {
runtime: any;
name: string;
prefix: string;
moduleUrl: string;
isHost: boolean;
constructor({runtime, name, moduleUrl, isHost}:
{runtime?: Type, name?: string, moduleUrl?: string, isHost?: boolean} = {}) {
constConstructor: boolean;
constructor({runtime, name, moduleUrl, prefix, constConstructor}: {
runtime?: any,
name?: string,
moduleUrl?: string,
prefix?: string,
constConstructor?: boolean
} = {}) {
this.runtime = runtime;
this.name = name;
this.prefix = prefix;
this.moduleUrl = moduleUrl;
this.isHost = normalizeBool(isHost);
this.constConstructor = constConstructor;
}
static fromJson(data: {[key: string]: any}): CompileTypeMetadata {
return new CompileTypeMetadata(
{name: data['name'], moduleUrl: data['moduleUrl'], isHost: data['isHost']});
static fromJson(data: {[key: string]: any}): CompileIdentifierMetadata {
return new CompileIdentifierMetadata({
name: data['name'],
prefix: data['prefix'],
moduleUrl: data['moduleUrl'],
constConstructor: data['constConstructor']
});
}
toJson(): {[key: string]: any} {
return {
// Note: Runtime type can't be serialized...
'class': 'Identifier',
'name': this.name,
'moduleUrl': this.moduleUrl,
'isHost': this.isHost
'prefix': this.prefix,
'constConstructor': this.constConstructor
};
}
get identifier(): CompileIdentifierMetadata { return this; }
}
export class CompileDiDependencyMetadata {
isAttribute: boolean;
isSelf: boolean;
isHost: boolean;
isSkipSelf: boolean;
isOptional: boolean;
query: CompileQueryMetadata;
viewQuery: CompileQueryMetadata;
token: CompileIdentifierMetadata | string;
constructor({isAttribute, isSelf, isHost, isSkipSelf, isOptional, query, viewQuery, token}: {
isAttribute?: boolean,
isSelf?: boolean,
isHost?: boolean,
isSkipSelf?: boolean,
isOptional?: boolean,
query?: CompileQueryMetadata,
viewQuery?: CompileQueryMetadata,
token?: CompileIdentifierMetadata | string
} = {}) {
this.isAttribute = normalizeBool(isAttribute);
this.isSelf = normalizeBool(isSelf);
this.isHost = normalizeBool(isHost);
this.isSkipSelf = normalizeBool(isSkipSelf);
this.isOptional = normalizeBool(isOptional);
this.query = query;
this.viewQuery = viewQuery;
this.token = token;
}
static fromJson(data: {[key: string]: any}): CompileDiDependencyMetadata {
return new CompileDiDependencyMetadata({
token: objFromJson(data['token'], CompileIdentifierMetadata.fromJson),
query: objFromJson(data['query'], CompileQueryMetadata.fromJson),
viewQuery: objFromJson(data['viewQuery'], CompileQueryMetadata.fromJson),
isAttribute: data['isAttribute'],
isSelf: data['isSelf'],
isHost: data['isHost'],
isSkipSelf: data['isSkipSelf'],
isOptional: data['isOptional']
});
}
toJson(): {[key: string]: any} {
return {
// Note: Runtime type can't be serialized...
'token': objToJson(this.token),
'query': objToJson(this.query),
'viewQuery': objToJson(this.viewQuery),
'isAttribute': this.isAttribute,
'isSelf': this.isSelf,
'isHost': this.isHost,
'isSkipSelf': this.isSkipSelf,
'isOptional': this.isOptional
};
}
}
export class CompileProviderMetadata {
token: CompileIdentifierMetadata | string;
useClass: CompileTypeMetadata;
useValue: any;
useExisting: CompileIdentifierMetadata | string;
useFactory: CompileFactoryMetadata;
deps: CompileDiDependencyMetadata[];
multi: boolean;
constructor({token, useClass, useValue, useExisting, useFactory, deps, multi}: {
token?: CompileIdentifierMetadata | string,
useClass?: CompileTypeMetadata,
useValue?: any,
useExisting?: CompileIdentifierMetadata | string,
useFactory?: CompileFactoryMetadata,
deps?: CompileDiDependencyMetadata[],
multi?: boolean
}) {
this.token = token;
this.useClass = useClass;
this.useValue = useValue;
this.useExisting = useExisting;
this.useFactory = useFactory;
this.deps = deps;
this.multi = multi;
}
static fromJson(data: {[key: string]: any}): CompileProviderMetadata {
return new CompileProviderMetadata({
token: objFromJson(data['token'], CompileIdentifierMetadata.fromJson),
useClass: objFromJson(data['useClass'], CompileTypeMetadata.fromJson)
});
}
toJson(): {[key: string]: any} {
return {
// Note: Runtime type can't be serialized...
'token': objToJson(this.token),
'useClass': objToJson(this.useClass)
};
}
}
export class CompileFactoryMetadata implements CompileIdentifierMetadata {
runtime: Function;
name: string;
prefix: string;
moduleUrl: string;
constConstructor: boolean;
diDeps: CompileDiDependencyMetadata[];
constructor({runtime, name, moduleUrl, constConstructor, diDeps}: {
runtime?: Function,
name?: string,
moduleUrl?: string,
constConstructor?: boolean,
diDeps?: CompileDiDependencyMetadata[]
}) {
this.runtime = runtime;
this.name = name;
this.moduleUrl = moduleUrl;
this.diDeps = diDeps;
this.constConstructor = constConstructor;
}
get identifier(): CompileIdentifierMetadata { return this; }
toJson() { return null; }
}
/**
* Metadata regarding compilation of a type.
*/
export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMetadataWithType {
runtime: Type;
name: string;
prefix: string;
moduleUrl: string;
isHost: boolean;
constConstructor: boolean;
diDeps: CompileDiDependencyMetadata[];
constructor({runtime, name, moduleUrl, prefix, isHost, constConstructor, diDeps}: {
runtime?: Type,
name?: string,
moduleUrl?: string,
prefix?: string,
isHost?: boolean,
constConstructor?: boolean,
diDeps?: CompileDiDependencyMetadata[]
} = {}) {
this.runtime = runtime;
this.name = name;
this.moduleUrl = moduleUrl;
this.prefix = prefix;
this.isHost = normalizeBool(isHost);
this.constConstructor = constConstructor;
this.diDeps = normalizeBlank(diDeps);
}
static fromJson(data: {[key: string]: any}): CompileTypeMetadata {
return new CompileTypeMetadata({
name: data['name'],
moduleUrl: data['moduleUrl'],
prefix: data['prefix'],
isHost: data['isHost'],
constConstructor: data['constConstructor'],
diDeps: arrayFromJson(data['diDeps'], CompileDiDependencyMetadata.fromJson)
});
}
get identifier(): CompileIdentifierMetadata { return this; }
get type(): CompileTypeMetadata { return this; }
toJson(): {[key: string]: any} {
return {
// Note: Runtime type can't be serialized...
'class': 'Type',
'name': this.name,
'moduleUrl': this.moduleUrl,
'prefix': this.prefix,
'isHost': this.isHost,
'constConstructor': this.constConstructor,
'diDeps': arrayToJson(this.diDeps)
};
}
}
export class CompileQueryMetadata {
selectors: Array<CompileIdentifierMetadata | string>;
descendants: boolean;
first: boolean;
propertyName: string;
constructor({selectors, descendants, first, propertyName}: {
selectors?: Array<CompileIdentifierMetadata | string>,
descendants?: boolean,
first?: boolean,
propertyName?: string
} = {}) {
this.selectors = selectors;
this.descendants = descendants;
this.first = normalizeBool(first);
this.propertyName = propertyName;
}
static fromJson(data: {[key: string]: any}): CompileQueryMetadata {
return new CompileQueryMetadata({
selectors: arrayFromJson(data['selectors'], CompileIdentifierMetadata.fromJson),
descendants: data['descendants'],
first: data['first'],
propertyName: data['propertyName']
});
}
toJson(): {[key: string]: any} {
return {
// Note: Runtime type can't be serialized...
'selectors': arrayToJson(this.selectors),
'descendants': this.descendants,
'first': this.first,
'propertyName': this.propertyName
};
}
}
@ -120,7 +370,8 @@ export class CompileTemplateMetadata {
*/
export class CompileDirectiveMetadata implements CompileMetadataWithType {
static create({type, isComponent, dynamicLoadable, selector, exportAs, changeDetection, inputs,
outputs, host, lifecycleHooks, template}: {
outputs, host, lifecycleHooks, providers, viewProviders, queries, viewQueries,
template}: {
type?: CompileTypeMetadata,
isComponent?: boolean,
dynamicLoadable?: boolean,
@ -131,6 +382,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
outputs?: string[],
host?: {[key: string]: string},
lifecycleHooks?: LifecycleHooks[],
providers?: Array<CompileProviderMetadata | CompileTypeMetadata | any[]>,
viewProviders?: Array<CompileProviderMetadata | CompileTypeMetadata | any[]>,
queries?: CompileQueryMetadata[],
viewQueries?: CompileQueryMetadata[],
template?: CompileTemplateMetadata
} = {}): CompileDirectiveMetadata {
var hostListeners: {[key: string]: string} = {};
@ -180,10 +435,13 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
hostProperties: hostProperties,
hostAttributes: hostAttributes,
lifecycleHooks: isPresent(lifecycleHooks) ? lifecycleHooks : [],
providers: providers,
viewProviders: viewProviders,
queries: queries,
viewQueries: viewQueries,
template: template
});
}
type: CompileTypeMetadata;
isComponent: boolean;
dynamicLoadable: boolean;
@ -196,9 +454,14 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
hostProperties: {[key: string]: string};
hostAttributes: {[key: string]: string};
lifecycleHooks: LifecycleHooks[];
providers: Array<CompileProviderMetadata | CompileTypeMetadata | any[]>;
viewProviders: Array<CompileProviderMetadata | CompileTypeMetadata | any[]>;
queries: CompileQueryMetadata[];
viewQueries: CompileQueryMetadata[];
template: CompileTemplateMetadata;
constructor({type, isComponent, dynamicLoadable, selector, exportAs, changeDetection, inputs,
outputs, hostListeners, hostProperties, hostAttributes, lifecycleHooks, template}: {
outputs, hostListeners, hostProperties, hostAttributes, lifecycleHooks, providers,
viewProviders, queries, viewQueries, template}: {
type?: CompileTypeMetadata,
isComponent?: boolean,
dynamicLoadable?: boolean,
@ -211,6 +474,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
hostProperties?: {[key: string]: string},
hostAttributes?: {[key: string]: string},
lifecycleHooks?: LifecycleHooks[],
providers?: Array<CompileProviderMetadata | CompileTypeMetadata | any[]>,
viewProviders?: Array<CompileProviderMetadata | CompileTypeMetadata | any[]>,
queries?: CompileQueryMetadata[],
viewQueries?: CompileQueryMetadata[],
template?: CompileTemplateMetadata
} = {}) {
this.type = type;
@ -225,9 +492,15 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
this.hostProperties = hostProperties;
this.hostAttributes = hostAttributes;
this.lifecycleHooks = lifecycleHooks;
this.providers = normalizeBlank(providers);
this.viewProviders = normalizeBlank(viewProviders);
this.queries = queries;
this.viewQueries = viewQueries;
this.template = template;
}
get identifier(): CompileIdentifierMetadata { return this.type; }
static fromJson(data: {[key: string]: any}): CompileDirectiveMetadata {
return new CompileDirectiveMetadata({
isComponent: data['isComponent'],
@ -246,7 +519,8 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
lifecycleHooks:
(<any[]>data['lifecycleHooks']).map(hookValue => LIFECYCLE_HOOKS_VALUES[hookValue]),
template: isPresent(data['template']) ? CompileTemplateMetadata.fromJson(data['template']) :
data['template']
data['template'],
providers: arrayFromJson(data['providers'], CompileProviderMetadata.fromJson)
});
}
@ -266,7 +540,8 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
'hostProperties': this.hostProperties,
'hostAttributes': this.hostAttributes,
'lifecycleHooks': this.lifecycleHooks.map(hook => serializeEnum(hook)),
'template': isPresent(this.template) ? this.template.toJson() : this.template
'template': isPresent(this.template) ? this.template.toJson() : this.template,
'providers': arrayToJson(this.providers)
};
}
}
@ -293,7 +568,11 @@ export function createHostComponentMeta(componentType: CompileTypeMetadata,
lifecycleHooks: [],
isComponent: true,
dynamicLoadable: false,
selector: '*'
selector: '*',
providers: [],
viewProviders: [],
queries: [],
viewQueries: []
});
}
@ -308,6 +587,7 @@ export class CompilePipeMetadata implements CompileMetadataWithType {
this.name = name;
this.pure = normalizeBool(pure);
}
get identifier(): CompileIdentifierMetadata { return this.type; }
static fromJson(data: {[key: string]: any}): CompilePipeMetadata {
return new CompilePipeMetadata({
@ -329,5 +609,23 @@ export class CompilePipeMetadata implements CompileMetadataWithType {
var _COMPILE_METADATA_FROM_JSON = {
'Directive': CompileDirectiveMetadata.fromJson,
'Pipe': CompilePipeMetadata.fromJson
'Pipe': CompilePipeMetadata.fromJson,
'Type': CompileTypeMetadata.fromJson,
'Identifier': CompileIdentifierMetadata.fromJson
};
function arrayFromJson(obj: any[], fn: (a: {[key: string]: any}) => any): any {
return isBlank(obj) ? null : obj.map(o => objFromJson(o, fn));
}
function arrayToJson(obj: any[]): string | {[key: string]: any} {
return isBlank(obj) ? null : obj.map(objToJson);
}
function objFromJson(obj: any, fn: (a: {[key: string]: any}) => any): any {
return (isString(obj) || isBlank(obj)) ? obj : fn(obj);
}
function objToJson(obj: any): string | {[key: string]: any} {
return (isString(obj) || isBlank(obj)) ? obj : obj.toJson();
}

View File

@ -34,8 +34,8 @@ export class HtmlToken {
}
export class HtmlTokenError extends ParseError {
constructor(errorMsg: string, public tokenType: HtmlTokenType, location: ParseLocation) {
super(location, errorMsg);
constructor(errorMsg: string, public tokenType: HtmlTokenType, span: ParseSourceSpan) {
super(span, errorMsg);
}
}
@ -125,7 +125,8 @@ class _HtmlTokenizer {
private _processCarriageReturns(content: string): string {
// http://www.w3.org/TR/html5/syntax.html#preprocessing-the-input-stream
// In order to keep the original position in the source, we can not pre-process it.
// In order to keep the original position in the source, we can not
// pre-process it.
// Instead CRs are processed right before instantiating the tokens.
return StringWrapper.replaceAll(content, CR_OR_CRLF_REGEXP, '\n');
}
@ -168,6 +169,16 @@ class _HtmlTokenizer {
return new ParseLocation(this.file, this.index, this.line, this.column);
}
private _getSpan(start?: ParseLocation, end?: ParseLocation): ParseSourceSpan {
if (isBlank(start)) {
start = this._getLocation();
}
if (isBlank(end)) {
end = this._getLocation();
}
return new ParseSourceSpan(start, end);
}
private _beginToken(type: HtmlTokenType, start: ParseLocation = null) {
if (isBlank(start)) {
start = this._getLocation();
@ -188,8 +199,8 @@ class _HtmlTokenizer {
return token;
}
private _createError(msg: string, position: ParseLocation): ControlFlowError {
var error = new HtmlTokenError(msg, this.currentTokenType, position);
private _createError(msg: string, span: ParseSourceSpan): ControlFlowError {
var error = new HtmlTokenError(msg, this.currentTokenType, span);
this.currentTokenStart = null;
this.currentTokenType = null;
return new ControlFlowError(error);
@ -197,7 +208,7 @@ class _HtmlTokenizer {
private _advance() {
if (this.index >= this.length) {
throw this._createError(unexpectedCharacterErrorMsg($EOF), this._getLocation());
throw this._createError(unexpectedCharacterErrorMsg($EOF), this._getSpan());
}
if (this.peek === $LF) {
this.line++;
@ -228,7 +239,8 @@ class _HtmlTokenizer {
private _requireCharCode(charCode: number) {
var location = this._getLocation();
if (!this._attemptCharCode(charCode)) {
throw this._createError(unexpectedCharacterErrorMsg(this.peek), location);
throw this._createError(unexpectedCharacterErrorMsg(this.peek),
this._getSpan(location, location));
}
}
@ -253,7 +265,7 @@ class _HtmlTokenizer {
private _requireStr(chars: string) {
var location = this._getLocation();
if (!this._attemptStr(chars)) {
throw this._createError(unexpectedCharacterErrorMsg(this.peek), location);
throw this._createError(unexpectedCharacterErrorMsg(this.peek), this._getSpan(location));
}
}
@ -267,7 +279,7 @@ class _HtmlTokenizer {
var start = this._getLocation();
this._attemptCharCodeUntilFn(predicate);
if (this.index - start.offset < len) {
throw this._createError(unexpectedCharacterErrorMsg(this.peek), start);
throw this._createError(unexpectedCharacterErrorMsg(this.peek), this._getSpan(start, start));
}
}
@ -295,7 +307,7 @@ class _HtmlTokenizer {
let numberStart = this._getLocation().offset;
this._attemptCharCodeUntilFn(isDigitEntityEnd);
if (this.peek != $SEMICOLON) {
throw this._createError(unexpectedCharacterErrorMsg(this.peek), this._getLocation());
throw this._createError(unexpectedCharacterErrorMsg(this.peek), this._getSpan());
}
this._advance();
let strNum = this.input.substring(numberStart, this.index - 1);
@ -304,7 +316,7 @@ class _HtmlTokenizer {
return StringWrapper.fromCharCode(charCode);
} catch (e) {
let entity = this.input.substring(start.offset + 1, this.index - 1);
throw this._createError(unknownEntityErrorMsg(entity), start);
throw this._createError(unknownEntityErrorMsg(entity), this._getSpan(start));
}
} else {
let startPosition = this._savePosition();
@ -317,7 +329,7 @@ class _HtmlTokenizer {
let name = this.input.substring(start.offset + 1, this.index - 1);
let char = NAMED_ENTITIES[name];
if (isBlank(char)) {
throw this._createError(unknownEntityErrorMsg(name), start);
throw this._createError(unknownEntityErrorMsg(name), this._getSpan(start));
}
return char;
}
@ -394,7 +406,7 @@ class _HtmlTokenizer {
let lowercaseTagName;
try {
if (!isAsciiLetter(this.peek)) {
throw this._createError(unexpectedCharacterErrorMsg(this.peek), this._getLocation());
throw this._createError(unexpectedCharacterErrorMsg(this.peek), this._getSpan());
}
var nameStart = this.index;
this._consumeTagOpenStart(start);

View File

@ -16,16 +16,14 @@ import {HtmlAst, HtmlAttrAst, HtmlTextAst, HtmlElementAst} from './html_ast';
import {Injectable} from 'angular2/src/core/di';
import {HtmlToken, HtmlTokenType, tokenizeHtml} from './html_lexer';
import {ParseError, ParseLocation, ParseSourceSpan} from './parse_util';
import {HtmlTagDefinition, getHtmlTagDefinition, getNsPrefix} from './html_tags';
import {HtmlTagDefinition, getHtmlTagDefinition, getNsPrefix, mergeNsAndName} from './html_tags';
export class HtmlTreeError extends ParseError {
static create(elementName: string, location: ParseLocation, msg: string): HtmlTreeError {
return new HtmlTreeError(elementName, location, msg);
static create(elementName: string, span: ParseSourceSpan, msg: string): HtmlTreeError {
return new HtmlTreeError(elementName, span, msg);
}
constructor(public elementName: string, location: ParseLocation, msg: string) {
super(location, msg);
}
constructor(public elementName: string, span: ParseSourceSpan, msg: string) { super(span, msg); }
}
export class HtmlParseTreeResult {
@ -146,7 +144,7 @@ class TreeBuilder {
selfClosing = true;
if (getNsPrefix(fullName) == null && !getHtmlTagDefinition(fullName).isVoid) {
this.errors.push(HtmlTreeError.create(
fullName, startTagToken.sourceSpan.start,
fullName, startTagToken.sourceSpan,
`Only void and foreign elements can be self closed "${startTagToken.parts[1]}"`));
}
} else if (this.peek.type === HtmlTokenType.TAG_OPEN_END) {
@ -189,10 +187,10 @@ class TreeBuilder {
if (getHtmlTagDefinition(fullName).isVoid) {
this.errors.push(
HtmlTreeError.create(fullName, endTagToken.sourceSpan.start,
HtmlTreeError.create(fullName, endTagToken.sourceSpan,
`Void elements do not have end tags "${endTagToken.parts[1]}"`));
} else if (!this._popElement(fullName)) {
this.errors.push(HtmlTreeError.create(fullName, endTagToken.sourceSpan.start,
this.errors.push(HtmlTreeError.create(fullName, endTagToken.sourceSpan,
`Unexpected closing tag "${endTagToken.parts[1]}"`));
}
}
@ -238,10 +236,6 @@ class TreeBuilder {
}
}
function mergeNsAndName(prefix: string, localName: string): string {
return isPresent(prefix) ? `@${prefix}:${localName}` : localName;
}
function getElementFullName(prefix: string, localName: string,
parentElement: HtmlElementAst): string {
if (isBlank(prefix)) {

View File

@ -420,3 +420,7 @@ export function splitNsName(elementName: string): string[] {
export function getNsPrefix(elementName: string): string {
return splitNsName(elementName)[0];
}
export function mergeNsAndName(prefix: string, localName: string): string {
return isPresent(prefix) ? `@${prefix}:${localName}` : localName;
}

View File

@ -9,12 +9,20 @@ export class ParseSourceFile {
constructor(public content: string, public url: string) {}
}
export abstract class ParseError {
constructor(public location: ParseLocation, public msg: string) {}
export class ParseSourceSpan {
constructor(public start: ParseLocation, public end: ParseLocation) {}
toString(): string {
var source = this.location.file.content;
var ctxStart = this.location.offset;
return this.start.file.content.substring(this.start.offset, this.end.offset);
}
}
export abstract class ParseError {
constructor(public span: ParseSourceSpan, public msg: string) {}
toString(): string {
var source = this.span.start.file.content;
var ctxStart = this.span.start.offset;
if (ctxStart > source.length - 1) {
ctxStart = source.length - 1;
}
@ -44,17 +52,9 @@ export abstract class ParseError {
}
}
let context = source.substring(ctxStart, this.location.offset) + '[ERROR ->]' +
source.substring(this.location.offset, ctxEnd + 1);
let context = source.substring(ctxStart, this.span.start.offset) + '[ERROR ->]' +
source.substring(this.span.start.offset, ctxEnd + 1);
return `${this.msg} ("${context}"): ${this.location}`;
}
}
export class ParseSourceSpan {
constructor(public start: ParseLocation, public end: ParseLocation) {}
toString(): string {
return this.start.file.content.substring(this.start.offset, this.end.offset);
return `${this.msg} ("${context}"): ${this.span.start}`;
}
}

View File

@ -4,7 +4,6 @@ import {TemplateCompiler} from './template_compiler';
import {Injectable} from 'angular2/src/core/di';
import {Type} from 'angular2/src/facade/lang';
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
export abstract class RuntimeCompiler extends Compiler {
abstract compileInHost(componentType: Type): Promise<HostViewFactoryRef>;

View File

@ -3,7 +3,7 @@ import {SourceModule, SourceExpression, moduleRef} from './source_module';
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
import {XHR} from 'angular2/src/compiler/xhr';
import {IS_DART, StringWrapper, isBlank} from 'angular2/src/facade/lang';
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
import {PromiseWrapper} from 'angular2/src/facade/async';
import {ShadowCss} from 'angular2/src/compiler/shadow_css';
import {UrlResolver} from 'angular2/src/compiler/url_resolver';
import {extractStyleUrls} from './style_url_resolver';

View File

@ -14,7 +14,7 @@ import {
MapWrapper,
StringMapWrapper
} from 'angular2/src/facade/collection';
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
import {PromiseWrapper} from 'angular2/src/facade/async';
import {
createHostComponentMeta,
CompileDirectiveMetadata,
@ -109,6 +109,7 @@ export class TemplateCompiler {
hostProperties: directive.hostProperties,
hostAttributes: directive.hostAttributes,
lifecycleHooks: directive.lifecycleHooks,
providers: directive.providers,
template: normalizedTemplate
}));
}

View File

@ -5,7 +5,7 @@ import {
} from './directive_metadata';
import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {BaseException} from 'angular2/src/facade/exceptions';
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
import {PromiseWrapper} from 'angular2/src/facade/async';
import {XHR} from 'angular2/src/compiler/xhr';
import {UrlResolver} from 'angular2/src/compiler/url_resolver';

View File

@ -7,11 +7,10 @@ import {Parser, AST, ASTWithSource} from 'angular2/src/core/change_detection/cha
import {TemplateBinding} from 'angular2/src/core/change_detection/parser/ast';
import {CompileDirectiveMetadata, CompilePipeMetadata} from './directive_metadata';
import {HtmlParser} from './html_parser';
import {splitNsName} from './html_tags';
import {splitNsName, mergeNsAndName} from './html_tags';
import {ParseSourceSpan, ParseError, ParseLocation} from './parse_util';
import {RecursiveAstVisitor, BindingPipe} from 'angular2/src/core/change_detection/parser/ast';
import {
ElementAst,
BoundElementPropertyAst,
@ -80,7 +79,7 @@ var TEXT_CSS_SELECTOR = CssSelector.parse('*')[0];
export const TEMPLATE_TRANSFORMS = CONST_EXPR(new OpaqueToken('TemplateTransforms'));
export class TemplateParseError extends ParseError {
constructor(message: string, location: ParseLocation) { super(location, message); }
constructor(message: string, span: ParseSourceSpan) { super(span, message); }
}
@Injectable()
@ -129,7 +128,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
}
private _reportError(message: string, sourceSpan: ParseSourceSpan) {
this.errors.push(new TemplateParseError(message, sourceSpan.start));
this.errors.push(new TemplateParseError(message, sourceSpan));
}
private _parseInterpolation(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
@ -433,8 +432,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
var parts = splitAtColon(name, [null, name]);
var target = parts[0];
var eventName = parts[1];
targetEvents.push(new BoundEventAst(eventName, target,
this._parseAction(expression, sourceSpan), sourceSpan));
var ast = this._parseAction(expression, sourceSpan);
targetMatchableAttrs.push([name, ast.source]);
targetEvents.push(new BoundEventAst(eventName, target, ast, sourceSpan));
// Don't detect directives for event names for now,
// so don't add the event name to the matchableAttrs
}
@ -583,6 +583,12 @@ class TemplateParseVisitor implements HtmlAstVisitor {
} else {
if (parts[0] == ATTRIBUTE_PREFIX) {
boundPropertyName = parts[1];
let nsSeparatorIdx = boundPropertyName.indexOf(':');
if (nsSeparatorIdx > -1) {
let ns = boundPropertyName.substring(0, nsSeparatorIdx);
let name = boundPropertyName.substring(nsSeparatorIdx + 1);
boundPropertyName = mergeNsAndName(ns, name);
}
bindingType = PropertyBindingType.Attribute;
} else if (parts[0] == CLASS_PREFIX) {
boundPropertyName = parts[1];

View File

@ -1,5 +1,3 @@
import {Promise} from 'angular2/src/facade/async';
// TODO: vsavkin rename it into TemplateLoader
/**
* An interface for retrieving documents by URL that the compiler uses

View File

@ -2,7 +2,7 @@ import {XHR} from 'angular2/src/compiler/xhr';
import {ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection';
import {isBlank, isPresent, normalizeBlank} from 'angular2/src/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
import {PromiseCompleter, PromiseWrapper, Promise} from 'angular2/src/facade/async';
import {PromiseCompleter, PromiseWrapper} from 'angular2/src/facade/async';
/**
* A mock implementation of {@link XHR} that allows outgoing requests to be mocked

View File

@ -15,12 +15,7 @@ import {
PLATFORM_INITIALIZER,
APP_INITIALIZER
} from './application_tokens';
import {
Promise,
PromiseWrapper,
PromiseCompleter,
ObservableWrapper
} from 'angular2/src/facade/async';
import {PromiseWrapper, PromiseCompleter, ObservableWrapper} from 'angular2/src/facade/async';
import {ListWrapper} from 'angular2/src/facade/collection';
import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/testability';
import {
@ -255,7 +250,7 @@ export class PlatformRef_ extends PlatformRef {
provide(ApplicationRef, {useFactory: (): ApplicationRef => app, deps: []})
]);
var exceptionHandler;
var exceptionHandler: Function;
try {
injector = this.injector.resolveAndCreateChild(providers);
exceptionHandler = injector.get(ExceptionHandler);
@ -432,7 +427,7 @@ export class ApplicationRef_ extends ApplicationRef {
try {
var injector: Injector = this._injector.resolveAndCreateChild(componentProviders);
var compRefToken: Promise<ComponentRef> = injector.get(APP_COMPONENT_REF_PROMISE);
var tick = (componentRef) => {
var tick = (componentRef: ComponentRef) => {
this._loadComponent(componentRef);
completer.resolve(componentRef);
};
@ -456,32 +451,32 @@ export class ApplicationRef_ extends ApplicationRef {
});
return completer.promise.then(_ => {
let c = this._injector.get(Console);
let modeDescription =
assertionsEnabled() ?
"in the development mode. Call enableProdMode() to enable the production mode." :
"in the production mode. Call enableDevMode() to enable the development mode.";
c.log(`Angular 2 is running ${modeDescription}`);
if (assertionsEnabled()) {
c.log(
"Angular 2 is running in the development mode. Call enableProdMode() to enable the production mode.");
}
return _;
});
}
/** @internal */
_loadComponent(ref): void {
var appChangeDetector = (<ElementRef_>ref.location).internalElement.parentView.changeDetector;
_loadComponent(componentRef: ComponentRef): void {
var appChangeDetector =
(<ElementRef_>componentRef.location).internalElement.parentView.changeDetector;
this._changeDetectorRefs.push(appChangeDetector.ref);
this.tick();
this._rootComponents.push(ref);
this._bootstrapListeners.forEach((listener) => listener(ref));
this._rootComponents.push(componentRef);
this._bootstrapListeners.forEach((listener) => listener(componentRef));
}
/** @internal */
_unloadComponent(ref): void {
if (!ListWrapper.contains(this._rootComponents, ref)) {
_unloadComponent(componentRef: ComponentRef): void {
if (!ListWrapper.contains(this._rootComponents, componentRef)) {
return;
}
this.unregisterChangeDetector(
(<ElementRef_>ref.location).internalElement.parentView.changeDetector.ref);
ListWrapper.remove(this._rootComponents, ref);
(<ElementRef_>componentRef.location).internalElement.parentView.changeDetector.ref);
ListWrapper.remove(this._rootComponents, componentRef);
}
get injector(): Injector { return this._injector; }

View File

@ -21,5 +21,7 @@ export {
KeyValueDiffers,
KeyValueDiffer,
KeyValueDifferFactory,
CollectionChangeRecord,
KeyValueChangeRecord,
TrackByFn
} from './change_detection/change_detection';

View File

@ -16,7 +16,6 @@ import {BindingTarget} from './binding_record';
import {Locals} from './parser/locals';
import {ChangeDetectionStrategy, ChangeDetectorState} from './constants';
import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile';
import {isObservable} from './observable_facade';
import {ObservableWrapper} from 'angular2/src/facade/async';
var _scope_check: WtfScopeFn = wtfCreateScope(`ChangeDetector#check(ascii id, bool throwOnChange)`);
@ -42,10 +41,6 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
propertyBindingIndex: number;
outputSubscriptions: any[];
// This is an experimental feature. Works only in Dart.
subscriptions: any[];
streams: any[];
dispatcher: ChangeDispatcher;
@ -73,7 +68,7 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
handleEvent(eventName: string, elIndex: number, event: any): boolean {
if (!this.hydrated()) {
this.throwDehydratedError();
this.throwDehydratedError(`${this.id} -> ${eventName}`);
}
try {
var locals = new Map<string, any>();
@ -130,7 +125,7 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
// facilitate error reporting.
detectChangesInRecords(throwOnChange: boolean): void {
if (!this.hydrated()) {
this.throwDehydratedError();
this.throwDehydratedError(this.id);
}
try {
this.detectChangesInRecordsInternal(throwOnChange);
@ -158,10 +153,6 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
this.mode = ChangeDetectionUtil.changeDetectionMode(this.strategy);
this.context = context;
if (this.strategy === ChangeDetectionStrategy.OnPushObserve) {
this.observeComponent(context);
}
this.locals = locals;
this.pipes = pipes;
this.hydrateDirectives(dispatcher);
@ -176,11 +167,6 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
dehydrate(): void {
this.dehydrateDirectives(true);
// This is an experimental feature. Works only in Dart.
if (this.strategy === ChangeDetectionStrategy.OnPushObserve) {
this._unsubsribeFromObservables();
}
this._unsubscribeFromOutputs();
this.dispatcher = null;
@ -248,19 +234,6 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
}
}
// This is an experimental feature. Works only in Dart.
private _unsubsribeFromObservables(): void {
if (isPresent(this.subscriptions)) {
for (var i = 0; i < this.subscriptions.length; ++i) {
var s = this.subscriptions[i];
if (isPresent(this.subscriptions[i])) {
s.cancel();
this.subscriptions[i] = null;
}
}
}
}
private _unsubscribeFromOutputs(): void {
if (isPresent(this.outputSubscriptions)) {
for (var i = 0; i < this.outputSubscriptions.length; ++i) {
@ -270,53 +243,6 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
}
}
// This is an experimental feature. Works only in Dart.
observeValue(value: any, index: number): any {
if (isObservable(value)) {
this._createArrayToStoreObservables();
if (isBlank(this.subscriptions[index])) {
this.streams[index] = value.changes;
this.subscriptions[index] = value.changes.listen((_) => this.ref.markForCheck());
} else if (this.streams[index] !== value.changes) {
this.subscriptions[index].cancel();
this.streams[index] = value.changes;
this.subscriptions[index] = value.changes.listen((_) => this.ref.markForCheck());
}
}
return value;
}
// This is an experimental feature. Works only in Dart.
observeDirective(value: any, index: number): any {
if (isObservable(value)) {
this._createArrayToStoreObservables();
var arrayIndex = this.numberOfPropertyProtoRecords + index + 2; // +1 is component
this.streams[arrayIndex] = value.changes;
this.subscriptions[arrayIndex] = value.changes.listen((_) => this.ref.markForCheck());
}
return value;
}
// This is an experimental feature. Works only in Dart.
observeComponent(value: any): any {
if (isObservable(value)) {
this._createArrayToStoreObservables();
var index = this.numberOfPropertyProtoRecords + 1;
this.streams[index] = value.changes;
this.subscriptions[index] = value.changes.listen((_) => this.ref.markForCheck());
}
return value;
}
private _createArrayToStoreObservables(): void {
if (isBlank(this.subscriptions)) {
this.subscriptions = ListWrapper.createFixedSize(this.numberOfPropertyProtoRecords +
this.directiveIndices.length + 2);
this.streams = ListWrapper.createFixedSize(this.numberOfPropertyProtoRecords +
this.directiveIndices.length + 2);
}
}
getDirectiveFor(directives: any, index: number): any {
return directives.getDirectiveFor(this.directiveIndices[index]);
}
@ -362,7 +288,7 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
oldValue, newValue, null);
}
throwDehydratedError(): void { throw new DehydratedException(); }
throwDehydratedError(detail: string): void { throw new DehydratedException(detail); }
private _currentBinding(): BindingTarget {
return this.bindingTargets[this.propertyBindingIndex];

View File

@ -1,9 +1,20 @@
import {IterableDiffers, IterableDifferFactory, TrackByFn} from './differs/iterable_differs';
import {IterableDiffers, IterableDifferFactory} from './differs/iterable_differs';
import {DefaultIterableDifferFactory} from './differs/default_iterable_differ';
import {KeyValueDiffers, KeyValueDifferFactory} from './differs/keyvalue_differs';
import {DefaultKeyValueDifferFactory} from './differs/default_keyvalue_differ';
import {CONST, CONST_EXPR, isPresent} from 'angular2/src/facade/lang';
import {
DefaultKeyValueDifferFactory,
KeyValueChangeRecord
} from './differs/default_keyvalue_differ';
import {CONST_EXPR} from 'angular2/src/facade/lang';
export {
DefaultKeyValueDifferFactory,
KeyValueChangeRecord
} from './differs/default_keyvalue_differ';
export {
DefaultIterableDifferFactory,
CollectionChangeRecord
} from './differs/default_iterable_differ';
export {
ASTWithSource,
AST,

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