Compare commits

..

1570 Commits

Author SHA1 Message Date
ebcd14f8e9 docs(changelog): update change log to rc.5
Closes #10599
2016-08-09 10:34:07 -07:00
b65f66feff feat(i18n): switch to sha1 for message fingerprinting 2016-08-09 09:52:11 -07:00
dd68ae3ef1 feat(ExtractorMerger): ignore implicit tags in translatable sections 2016-08-09 09:52:11 -07:00
1b04d70626 feat(ExtractorMerger): allow nested implicit tags 2016-08-09 09:52:11 -07:00
01bca41168 fix: allow users to pass in absolute paths to ngc (#10572) 2016-08-09 09:36:37 -07:00
94e1ab33ce chore(release): update package.jsons 2016-08-09 09:33:17 -07:00
0b08dd8674 chore(animations): make sure host-prop animation deprecation is correctly emitted
Closes #10581
2016-08-08 23:39:22 -07:00
b2b47177cd add brandon suggestions 2016-08-09 03:30:25 +02:00
f08257ff4a docs(cheatsheet): update for rc5 2016-08-09 03:06:01 +02:00
d1f4222c83 docs(cheatsheet): add NgModules docs to the cheatsheet 2016-08-08 17:18:50 -07:00
d21331e902 fix(ngUpgrade): to work with @NgModule
We changed the bootstrap order:
1. create NgZone
2. bootstrap ng1 inside NgZone and upgrade ng1 components to ng2 components.
3. bootstrap ng2 with NgZone

Note: Previous footgun behavior was: bootstrap ng2 first to extract NgZone, so that ng1 bootstrap can happen in NgZone. This meant that if ng2 bootstrap eagerly compiled a component which contained ng1 components, then we did not have complete metadata.
2016-08-08 12:50:08 -07:00
37f138e83d test(ngUpgrade): convert the upgrade example to use NgModules
The example worked even before this change - verifying that the fallback/legacy mode works as well.
2016-08-08 12:20:22 -07:00
5dab0bad3c test(NgUpgrade): add support for loading plaground/upgrade example without bundles 2016-08-08 12:20:22 -07:00
85e70a4cde style(ngUpgrade): small cleanup 2016-08-08 12:20:21 -07:00
6b564ecda5 feat(ngUpgrade): add support for NgModules
BREAKING CHANGE: UpgradeAdapter.addProvider are now deprecated in favor of passing in an NgModule into the adapter's constructor

Before:

```
let upgradeAdapter = new UpgradeAdapter();
upgradeAdapter.addProviders([myProvidersArray);
```

After:

```
@NgModule({
  providers: myProvidersArray
})
class MyModule {}

let upgradeAdapter = new UpgradeAdapter(MyModule);
```
2016-08-08 12:17:35 -07:00
d4cceff0ef chore: remove module aware bootstrap API (#10543)
This API was introduced post RC4, but needs to be removed
before RC5 as we have decided against it.
2016-08-08 09:36:09 -07:00
a415613457 fix(router): update dts files 2016-08-08 09:15:59 -07:00
1a41bd1ca4 feature(router): add route.root returning the root of router state 2016-08-08 09:15:59 -07:00
5a99393355 fix(router): route.parent should work for secondary children 2016-08-08 09:15:59 -07:00
afcb3c0035 fix(router): support outlets in non-absolute positions 2016-08-08 09:15:59 -07:00
d2d36c61f3 fix(router): fix matrix params check to handle 'special' objects 2016-08-08 09:15:59 -07:00
0bd97ecda2 feat(http): add options method to Http (#10540)
Add options method to the Http object, which could be useful when using self-describing RESTful APIs.

This closes #10500, closes #7918
2016-08-08 09:15:13 -07:00
46bbcefb36 fix(linker): prevent pollution of empty embeddedView context (#10548)
Fixes #10045
2016-08-08 09:11:35 -07:00
74b57dfa7d refactor(i18n): remove circular dep 2016-08-05 13:39:54 -07:00
8c9c0986e9 refatcor(digestMessage): takes a i18n.Message 2016-08-05 13:39:54 -07:00
4028fcaa51 refactor(i18n): Drop html.Message and create i18n.Message right away 2016-08-05 13:39:54 -07:00
7a8ef1eae5 feat(i18n): merge translations 2016-08-05 13:39:54 -07:00
e811a5d97f refactor(i18n): misc updates 2016-08-05 13:39:54 -07:00
df44e3e425 fix(i18n extractor): array manipulation 2016-08-05 13:39:54 -07:00
cdb1a237e5 refactor(compiler): html_parser -> ml_parser 2016-08-05 13:39:54 -07:00
fcafdff10b feat(forms): allow both patching and strict setting of values (#10537) 2016-08-05 13:35:17 -07:00
c586656d43 fix(router): fix type definition 2016-08-05 13:12:21 -07:00
3a307c2794 fix(router): absolute redirects should work with lazy loading 2016-08-05 13:12:21 -07:00
4f17dbc721 fix(router): add segmentPath to the link DSL 2016-08-05 13:12:21 -07:00
99989f5d3f chore(facade): remove most facade/async functions 2016-08-05 12:26:28 -07:00
6baf3baedd chore(playground): clang-format 2016-08-05 12:26:28 -07:00
0d1f3c3b07 fix: support trailing slash in basePath (#10533) 2016-08-05 11:11:24 -07:00
83e2d3d1cb refactor(RegExpWrapper): remove the facade (#10512) 2016-08-05 09:50:49 -07:00
b4613ab2d2 fix(ExpressionParser): undefined is undefined (was null) 2016-08-05 09:45:13 -07:00
0ca05eee45 feat(ExpressionParser): add support for this 2016-08-05 09:45:13 -07:00
26c9e1dc70 refactor(NgFor spec): code cleanup 2016-08-05 09:45:13 -07:00
797cb5ae7b refactor(NgStyleSpec): simplify NgStyle tests (#10519) 2016-08-05 09:28:08 -07:00
63b82cd730 feat(router): Allow navigation without updating the URL (#9608) 2016-08-04 11:46:09 -07:00
2b704f0586 fix(linker/compiler): rename const to avoid duplicate declaration (#10457)
Currently in the `linker/compiler.ts` file, the **same identifier** is used in **two declarations**:
```typescript
export type CompilerOptions = { … }
…
export const CompilerOptions = new OpaqueToken('compilerOptions');
```
This breaks the API doc generation. I’m surprised that this was not flagged by the tsc.

The duplicate declaration was introduced in 46b212706b.
2016-08-04 11:31:58 -07:00
ce5ba80792 refactor(NgTemplateOutlet): simplify implementation (#10492) 2016-08-04 11:28:36 -07:00
8b18ef4ba2 feat(NgStyle): add support for the style.unit notation (#10496)
Closes #10326
2016-08-04 11:00:43 -07:00
cd18de7a21 refactor(compiler): use Object.keys instead of Object.getOwnPropertyNames (#10498) 2016-08-04 10:35:41 -07:00
fd19671c07 chore(benchpress): clang-format and lint 2016-08-04 10:08:37 -07:00
3fcd6fd93f chore(benchpress): make benchpress compile
Also adds compiling benchpress to the build scripts to verify that.
2016-08-04 10:08:37 -07:00
c8d53d71a3 fix(ngc): gather metadata for OpaqueToken
Fixes #10482
2016-08-04 10:05:25 -07:00
790362e243 fix: put all ngc files into a single directory (#10486)
Prior to this change `ngc` would place generated files which refer
to components in the node_modules into the node_module. This is an
issue. Now all of the files are forced into a single directory
as specified in `tsconfig.json` by the `genDir` option.

see: https://docs.google.com/document/d/1OgP1RIpZ-lWUc4113J3w13HTDcW-1-0o7TuGz0tGx0g
2016-08-03 21:34:03 -07:00
2eda7a5293 fix(router): provideRouter should use provideRoutes (#10488) 2016-08-03 19:35:12 -07:00
9925aa89dc fix(compiler): Report references to non-exported symbols.
Includes fixes to places now reported as errors.

Part of #8310
2016-08-03 15:42:38 -07:00
6195a45ae2 fix(forms): export AbstractFormGroupDirective
Because
- `Form` is **exported** -- see line 30/31 of
`modules/@angular/forms/src/forms.ts`: i.e., <br>`export {Form} from
'./directives/form_interface'`; and
- Methods of `Form`, which are public, have an
`AbstractFormGroupDirective` parameter;
e.g.,<br>`Form.getFormGroup(dir: AbstractFormGroupDirective):
FormGroup`.

Then it makes sense for `AbstractFormGroupDirective` to be
public/exported too. In any case, if it isn't exported then the **API
docs for `Form` don't get generated properly.**
2016-08-03 15:33:29 -07:00
422d380b3e feat(router): add queryParams and fragment to every activated route 2016-08-03 15:30:03 -07:00
550ab31bd0 feat(router): add parent, children, firstChild to ActivatedRoute 2016-08-03 15:30:03 -07:00
5fceb21549 refactor(router): remove dead code 2016-08-03 15:30:03 -07:00
29caa37943 feat(router): support sibling modules providing routes 2016-08-02 13:31:15 -07:00
8efbcc996a fix(compiler): allow to use pipes inside of *ngIf (#10452)
Fixes #9746
2016-08-02 12:19:34 -07:00
91c64d2b8d fix: missing export for validators 2016-08-02 12:12:15 -07:00
82e7ecd611 fix(compiler): StaticReflect now resolves re-exported symbols (#10453)
Fixes: #10451
2016-08-02 11:45:14 -07:00
3d53b33391 chore: update public api and integrate minor review comments 2016-08-02 11:23:26 -07:00
34624b2db2 refactor(core): deprecate old methods on ApplicationRef
BREAKING CHANGE:
- `ApplicationRef.run` is deprecated. Use `NgZone.run` directly
- `ApplicationRef.injector` is deprecated. Inject an `Injector` or
  use `NgModuleRef.injector` instead
- `ApplicationRef.zone` is deprecated. Inject `NgZone` instead.
2016-08-02 11:23:26 -07:00
1ab5eb0844 refactor(core): don’t use zone.run in ApplicationRef.bootstrap.
`ApplicationRef.bootstrap` is supposed to be run inside of `ngDoBootstrap` method
of the module that is bootstrapped, and that method already runs inside of the
zone.
2016-08-02 11:23:26 -07:00
630028350a refactor(core): Introduce AppInitStatus
This class allows any provider to know and wait for the initialization of the
application. This functionality previously was tied to `ApplicationRef`.

BREAKING CHANGE:
- `ApplicationRef.waitForAsyncInitializers` is deprecated. Use 
  `AppInitStatus.donePromise` / `AppInitStatus.done` instead.
2016-08-02 11:23:26 -07:00
7e4fd7d7da refactor(core): introduce @NgModule.bootstrap and ngDoBootstrap method
If a `@NgModule` has a `bootstrap` property, `PlatformRef.bootstrapModule` /
`PlatformRef.bootstrapModuleFactory` will automatically bootstrap the components
listed in there.
If such a property does not exist, `PlatformRef.bootstrapModule` /
`PlatformRef.bootstrapModuleFactory` will try to call the method `ngDoBootstrap(appRef: ApplicationRef)` on the module class.
Otherwise an error is reported.
2016-08-02 11:23:26 -07:00
af2e80e068 refactor(core): introduce APP_BOOTSTRAP_LISTENER multi provider
Using the `registerBootstrapListener` easily lead to race condition
and needed dependencies on `ApplicationRef`.

BREAKING CHANGE:
- `ApplicationRef.registerBootstrapListener` is deprecated. Provide a multi
  provider for the new token `APP_BOOTSTRAP_LISTENER` instead.
2016-08-02 11:23:26 -07:00
8e6091de6c refactor(core): use ngOnDestroy in providers
Note about the addition of `beforeEach(fakeAsync(inject(…))))` in some tests:
`ApplicationRef` is now using `ngOnDestroy` and there is eager,
including all of its dependencies which contain `NgZone`.
The additional `fakeAsync` in `beforeEach` ensures that `NgZone`
uses the fake async zone as parent, and not the root zone.

BREAKING CHANGE (via deprecations):
- `ApplicationRef.dispose` is deprecated. Destroy the module that was
   created during bootstrap instead by calling `NgModuleRef.destroy`.
- `AplicationRef.registerDisposeListener` is deprecated.
   Use the `ngOnDestroy` lifecycle hook for providers or
   `NgModuleRef.onDestroy` instead.
- `disposePlatform` is deprecated. Use `destroyPlatform` instead.
- `PlatformRef.dipose()` is deprecated. Use `PlatformRef.destroy()`
   instead.
- `PlatformRef.registerDisposeListener` is deprecated. Use
  `PlatformRef.onDestroy` instead.
- `PlaformRef.diposed` is deprecated. Use `PlatformRef.destroyed`
  instead.
2016-08-02 11:23:26 -07:00
ecdaded25f feat(core): introduce NgModuleRef.destroy and call ngOnDestroy on all providers 2016-08-02 11:23:26 -07:00
c161ed415d feat(core): support ngOnDestroy on providers of a directive.
Such providers also become eager as they will be instantiated
anyways on destruction.
2016-08-02 11:23:26 -07:00
ff3b71f7b3 refactor(core): collect lifecycle hooks for all injectable classes 2016-08-02 11:23:26 -07:00
16cc9b46aa fix(fake_async): share zone between beforeEach and it
This is needed for the case if a `beforeEach` instantiates
`NgZone`and the `it` uses
`TestComponentBuilder.createFakeAsync`.

Otherwise the `NgZone` will use the root zone as parent,
and `TestComponentBuilder.createFakeAsync` will always return
undefined as `tick` does not forward promises created
under the zone of `NgZone`.
2016-08-02 11:23:26 -07:00
3ce11ed58c docs(pipes): update I18nSelectPipe API documentation (#10449) 2016-08-02 10:59:07 -07:00
7db75fa361 test: reactivate some of the disabled tests in Edge (#10450) 2016-08-02 10:58:03 -07:00
d6d4568830 fix(forms): allow arrays as parents (#10440)
Closes #10432
2016-08-02 09:40:42 -07:00
a55d796c4b fix: Better error message in case of unknown property binding 2016-08-02 09:28:04 -07:00
73f02c7861 fix: throw useful error on missing platform module. 2016-08-02 09:28:04 -07:00
8c8754e573 chore: add browserNoRouter test running mode 2016-08-02 09:28:04 -07:00
c977a906b3 docs(cheatsheet): add moduleId (#10095) 2016-08-02 07:34:04 -07:00
e0eea6c2f4 feat(forms): add invalid prop to abstract controls (#10439) 2016-08-01 18:41:25 -07:00
3e377f520e fix(router): make an outlet to unregister itself when it is removed from the dom 2016-08-01 16:56:38 -07:00
8dc82a0080 fix(router): fix offline compilation by exporting provideLocationStrategy 2016-08-01 16:23:28 -07:00
4624a35845 docs(router): fixes api docs 2016-08-01 16:23:28 -07:00
8d4499959a feat(forms): add get method for easy access to child controls (#10428) 2016-08-01 14:22:50 -07:00
2dfc9c653b refactor: dart 2016-08-01 11:34:51 -07:00
106db0aba8 refactor: IS_DART === false 2016-08-01 11:34:51 -07:00
28c4852cd6 refactor: remove ts2dart annotations 2016-08-01 11:34:51 -07:00
13c8211065 fix: String.split(str, n) stops after n separator (#10408) 2016-08-01 11:33:35 -07:00
e73d0511cf fix(TemplateParser): report empty expression (#10391)
fixes #3754
2016-08-01 11:24:49 -07:00
e18626b7a2 fix(core): ensure ngFor only inserts/moves/removes elements when necessary (#10287)
Closes #9960
Closes #7239
Closes #9672
Closes #9454
Closes #10287
2016-08-01 11:09:52 -07:00
4df7b1cfbc chore: fixes some issues that happen with closure compiler. (#10392) 2016-08-01 09:47:49 -07:00
f9573ece41 ci(browser providers): update browsers in SL and BS (#10425) 2016-08-01 08:00:12 -07:00
93ade740e2 fix(router): configure DI correctly when using the old provideRouter function 2016-08-01 02:35:41 -07:00
a46437c57d refactor(core): fix bootstrapModule regarding zones and initializers (#10383)
This makes `bootstrapModuleFactory` wait for promises
returned by `APP_INITIALIZER`s, also making `bootstrapModuleFactory` async.
I.e. now `bootstrapModule` and `bootstrapModuleFactory` behave in the
same way.

This ensures that all code from module instantiation, to creating
`ApplicationRef`s as well as calling `APP_INITIALIZERS` is run
in the Angular zone.

This also moves the invocation of the initializers from the `ApplicationRef`
constructor into the `bootstrapModuleFactory` call, allowing initializers
to get a hold of `ApplicationRef` (see #9101).

Fixes #9101
Fixes #10363
Fixes #10205
2016-08-01 02:12:59 -07:00
633c7d1ebe refactor(core): cleanup unused NgZone factory
This factory was only used when using `coreBootstrap` / `coreLoadAndBoostrap`
which is not supported any more.
2016-08-01 02:12:59 -07:00
251953218c fix(http): URLSearchParams.clone propagate the QueryEncoder (#9900) 2016-07-30 19:01:20 -07:00
0d6cc17252 fix(UrlParser): stop setting default value 'true' (#10399) 2016-07-30 14:34:03 -07:00
c8989c900f chore(github): remove reference to Dart in the issue template (#10388) 2016-07-30 00:22:04 -07:00
6134320f16 refactor(http): rewrite for readable & efficient. 2016-07-29 21:30:28 -07:00
7f647822bd fix(http): headers should be case-insensitive.
fixes #9452

spec at https://tools.ietf.org/html/rfc2616
2016-07-29 21:30:15 -07:00
e34a04d2ad feat(xmb/xtb): support dtd 2016-07-29 14:18:34 -07:00
44093905e2 refactor(test): refine types 2016-07-29 13:53:13 -07:00
3e2900f74b refactor(i18n): move code around 2016-07-29 13:40:58 -07:00
11fd2eccec refactor: fix merge glitches 2016-07-29 13:40:58 -07:00
0eee1d5de3 feat(i18n): xtb serializer 2016-07-29 13:40:58 -07:00
1b77604ee2 refactor(compiler): re-arrange files 2016-07-29 13:40:58 -07:00
cc5cfe87c3 feat(i18n): xmb serializer 2016-07-29 13:40:58 -07:00
48f230a951 feat(I18nAst): introduce an intermediate AST 2016-07-29 13:40:58 -07:00
2be50bdbb0 refactor(i18n): message extractor
fixes #8802
2016-07-29 13:40:58 -07:00
f7258ea52a test(HtmlAst): add a serializer 2016-07-29 13:40:58 -07:00
28e8b2faab feat(ICU): extract ICU messages 2016-07-29 13:40:58 -07:00
3c3e9ddb10 feat(router): add a validation to make sure pathMatch is set correctly 2016-07-29 12:27:43 -07:00
5162fb6d52 feat(router): add isActive to router 2016-07-29 12:27:43 -07:00
2fdb39e60a feat(router): activateroute should expose its route config 2016-07-29 12:27:43 -07:00
43c71ae103 fix(platform-browser): IEMobile is badly detected when testing (#10382) 2016-07-29 10:05:12 -07:00
f0bd528d77 test(http): fix Blob creation for Android <= 4.3
Closes #10377
2016-07-29 18:20:09 +02:00
50a024b42f test(http): add Typed Array polyfill for IE9 2016-07-29 18:19:54 +02:00
b48f7bcb8d fix(forms): normalize written value in NumberValueAccessor
Closes #10379
2016-07-29 18:18:30 +02:00
763ca60f5b feat(compiler): Support default parameters in static reflector (#10370)
Closes: #10369
2016-07-29 09:10:45 -07:00
0eca7abdd8 chore: update public api
Closes #10355
2016-07-29 04:47:50 -07:00
d0a95e35af refactor(testing): introduce new testing api to support ng modules
BREAKING CHANGE:
- deprecations:
  * `withProviders`, use `TestBed.withModule` instead
  * `addProviders`, use `TestBed.configureTestingModule` instead
  * `TestComponentBuilder`, use `TestBed.configureTestModule` / `TestBed.override...` / `TestBed.createComponent` instead.

Closes #10354
2016-07-29 04:47:18 -07:00
acc6c8d0b7 feat(compiler): introduce MockDirectiveResolver.setDirective 2016-07-29 04:47:18 -07:00
3dbc66c1ac refactor(core): introduce interfaces for constructor arguments of decorators
For @Directive, @Component, @Pipe, @NgModule
2016-07-29 04:47:18 -07:00
4ad6bcce54 feat(compiler): add MockPipeResolver 2016-07-29 04:47:17 -07:00
0988cc82b0 refactor(core): remove ViewResolver and ViewResolverMock
The methods on `ViewResolverMock` have been merged into `DirectiveResolver`.

BREAKING CHANGE:
- ES5 users can no longer use the `View(…)` function to provide `ViewMetadata`.
  This mirrors the removal of the `@View` decorator a while ago.
2016-07-29 04:47:17 -07:00
20b03bad11 feat(compiler): Added support for conditional expressions. (#10366)
Expression evaluated by the static reflector can now supports
conditional expressions.

Closes: #10365
2016-07-28 17:32:29 -07:00
81d27daf0d fix(router): update dts files 2016-07-28 15:03:19 -07:00
bb8b82b3f5 fix(router): relax type defintion of Route to improve dev ergonomics 2016-07-28 15:03:19 -07:00
915a6666f8 fix(core): fix offline detection in ng_module_factory_loader 2016-07-28 15:03:19 -07:00
72da547d6a fix(router): updates router module to be offline-compilation friendly 2016-07-28 15:03:19 -07:00
7c76a75452 fix(forms): update dirty before emitting value change (#10362)
Closes #5328
2016-07-28 14:25:33 -07:00
a32c4ad2f0 fix(compiler): auto declare entryComponents recursively
Closes #10348
2016-07-28 12:03:34 -07:00
fb3608aa5d fix(compiler): report better error messages for host bindings
Closes #10346
2016-07-28 11:21:50 -07:00
0a46f37444 fix(metadata): fix typechecking with typescript@next 2016-07-28 11:17:53 -07:00
9b39e499ac fix(core): support components without a selector (#10331)
Components without a selector now get the selector `ng-component`.
Directives without a selector will throw an error message.

Closes #3464
Closes #10216
2016-07-28 10:39:10 -07:00
a67cc8229d chore: move injector to being non-internal but private (#10339)
* chore: move injector to being non-internal but private

* Add the new non-internal method to the public API.
2016-07-28 10:38:29 -07:00
b58e9ea775 feat(compiler): Added support for references to static fields. (#10334)
Closes: #10332
2016-07-27 19:26:59 -07:00
422effdd18 refactor(http): use ngModules for http (#10329) 2016-07-27 13:09:05 -07:00
3b690b68a6 fix(testing): Fix error message in test bed
The API name in the error message was wrong
2016-07-27 12:03:27 -07:00
43349dd373 fix(forms): improve ngModel error messages (#10314) 2016-07-27 10:59:40 -07:00
e44e8668ea fix(core): only warn and auto declare undeclared entryComponents.
This is needed to support existing applications.
After final these warnings will become errors.

Closes #10316
2016-07-27 10:56:12 -07:00
69e72c0786 chore: remove browserDynamicTestingPlatform as it is deprecated and was not part of rc.4
Closes #10319
2016-07-27 10:52:47 -07:00
553344739c fix(core): allow module providers to overwrite providers from ModuleWithProviders
Fixes #10313
Closes #10317
2016-07-27 10:51:58 -07:00
367f0fd142 fix(static_reflector): report methods with decorators in propMetadata as well
This was the behavior of our regular reflector as well, although the
method name does not imply this.

Fixes #10308
Closes #10318
2016-07-27 10:50:58 -07:00
58d9e7fc5a feat(facade): add support for all thenables (#10278)
All objects that have a then function will be considered Promises
2016-07-27 10:37:48 -07:00
9d9e9c6ff1 refactor(router): take advantage of the new way of configuring modules 2016-07-27 10:24:29 -07:00
ba88db5141 fix(ngClass): do not deconstruct classes on element removal (#10303)
Prior to this fix [ngClass] would remove all dynamic classes
when destroyed. It's essential that classes are persisted such
that remove-based animations will still be stylistically correct.
This patch fixes this issue.

Closes #10008
Closes #10303
2016-07-26 15:20:27 -07:00
62e7c0f464 feat(router): implement canLoad 2016-07-26 14:39:02 -07:00
fc83bbbe98 fix(compiler): treat custom elements as unknown elements by default
Closes #10300
2016-07-26 12:31:22 -07:00
482c019199 feat(security): only warn when actually sanitizing HTML. (#10272)
Previously, Angular would warn users when simply re-encoding text
outside of the ASCII range. While harmless, the log spam was annoying.

With this change, Angular specifically tracks whether anything was
stripped during sanitization, and only reports a warning if so.

Fixes #10206.
2016-07-26 11:39:09 -07:00
b449467940 feat(compiler): Allow calls to simple static methods (#10289)
Closes: #10266
2016-07-26 10:18:35 -07:00
0aba42ae5b fix(forms): throw error if wrong control container for reactive forms (#10286) 2016-07-26 10:08:46 -07:00
0d1bf8148b fix(animations): ensure animation detection doesn't rely on the body node
Closes #10230
Closes #10191
Closes #10273
2016-07-26 09:33:50 -07:00
b42411ba1f chore: update public api
- `precompile` -> `entryComponents`
- introduce `ModuleWithProviders`
- introduce `@NgModule.schema`
- update to bootstrap

Closes #10268
2016-07-26 07:45:40 -07:00
5a21f168d6 refactor(core): change bootstrap of modules and names of platforms
BREAKING CHANGES:
- `browserPlatform`/`browserDynamicPlatform`/... have been deprecated and renamed into `platformBrowser`/`platformBrowserDynamic`/....
- `bootstrapModule` and `bootstrapModuleFactory` have been moved to be members of `PlaformRef`.
  E.g. `platformBrowserDynamic().bootstrapModule(MyModule)`.
2016-07-26 07:28:55 -07:00
00b726f695 refactor(core): introduce NgModule.schemas
This allows Angular to error on unknown properties,
allowing applications that don’t use custom elements
to get better error reporting.

Part of #10043

BREAKING CHANGE:
- By default, Angular will error during parsing
  on unknown properties,
  even if they are on elements with a `-` in their name
  (aka custom elements). If you application is using
  custom elements, fill the new parameter `@NgModule.schemas`
  with the value `[CUSTOM_ELEMENTS_SCHEMA]`.

  E.g. for bootstrap:
  ```
  bootstrap(MyComponent, {schemas: [CUSTOM_ELEMENTS_SCHEMA]});
  ```
2016-07-26 07:04:36 -07:00
f02da4e91a feat(core): introduce ModuleWithProviders.
Modules can now provider helper functions that allow
to import a module together with an array of providers.

Part of #10043
2016-07-26 07:04:36 -07:00
d6b65db9a7 fix(static_reflector): resolve values of functions in the function context 2016-07-26 07:04:36 -07:00
6f4e49ed53 refactor(core): rename precompile into entryComponents.
Part of #10043

BREAKING CHANGE:
- `@Component.precompile` was renamed to `@Component.entryComponents`
  (old property still works but is deprecated)
- `ANALYZE_FOR_PRECOMPILE` was renamed to `ANALYZE_FOR_ENTRY_COMPONENTS` (no deprecations)
2016-07-26 07:04:36 -07:00
46b212706b refactor(core): change module semantics
This contains major changes to the compiler, bootstrap of the platforms
and test environment initialization.

Main part of #10043
Closes #10164

BREAKING CHANGE:
- Semantics and name of `@AppModule` (now `@NgModule`) changed quite a bit.
  This is actually not breaking as `@AppModules` were not part of rc.4.
  We will have detailed docs on `@NgModule` separately.
- `coreLoadAndBootstrap` and `coreBootstrap` can't be used any more (without migration support).
  Use `bootstrapModule` / `bootstrapModuleFactory` instead.
- All Components listed in routes have to be part of the `declarations` of an NgModule.
  Either directly on the bootstrap module / lazy loaded module, or in an NgModule imported by them.
2016-07-26 07:04:10 -07:00
ca16fc29a6 build: fix and document the symlinks scripts for Windows
Closes #10213
2016-07-26 09:52:11 +02:00
9edea0b139 fix(ng upgrade): do not compile ng2 components until after ng1 bootstrap (#10084)
Closes #9407 and angular/protractor#2944
2016-07-25 21:14:35 -07:00
d15a1d64e1 fix(router): make router provides work with cli and offline compilation 2016-07-25 18:04:22 -07:00
c87847974a chore(forms): separate out tests for reactive forms (#10283) 2016-07-25 15:57:51 -07:00
6f68330fa5 feat(router): rename UrlPathWithParams into UrlSegment
BREAKING CHANGE:

UrlPathWithParams => UrlSegment
UrlSegment => UrlSegmentGroup
2016-07-25 12:15:07 -07:00
2b63330a36 fix(router): handle when both primary and secondary are empty-path and primary has a child 2016-07-22 18:51:35 -07:00
06e4ca4bb3 fix(router): advance query params and fragment after advanced routes 2016-07-22 18:51:35 -07:00
43437c175a fix(router): handle url fragments when no url segments present 2016-07-22 18:51:35 -07:00
8d90a5a4cf fix(router): router link active should take all descendants into account 2016-07-22 18:51:35 -07:00
93a4ca652a refactor(router): renames PRIMARY_OUTLET into primary 2016-07-22 18:51:35 -07:00
41178367d1 feat(HtmlLexer): better hint on unclosed ICU message errors
fixes #10227
2016-07-22 17:27:15 -07:00
54f2edbb90 refactor(Lexer): code cleanup 2016-07-22 16:50:54 -07:00
b652a7fc9f chore: remove obsolete files (#10240) 2016-07-22 16:18:31 -07:00
e34eb4520f fix(testing): ComponentFixture - Avoid extra scheduleMicrotask (#10223)
Don't schedule microtask to check for pending macrotasks when no one is waiting for a whenStable().
2016-07-22 16:07:11 -07:00
190bcc89c1 refactor(EventEmitter): optional emits (#10058)
the Subject#next is optional
2016-07-22 10:19:57 -07:00
64fc4648b7 fix(testing): correctly import NgMatchers (#10077)
Some test cases incorrectly rely on the side effect of other test cases
importing `NgMatchers`. This commit fixes this by making `expect` in
`core/testing_internal` properly typed.
2016-07-22 09:20:51 -07:00
bdb59129d0 feat(http): add content-type override support for http request (#10211) 2016-07-22 08:37:32 -07:00
d455942389 fix(CurrencyPipe): use default Intl formatting options when none provided
fixes #10189
2016-07-22 08:21:31 -07:00
cdb3678fe3 chore(tools): remove unused files 2016-07-22 08:20:46 -07:00
e73ac1e992 chore(Dart): remove obsolete files, update the docs 2016-07-22 08:20:46 -07:00
51f3d22e4f feat(security): trust resource URLs as URLs. (#10220)
Resource URLs are strictly "more" trustworthy than plain URLs, so trusting them maintains the same level of security while avoiding to break people when we downgrade a resource URL context to a plain URL context.
2016-07-21 17:44:59 -07:00
00aa7a76b6 chore(tests): enable lint, make it green. (#10224) 2016-07-21 17:12:00 -07:00
27b87ef535 fix(router): throw when cannot parse a url 2016-07-21 16:14:55 -07:00
44709e0dca fix(router): handle urls with only secondary top-level segments 2016-07-21 16:14:44 -07:00
31a7709ece test: execute router tests on ./test.sh browser (#10053) 2016-07-21 16:12:40 -07:00
a441b5b8fe feat(security): categorize <track src> as a regular URL.
After security review, it turns out we were too paranoid about <track src>. Its content is not actually active or dangerous.

Fixes #10089.
2016-07-21 13:53:35 -07:00
76b8a49bfb feat(http): add support for blob as a response type (#10190) 2016-07-21 13:44:38 -07:00
db54a84d14 fix(router): routerLinkActive should only set classes after the router has successfully navigated 2016-07-20 17:51:21 -07:00
eb6ff65af7 refactor(router): makes an error message clearer 2016-07-20 14:47:51 -07:00
23ee29b6a2 fix(router): navigation should not preserve query params and fragment by default
BREAKING CHANGE

Previously both imperative (router.navigate) and declarative (routerLink) navigations
would preserve the current query params and fragment. This behavior turned out to
be confusing. This commit changes it.

Now, neither is preserved by default. To preserve them, you need to do the following:

router.naviage("newUrl", {preserveQueryParams: true, preserveFragment: true})

<a routerLink="newUrl" preserveQueryParams preserveFragment></a>
2016-07-20 14:30:04 -07:00
73a69895d8 cleanup(router): removes unnecessary files from tsconfig 2016-07-20 11:44:07 -07:00
2799e7a3ca refactor(router): rename RouterTestModule into RouterTestingModule 2016-07-20 11:39:31 -07:00
b43f95435b fix(testing): add an explicit doAsyncPrecompilation step (#10015)
This removes the magic from the `inject` test helper that would inspect
the current zone and would only work with our `async` test helper.
Now, `inject` is always synchronous, and if you are using a module
that requires async precompilation, you're required to call
`doAsyncPrecompilation` in your tests.

This is part of the breaking changes introduced with the swap
to each test having an AppModule.

Closes #9975
Closes #9593

BREAKING CHANGE:

`TestInjector` is now renamed to `TestBed`

Before:

```js
import {TestInjector, getTestInjector} from '@angular/core/testing';
```

After:

```js
import {TestBed, getTestBed} from '@angular/core/testing';
```
2016-07-20 10:51:21 -07:00
450f61d384 docs(router): fix provider token (#10177) 2016-07-20 07:27:09 -07:00
f3dd91e1d7 feat(NumberPipe): add string support (#10163)
fixes #10159
2016-07-19 11:27:06 -07:00
979946c062 fix(testing): Add platform directives to the shim that keeps setBaseTestProviders running (#10154)
Due to daa9da4047,
tests using the setBaseTestProviders stopped working with ambient directives such as
`ngIf`. Add them back in to keep the shim working.
2016-07-19 08:59:14 -07:00
51e661eb74 fix(router): export navigation extras 2016-07-18 17:42:04 -07:00
921a17960c fix(router): lazy loaded components should use loaded injector 2016-07-18 17:42:04 -07:00
7a4f6621ed fix(router): handle lastPathIndex of empty-path routes 2016-07-18 17:42:04 -07:00
83bc5c97ef fix(http): convert objects passed to requests into a string (#10124)
This remove a breaking change introduced with commit #e7a8e2757b06d572f614f53b648d2fd75df370d2 where json objects passed to  requests were not converted into string.

BREAKING CHANGE:

The behavior in this commit is the same as before PR 7260 : the objects sent with the request are converted to a string, therefore there is no need for the user to take care of the serialization.

Fixes #10073
2016-07-18 14:20:03 -07:00
3f08efa35d fix(KeyValueDiffer): check for changes
fixes #9115
2016-07-18 11:30:07 -07:00
0914dc35e8 refactor(Differ): cleanup 2016-07-18 11:30:07 -07:00
b6746cce9c refactor(decorators): cleanup 2016-07-18 10:38:59 -07:00
8cd97c2054 refactor(NgFor): cleanup 2016-07-18 10:38:59 -07:00
32d8cde9c6 docs(NgSwitch): fix typo ngSwitch to NgSwitch (#10143) 2016-07-18 10:24:55 -07:00
1803ed2512 fix(router): fix rollup config to properly set up rxjs 2016-07-15 16:27:54 -07:00
f08060b0b0 fix(router): back button does not work in IE11 and Safari 2016-07-15 14:59:59 -07:00
b77a4a40a4 fix(router): expose initalNavigation and dispose so they can be used with webworkers 2016-07-14 17:29:01 -07:00
e1109d52e1 docs(router): update the docs of LocationPathStrategy 2016-07-14 15:00:50 -07:00
0668ba50e8 fix(router): freeze params and queryParams to prevent common source of bugs 2016-07-14 13:33:05 -07:00
44ff005ce3 doc(developer): updates (#10075) 2016-07-14 08:51:05 -07:00
aa88438b54 feat(Router): add extra validation for when route was passed as Array (#9942) 2016-07-14 08:28:31 -07:00
85be729c70 fix(router): lazily-loaded modules should use loaded injectors instead of the root one 2016-07-13 18:25:30 -07:00
a5dc5705a3 feat(router): guards and data resolvers can now return promises 2016-07-13 18:25:30 -07:00
9e3d13f61f feat(router): add support for canActivateChild 2016-07-13 18:25:30 -07:00
961c9d48ae fix(compiler): Query expression lambdas should have dynamic type
Fixes: #9875
2016-07-13 15:41:30 -07:00
9229bbbc80 Revert "fix(compiler): Generates function expressions as returning any (#9980)"
This reverts commit eb5763c23f.
2016-07-13 15:41:30 -07:00
34feecf60e fix(forms): improve no value accessor error message (#10051) 2016-07-13 14:13:02 -07:00
4c762a6be3 chore(lint): enable lint check for duplicate vars 2016-07-13 12:33:39 -07:00
0426325ef7 fix(router): merge SystemJsAppModuleFactoryLoader and SystemJsAllModuleLoader 2016-07-13 11:16:46 -07:00
0b54e3cf0a fix(router): do not fire events on 'duplicate' location events 2016-07-13 11:16:46 -07:00
5cf58971f1 fix(router): update current state and url before activating components 2016-07-13 11:16:46 -07:00
6518ff88b2 fix(compiler): No longer writes 0 length files outside of genDir (#10023)
Fixes: #9984
2016-07-13 11:15:23 -07:00
42b0c1d8a2 refactor: misc cleanup (#10046) 2016-07-13 11:01:32 -07:00
4a965052f9 fix(platform-browser): remove testing_e2e target (#10029)
The testing_e2e util does not belong in platform-browser and was never
intended to be a public API. Move it out of that whole tree.

BREAKING CHANGE:

The following API was never intended to be public and is removed:

```js
import {verifyNoBrowserErrors} from '@angular/platform-browser/testing_e2e';
```

Consider using Protractor's console plugin: https://github.com/angular/protractor-console-plugin
2016-07-13 10:10:02 -07:00
5725c5925c docs(ExceptionHandler): Update code example in comment block to correct syntax error. (#10032) 2016-07-13 07:28:08 -07:00
a46291b67c docs(router) Added @Injectable decorators to Router API examples (#10033) 2016-07-13 07:27:32 -07:00
4ac76ca281 docs(router): fix syntax in code example of comment block (#10026) 2016-07-13 07:26:26 -07:00
e7a8e2757b refactor(http): share 'body' logic between Request and Response 2016-07-12 18:01:06 -07:00
1266460386 feat(http): add support for ArrayBuffer
Add the buffer option to store response in ArrayBuffer
Improve the interface to get back response independently of the buffer type
2016-07-12 18:01:06 -07:00
0ccb6e0dfc refactor: code cleanup 2016-07-12 16:57:39 -07:00
3050ae155c feat(ICU): enable ICU extraction even when when in is not used
BREAKING CHANGES:

"{" is used a a delimiter for ICU messages then it could not be used in text nodes.
"{" should be escaped as "{{ '{' }}"

Before:

    <span>some { valid } text</span>

After:

    <span>some { invalid } text<span> <!-- throw parse error -->
    <span>some {{ '{' }} valid } text</span>
2016-07-12 16:57:39 -07:00
402fd934d0 refactor: code cleanup 2016-07-12 15:26:03 -07:00
6c86e8d80a test(HtmlLexer): Add test for "{" and "{{" escaping 2016-07-12 15:26:03 -07:00
60e6f91a53 refactor(HmtlLexer): cleanup 2016-07-12 15:26:03 -07:00
e676fded21 refactor(Lexer): add types
relates to #9100
2016-07-12 15:26:03 -07:00
25e070dd65 fix(HtmlParser): correctly propagate the interpolation config across layers 2016-07-12 15:26:03 -07:00
da8eb9f8b8 feat(forms): add ability to reset forms (#9974)
Closes #4914
Closes #4933
2016-07-12 15:02:25 -07:00
806a25413c feat(animations): allow animation integration support into host params
Closes #9044
Closes #9933
2016-07-12 14:55:36 -07:00
5af1e891cd fix(animations): throw errors when duplicate component trigger names are registered 2016-07-12 14:55:31 -07:00
79eda30f0f refactor(animations): collect parser / lookup errors in the same place 2016-07-12 14:55:27 -07:00
6d02d2f107 fix(SyncAsyncResult): fix default async value (#10013) 2016-07-12 13:55:06 -07:00
ded518d47f feat(router): update routerLink DSL to handle aux routes 2016-07-12 11:44:55 -07:00
27436270fd fix(Compiler): Catch exceptions in the logging of binding update
fixes #9994
2016-07-12 11:21:32 -07:00
b4ea0b1601 test(datePipe): remove hardcoded timezone (#10007)
Closes #9964
2016-07-12 11:13:26 -07:00
7b31178546 fix(platform-browser-dynamic): Add @Injectable() annotation to XHRImpl.
Otherwise Closure compiled code will complain that the class is missing the annootation.
2016-07-11 23:27:35 -07:00
2ff83324af fix(core): Don't use ES6 spread operator when undefined is allowed.
Workaround a closure bug where it doesn't produce the right code ES6 operator when the array value can be undefined.
2016-07-11 23:14:25 -07:00
4ef86891a3 fix(compiler): Ignore references to declared modules and unneeded types (#9776)
Fixes: #9670
2016-07-11 17:26:35 -07:00
eb5763c23f fix(compiler): Generates function expressions as returning any (#9980)
Function expressions are used in an expression context so untyped
function expressions should have any as the result type.

Fixes: #9877
2016-07-11 17:19:38 -07:00
d1a3e3aff1 fix(DirectiveResolver): throw on duplicate Input & Output names 2016-07-11 16:38:06 -07:00
93d0a01d3d refactor: code cleanup 2016-07-11 16:22:23 -07:00
9af2d8b810 fix(testing): remove deprecated testing APIs (#9923)
See https://github.com/angular/angular/blob/master/CHANGELOG.md for
prior deprecation and how to update.
2016-07-11 16:04:32 -07:00
94dc632a6d fix(NgStyle): remove duplicate input declaration (#9978)
fixes #9977
2016-07-11 15:09:04 -07:00
29231877e6 fix(testing): reintroduce and deprecate setBaseTestProviders (#9905)
This change reverts the removal of setBaseTestProviders that was
introduced in 8d746e3f67.

Instead, setBaseTestProviders and the providers provided from
`@angular/platform-browser-dynamic/testing` and `@angular/server/testing`
will still work for the next release, but are deprecated.

See 8d746e3f67 for how to upgrade.
2016-07-11 14:01:11 -07:00
e68252a79b fix(forms): re-enable form provider functions for easier migration (#9972) 2016-07-11 13:23:38 -07:00
4ec2a30942 fix(compiler): Fixed ?. operator to short-circut execution (#9965)
Fixes: #9850
2016-07-11 12:58:56 -07:00
e6b24437a9 fix(compiler): Collector collects enum values. (#9967)
Fixes: #9928
2016-07-11 11:50:33 -07:00
a05f7b2d76 refactor(animations): move MockAnimationPlayer back into core (#9966)
Closes #9966
2016-07-11 10:55:47 -07:00
61e18434d3 refactor(router): not use reserved words as variable (#9941) 2016-07-11 10:53:29 -07:00
57473e72ec refactor: code cleanup (#9931) 2016-07-09 10:12:39 -07:00
c3bdd504d0 fix(animations): ensure all child elements are rendered before running animations
Closes #9402
Closes #9775
Closes #9887
2016-07-08 18:18:46 -07:00
daa9da4047 refactor(core): deprecate coreBootstrap, PLATFORM_PIPES/DIRECTIVES providers and ComponentResolver
BREAKING CHANGE (deprecations)

- Instead of `coreBootstrap`, create an `@AppModule` and use `bootstrapModule`.
- Instead of `coreLoadAndBootstarp`, create an `@AppModule` and use `bootstrapModuleFactory`.
- Instead of `bootstrapWorkerApp`, create an `@AppModule` that includes the `WorkerAppModule` and use `bootstrapModule` with the `workerAppPlatform()`.
- Instead of `bootstrapWorkerUi`, create an @AppModule that includes the `WorkerUiModule` and use `bootstrapModule` with the `workerUiPlatform()` instead.
- Instead of `serverBootstrap`, create an @AppModule and use `bootstrapModule` with the `serverDynamicPlatform()` instead.
- Instead of `PLATFORM_PIPES` and `PLATFORM_DIRECTIVES`, provide platform directives/pipes via an `@AppModule`.
- Instead of `ComponentResolver`:
  - use `ComponentFactoryResolver` together with `@AppModule.precompile`/`@Component.precompile` or `ANALYZE_FOR_PRECOMPILE` provider for dynamic component creation.
  - use `AppModuleFactoryLoader` for lazy loading.
- Instead of `SystemJsComponentResolver`, create an `@AppModule` and use `SystemJsAppModuleLoader`.
- Instead of `SystemJsCmpFactoryResolver`, create an `@AppModule` and use `SystemJsAppModuleFactoryLoader`

Closes #9726
2016-07-08 15:31:50 -07:00
245b0910ed feat(router): add activate and deactivate events to RouterOutlet 2016-07-08 15:01:52 -07:00
a77db44129 feat(router): empty-path routes should inherit matrix params 2016-07-08 14:48:59 -07:00
34b3c534e7 fix(router): disallow root segments with matrix params 2016-07-08 14:47:55 -07:00
fa47890032 refactor(core): clean up platform bootstrap and initTestEnvironment
- Introduces `CompilerFactory` which can be part of a `PlatformRef`.
- Introduces `WorkerAppModule`, `WorkerUiModule`, `ServerModule`
- Introduces `serverDynamicPlatform` for applications using runtime compilation
  on the server.
- Changes browser bootstrap for runtime and offline compilation (see below for an example).
  * introduces `bootstrapModule` and `bootstrapModuleFactory` in `@angular/core`
  * introduces new `browserDynamicPlatform` in `@angular/platform-browser-dynamic
- Changes `initTestEnvironment` (which used to be `setBaseTestProviders`) to not take a compiler factory any more (see below for an example).

BREAKING CHANGE:

## Migration from `setBaseTestProviders` to `initTestEnvironment`:

- For the browser platform:
  BEFORE:
  ```
  import {setBaseTestProviders} from ‘@angular/core/testing’;
  import {TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
      TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS} from ‘@angular/platform-browser-dynamic/testing’;

  setBaseTestProviders(TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
      TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS);
  ```

  AFTER:
  ```
  import {initTestEnvironment} from ‘@angular/core/testing’;
  import {browserDynamicTestPlatform,
      BrowserDynamicTestModule} from ‘@angular/platform-browser-dynamic/testing’;

  initTestEnvironment(
      BrowserDynamicTestModule,
      browserDynamicTestPlatform());

  ```
- For the server platform:
  BEFORE:
  ```
  import {setBaseTestProviders} from ‘@angular/core/testing’;
  import {TEST_SERVER_PLATFORM_PROVIDERS,
      TEST_SERVER_APPLICATION_PROVIDERS} from ‘@angular/platform-server/testing/server’;

  setBaseTestProviders(TEST_SERVER_PLATFORM_PROVIDERS,
      TEST_SERVER_APPLICATION_PROVIDERS);
  ```

  AFTER:
  ```
  import {initTestEnvironment} from ‘@angular/core/testing’;
  import {serverTestPlatform,
      ServerTestModule} from ‘@angular/platform-browser-dynamic/testing’;

  initTestEnvironment(
      ServerTestModule,
      serverTestPlatform());

  ```

## Bootstrap changes
```
@AppModule({
  modules: [BrowserModule],
  precompile: [MainComponent],
  providers: […], // additional providers
  directives: […], // additional platform directives
  pipes: […] // additional platform pipes
})
class MyModule {
  constructor(appRef: ApplicationRef) {
    appRef.bootstrap(MainComponent);
  }
}

// offline compile
import {browserPlatform} from ‘@angular/platform-browser’;
import {bootstrapModuleFactory} from ‘@angular/core’;

bootstrapModuleFactory(MyModuleNgFactory, browserPlatform());

// runtime compile long form
import {browserDynamicPlatform} from ‘@angular/platform-browser-dynamic’;
import {bootstrapModule} from ‘@angular/core’;

bootstrapModule(MyModule, browserDynamicPlatform());
```

Closes #9922
Part of #9726
2016-07-08 13:41:38 -07:00
d84a43c828 fix(TemplateParser): add support for data-template attribute
fixes #9904
2016-07-08 13:07:01 -07:00
9a1babb30c refactor(TemplateParser): cleanup 2016-07-08 13:07:01 -07:00
30a332ee36 feat(forms): updateValue() for form groups and form arrays (#9901)
Closes #9553
2016-07-08 13:04:25 -07:00
426b002897 docs(developer): update building and testing instructions (#9903)
fixes #9683
2016-07-08 12:00:27 -07:00
2de8364de2 feat(ExpressionChangedAfterItHasBeenCheckedException): more meaningful error message
fixes #9882
2016-07-08 10:34:22 -07:00
eacc9e6541 refactor: misc cleanup 2016-07-08 10:34:22 -07:00
749dec7dfb doc(api): fix invalid doc links (#9873)
Errors were reported during API doc generation.
2016-07-07 23:02:35 -07:00
96a9e66616 feat(router): add the ANALYZE_FOR_PRECOMPILE provider to make dev ergonomics better 2016-07-07 18:07:10 -07:00
46e105f3ab fix(router): encode/decode params and path segments 2016-07-07 17:33:42 -07:00
f7a0e9ecb6 fix(router): fix RouterLinKActive to work with RouterLink 2016-07-07 17:33:42 -07:00
93025d1bc6 fix(forms): export form directive arrays for offline compile (#9893) 2016-07-07 17:29:20 -07:00
98d49d4ce3 refactor(core): make lockRunMode a noop and deprecate it.
BREAKING CHANGE:
- `lockRunMode` is deprecated and no more needed.

Closes #9878
2016-07-07 16:16:55 -07:00
1426f680f5 refactor(core): add a deprecation message for using PLATFORM_DIRECTIVES / PLATFORM_PIPES / CompilerConfig / XHR as regular providers in bootstrap.
We still support this via a hack, but should remove this soon.

This also fixes tests for module directives / pipes
as they used directives / pipes that were
already present in the underlying platform.
2016-07-07 16:16:55 -07:00
c7fc51a185 chore(public api): run build.sh before generating the api doc (#9889) 2016-07-07 15:39:53 -07:00
b7e69bc1a1 fix(NgPlural): expression inside cases (#9883)
fixes #9868
2016-07-07 14:47:06 -07:00
72544ba551 feat(router): add RouterTestModule 2016-07-07 14:28:01 -07:00
c43dd5a655 refactor(router): renamed RouterAppModule into RouterModule 2016-07-07 14:28:01 -07:00
7f4954bed6 fix(animations): change trigger binding syntax to function as a property binding []
Animation triggers can now be set via template bindings `[]`

BREAKING CHANGE:

animation trigger expressions within the template that are assigned as
an element attribute (e.g. `@prop`) are deprecated. Please use the
Angular2 property binding syntax (e.g. `[@prop]`) when assigning
properties.

```ts
// this is now deprecated
<div @trigger="expression"></div>

// do this instead
<div [@trigger]="expression"></div>
```
2016-07-07 14:13:06 -07:00
f1fc1dc669 revert: fix(animations): ensure all child elements are rendered before running animations
This reverts commit cbe85a0893.
2016-07-07 14:12:17 -07:00
cbe85a0893 fix(animations): ensure all child elements are rendered before running animations
Closes #9402
Closes #9775
2016-07-07 14:10:04 -07:00
7073cf74fe feat(core): allow to add precompiled tokens via a provider
Introduces the new `ANALYZE_FOR_PRECOMPILE` token. This token can be used to
create a virtual provider that will populate the `precompile` fields of
components and app modules based on its
`useValue`. All components that are referenced in the `useValue`
value (either directly or in a nested array or map) will be added
to the `precompile` property.

closes #9874
related to #9726
2016-07-07 12:16:48 -07:00
9d265b6f61 feat(forms): add modules for forms and deprecatedForms (#9859)
Closes #9732

BREAKING CHANGE:

We have removed the deprecated form directives from the built-in platform directive list, so apps are not required to package forms with their app. This also makes forms friendly to offline compilation.

Instead, we have exposed three modules:

OLD API:
- `DeprecatedFormsModule`

NEW API:
- `FormsModule`
- `ReactiveFormsModule`

If you provide one of these modules, the default forms directives and providers from that module will be available to you app-wide.  Note: You can provide both the `FormsModule` and the `ReactiveFormsModule` together if you like, but they are fully-functional separately.

**Before:**
```ts
import {disableDeprecatedForms, provideForms} from @angular/forms;

bootstrap(App, [
   disableDeprecatedForms(),
   provideForms()
]);
```

**After:**

```ts
import {DeprecatedFormsModule} from @angular/common;

bootstrap(App, {modules: [DeprecatedFormsModule] });
```

-OR-

```ts
import {FormsModule} from @angular/forms;

bootstrap(App, {modules: [FormsModule] });
```

-OR-

```ts
import {ReactiveFormsModule} from @angular/forms;

bootstrap(App, {modules: [ReactiveFormsModule] });
```

You can also choose not to provide any forms module and run your app without forms.

Or you can choose not to provide any forms module *and* provide form directives at will.  This will allow you to use the deprecatedForms API for some components and not others.

```
import {FORM_DIRECTIVES, FORM_PROVIDERS} from @angular/forms;

@Component({
   selector: some-comp,
   directives: [FORM_DIRECTIVES],
   providers: [FORM_PROVIDERS]
})
class SomeComp
```
2016-07-07 11:32:51 -07:00
776a83f9da doc(i18nPluralPipe): update API doc example (#9862) 2016-07-07 08:48:37 -07:00
f29457f3f0 fix(datePipe): short timezone not displayed, closes #9812 (#9816) 2016-07-07 08:47:30 -07:00
a005d1595e chore(compiler-cli): correct homepage URL in package.json (#9869) 2016-07-07 08:43:53 -07:00
8d746e3f67 feat(testing): add implicit test module
Every test now has an implicit module. It can be configured via `configureModule` (from @angular/core/testing)
to add providers, directives, pipes, ...

The compiler now has to be configured separately via `configureCompiler` (from @angular/core/testing)
to add providers or define whether to use jit.

BREAKING CHANGE:
- Application providers can no longer inject compiler internals (i.e. everything
  from `@angular/compiler). Inject `Compiler` instead. This reflects the
  changes to `bootstrap` for module support (3f55aa609f).
- Compiler providers can no longer be added via `addProviders` / `withProviders`.
  Use the new method `configureCompiler` instead.
- Platform directives / pipes need to be provided via
  `configureModule` and can no longer be provided via the
  `PLATFORM_PIPES` / `PLATFORM_DIRECTIVES` tokens.
- `setBaseTestProviders()` was renamed into `initTestEnvironment` and 
  now takes a `PlatformRef` and a factory for a
  `Compiler`.
- E.g. for the browser platform:
  
  BEFORE:
  ```
  import {setBaseTestProviders} from ‘@angular/core/testing’;
  import {TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
      TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS} from ‘@angular/platform-browser-dynamic/testing’;
  
  setBaseTestProviders(TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
      TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS);   
  ```

  AFTER:
  ```
  import {setBaseTestProviders} from ‘@angular/core/testing’;
  import {browserTestCompiler, browserDynamicTestPlatform,
      BrowserDynamicTestModule} from ‘@angular/platform-browser-dynamic/testing’;
  
  initTestEnvironment(
      browserTestCompiler,
      browserDynamicTestPlatform(),
      BrowserDynamicTestModule);

  ```
- E.g. for the server platform:
  
  BEFORE:
  ```
  import {setBaseTestProviders} from ‘@angular/core/testing’;
  import {TEST_SERVER_PLATFORM_PROVIDERS,
      TEST_SERVER_APPLICATION_PROVIDERS} from ‘@angular/platform-server/testing/server’;
  
  setBaseTestProviders(TEST_SERVER_PLATFORM_PROVIDERS,
      TEST_SERVER_APPLICATION_PROVIDERS);   
  ```

  AFTER:
  ```
  import {setBaseTestProviders} from ‘@angular/core/testing’;
  import {serverTestCompiler, serverTestPlatform,
      ServerTestModule} from ‘@angular/platform-browser-dynamic/testing’;
  
  initTestEnvironment(
      serverTestCompiler,
      serverTestPlatform(),
      ServerTestModule);

  ```

Related to #9726
Closes #9846
2016-07-06 18:04:19 -07:00
37e6da6dfb refactor(router): clean up naming 2016-07-06 16:19:52 -07:00
8aa2a0c1b2 feat(router): add RouterAppModule 2016-07-06 16:00:40 -07:00
6bfd514caf fix(router): remove a circular dep 2016-07-06 14:38:05 -07:00
ad3f18c0dd chore(build): update build script to increase node's heap size 2016-07-06 14:38:05 -07:00
39d04b4a15 chore(public_api): update public api 2016-07-06 14:38:05 -07:00
6fbe56dbf2 feat(router): update the example app to use lazily-loaded modules 2016-07-06 14:38:05 -07:00
8ebb8e44c8 feat(router): add support for lazily loaded modules 2016-07-06 14:38:05 -07:00
6fcf962fb5 feat(core): add AddModuleFactoryLoader 2016-07-06 14:38:05 -07:00
2708ce6a17 docs(api): fix links (#9852) 2016-07-06 14:34:27 -07:00
30bec78da3 fix(compiler): Missing metadata files should result in undefined (#9704)
RelectorHost threw an exception when metadata was requested for a
.d.ts file that didn't have a .metadata.json file.  Changed it to
return undefined.

Fixes #9678
2016-07-06 14:26:31 -07:00
9a04fcd061 feat(compiler): Expression span information and error correction (#9772)
Added error correction so the parser always returns an AST
Added span information to the expression parser
Refactored the test to account for the difference in error reporting
Added tests for error corretion
Modified tests to validate the span information
2016-07-06 14:06:47 -07:00
ae62f082fd docs(api): fix broken example urls (#9828) 2016-07-06 13:57:38 -07:00
9cc3b2ca9e fix(animations): ensure a null easing value is never used with web-animations
Closes #9780
Closes #9752
2016-07-06 11:25:54 -07:00
3fe1cb0253 refactor(core): ensure CSS parser uses ParseSourceSpan to track ast locations
This commit also fixes up any remaining TODO comments.

Closes #9778
2016-07-06 11:22:45 -07:00
0ed7773223 build(gulp): Fix paths for public-api tasks on Windows (#9794) 2016-07-05 20:52:35 -07:00
3f55aa609f feat(browser): use AppModules for bootstrap in the browser
This introduces the `BrowserModule` to be used for long form
bootstrap and offline compile bootstrap:

```
@AppModule({
  modules: [BrowserModule],
  precompile: [MainComponent],
  providers: […], // additional providers
  directives: […], // additional platform directives
  pipes: […] // additional platform pipes
})
class MyModule {
  constructor(appRef: ApplicationRef) {
    appRef.bootstrap(MainComponent);
  }
}

// offline compile
import {bootstrapModuleFactory} from ‘@angular/platform-browser’;
bootstrapModuleFactory(MyModuleNgFactory);

// runtime compile long form
import {bootstrapModule} from ‘@angular/platform-browser-dynamic’;
bootstrapModule(MyModule);
```

The short form, `bootstrap(...)`, can now creates a module on the fly,
given `directives`, `pipes, `providers`, `precompile` and `modules`
properties.

Related changes:
- make `SanitizationService`, `SecurityContext` public in `@angular/core` so that the offline compiler can resolve the token
- move `AnimationDriver` to `platform-browser` and make it
  public so that the offline compiler can resolve the token

BREAKING CHANGES:
- short form bootstrap does no longer allow
  to inject compiler internals (i.e. everything 
  from `@angular/compiler). Inject `Compiler` instead.
  To provide custom providers for the compiler,
  create a custom compiler via `browserCompiler({providers: [...]})`
  and pass that into the `bootstrap` method.
2016-07-02 20:35:09 -07:00
74b45dfbf8 Revert "refactor(core): ensure CSS parser uses ParseSourceSpan to track ast locations"
This reverts commit 5c9f871b21.
2016-07-01 21:21:56 -07:00
5c9f871b21 refactor(core): ensure CSS parser uses ParseSourceSpan to track ast locations
This commit also fixes up any remaining TODO comments.

Closes #9285
2016-07-01 17:18:14 -07:00
77dc6ef411 fix(forms): mark control containers as touched when child controls are touched (#9735) 2016-07-01 15:36:04 -07:00
5eca6e4e40 bug(datePipe): passing "hh" to the datepipe (#9774)
closes #9759
2016-07-01 15:34:57 -07:00
0c65d5cf2b fix(router): handle router outlets in ngIf 2016-06-30 22:14:42 -07:00
f65ebec3ed fix(router): update links when query params change 2016-06-30 22:14:42 -07:00
81bf3f66ca docs(router): rename global redirects into absolute redirects 2016-06-30 22:14:42 -07:00
3cbded6694 fix(forms): use change event for select multiple (#9713) 2016-06-30 20:24:39 -07:00
137fff9632 fix(router): remove private and internal annotations (#9753) 2016-06-30 19:39:13 -07:00
695c08b9dd test(forms): add test for multi-select and custom accessors (#9624) 2016-06-30 18:04:00 -07:00
119794249b doc(changelog): cleans up markdown for breaking changes (#9739) 2016-06-30 17:31:39 -07:00
afb72164e4 fix(docs): typo in comments (#9743)
correct a typo in comments
2016-06-30 15:18:41 -07:00
9fee5630fd chore: fix package on changelog (#9736) 2016-06-30 15:17:00 -07:00
01de58d650 chore(router): bump up version number 2016-06-30 14:58:59 -07:00
dabf214f17 fix(router): remove private and internal annotations (#9745) 2016-06-30 14:47:55 -07:00
fb2539e1d5 fix(router): remove the precompile warning 2016-06-30 14:33:04 -07:00
ad9f02a73e chore: enable cyclic dependency check
Closes #9742
2016-06-30 14:28:22 -07:00
2d73583253 chore(compiler): fix cyclic dependency 2016-06-30 14:28:22 -07:00
73f017bad9 fix(typescript): make router compile with typescript@next
fixes #9731
2016-06-30 11:51:52 -07:00
055282f156 chore(router): bump up version number 2016-06-30 11:45:31 -07:00
fe7de53b89 chore(router): update router change log 2016-06-30 11:45:31 -07:00
17e4cfc748 feat(core): introduce @AppModule
Main part for #9726
Closes #9730
2016-06-30 11:34:40 -07:00
1608d91728 docs(changelog): change log and package.json to rc4
Closes #9727
2016-06-30 10:16:32 -07:00
a3b90411aa fix(router): fix RouterLinkActive to handle the case when the link has extra paths 2016-06-30 09:26:57 -07:00
5781b96490 fix(router): redirect should not add unnecessary brackets 2016-06-30 09:26:57 -07:00
f208ee0d57 fix(router): reexport router directives 2016-06-30 09:26:57 -07:00
8aa388de6c doc(directive): fixes incorrect example code (#9635) 2016-06-29 22:16:43 -07:00
51d4c9dcbd fix(compiler): make code easier to type check
These changes are needed for the G3 sync as we use a different version/settings of Typescript than on Github.

closes #9701
2016-06-29 10:43:58 -07:00
e81dea695c fix(compiler): report not existing files as errors
Closes #9690
2016-06-29 07:35:34 -07:00
3fec27961e fix: support *directive on <template> (#9691)
fixes #7315
2016-06-28 21:53:41 -07:00
3784696b9e fix(router): make the contstructor of the router service public 2016-06-28 18:39:37 -07:00
8c45aebc18 fix(router): make router links work on non-a tags 2016-06-28 18:39:37 -07:00
810c722413 docs(security): point users to docs when sanitization fails. (#9680) 2016-06-28 18:13:46 -07:00
e2116c53f3 fix(upgrade): add peerDependency on platform-browser-dynamic (#9674)
Closes #9623
2016-06-28 17:27:28 -07:00
296a447e3c docs(router): add api docs 2016-06-28 14:49:29 -07:00
0961bd1eff feat(forms): use formControlName on radio buttons when name is absent (#9681) 2016-06-28 15:21:53 -06:00
9340e1b065 docs(security): security api doc update and fix stability marker for Type 2016-06-28 14:01:48 -07:00
ae4fa56ee9 fix(public API): update golden files
broken by #9606
2016-06-28 12:21:50 -07:00
2d9d7f1310 fix(security): allow empty CSS values. (#9675) 2016-06-28 11:45:02 -07:00
5ee84fe0f6 refactor: add types (#9606)
relates to #9100
2016-06-28 11:35:59 -07:00
1620426393 fix(http): don't encode values that are allowed in query (#9651)
This implements a new class, QueryEncoder, that provides
methods for encoding keys and values of query parameter.
The encoder encodes with encodeURIComponent, and then
decodes a whitelist of allowed characters back to their
unencoded form.

BREAKING CHANGE:

The changes to Http's URLSearchParams serialization now 
prevent encoding of these characters inside query parameters
which were previously converted to percent-encoded values:

@ : $ , ; + ; ? /

The default encoding behavior can be overridden by extending
QueryEncoder, as documented in the URLSearchParams service.

Fixes #9348
2016-06-28 11:31:35 -07:00
bf598d6b8b feat(compiler): support sync runtime compile
Adds new abstraction `Compiler` with methods
`compileComponentAsync` and `compileComponentSync`.
This is in preparation of deprecating `ComponentResolver`.

`compileComponentSync` is able to compile components
synchronously given all components either have an inline
template or they have been compiled before.

Also changes `TestComponentBuilder.createSync` to
take a `Type` and use the new `compileComponentSync` method.

Also supports overriding the component metadata even if
the component has already been compiled.

Also fixes #7084 in a better way.

BREAKING CHANGE:
`TestComponentBuilder.createSync` now takes a component type
and throws if not all templates are either inlined
are compiled before via `createAsync`.

Closes #9594
2016-06-28 10:26:16 -07:00
24eb8389d2 fix: public api surface fixes + stability markers
- ts-api-guardian will now error if a new public symbol is added with a stability marker (`@stable`, `@experimental`, `@deprecated`)
- DomEventsPlugin and KeyEventsPlugin were removed from public api surface - these classes is an implementation detail
- deprecated BROWSER_PROVIDERS was removed completely
- `@angular/compiler` was removed from the ts-api-guardian check since this package shouldn't contain anything that users need to directly import
- the rest of the api surface was conservatively marked as stable or experimental

BREAKING CHANGES: DomEventsPlugin and KeyEventsPlugin previously exported from core are no longer public - these classes are implementation detail.

Previously deprecated BROWSER_PROVIDERS was completely removed from platform-browser.

Closes #9236
Closes #9235
Ref #9234
2016-06-28 07:39:40 -07:00
fcfddbf79c feat(router): add pathMatch property to replace terminal 2016-06-27 20:21:30 -07:00
dc64e90ab9 feat(router): use componentFactoryResolver 2016-06-27 20:21:30 -07:00
e12b1277df feat(core): split ChangeDetectorStrategy into ChangeDetectionStrategy and ChangeDetectorStatus 2016-06-27 20:19:20 -07:00
797914e948 fix(forms): emit statusChange when child controls have async validator (#9652) 2016-06-27 21:01:24 -06:00
e0b0a594bb fix(animations): ensure void => * animations are triggered when an expression is omitted
Closes #9327
Closes #9381
2016-06-27 18:55:10 -07:00
ed0ade6f34 fix(forms): make radio button selection logic more flexible (#9646)
Closes #9558
2016-06-27 15:29:33 -06:00
5cc7b41f39 Revert "fix(Compiler): relax childIsRecursive check (#8705)"
This fix prevented waiting for child components even if the cycle was only introduced via the `directives` array, i.e. without actually having a cycle. This easily causes issues for applications that have one shared list of directives for all components.

This reverts commit 3d5bb23184.

Closes #9647
2016-06-27 14:27:03 -07:00
f2f1ec0117 feat(router): implement data and resolve 2016-06-27 14:25:56 -07:00
e913d9954d chore(typings): restrict Angular to es5+collections+promise 2016-06-27 13:58:59 -07:00
d20488752b fix(router): top-levels do not work in ngIf 2016-06-27 13:34:54 -07:00
855f3afb28 fix(router): canceled navigations should return a promise that is resolved with false 2016-06-27 13:34:54 -07:00
3f44377f2f fix(router): handle empty path with query params 2016-06-27 13:34:54 -07:00
90295e3252 fix(router): preserve fragment on initial load 2016-06-27 13:34:54 -07:00
a620f95891 build(npm): upgrade ts-api-guardian to v0.1.4
Closes #9642
2016-06-27 12:27:59 -07:00
db66509e66 test(security): tests for HTML5 elements, srcset.
Part of #9572.
2016-06-27 12:19:03 -07:00
6605eb30e9 feat(security): allow more HTML5 elements and attributes in sanitizers
Allow more elements and attributes from the HTML5 spec which were stripped by the htmlSanitizer.

fixes #9438

feat(security): allow audio data URLs in urlSanitizer

test(security) : add test for valid audio data URL

feat(security): allow and sanitize srcset attributes

test(security): test for srcset sanitization
2016-06-27 12:19:03 -07:00
3644eef860 feat(DomRenderer): Adding support for document fragments in SVG foreign objects (#9458) 2016-06-27 08:26:45 -07:00
fb2509675d doc(changelog): add backticks around html elements so they actually render (#9637) 2016-06-27 08:21:34 -07:00
eef9512ce6 fix(forms): async validator-directives process Observables correctly (#8186)
Closes #/8022
2016-06-26 16:52:50 -06:00
9f00a1b902 fix(forms): add select multiple accessor as built-in accessor 2016-06-26 16:24:27 -06:00
c369bc747d docs: update cheatsheet import lines (#9614) 2016-06-26 07:31:35 -07:00
c03e1f2f59 feat(forms): add support for formArrayName
Closes #9251
2016-06-25 13:30:53 -07:00
17dcbf66b9 feat(forms): expose ValidatorFn and AsyncValidatorFn
Closes #8834
2016-06-24 18:24:11 -07:00
40b907a657 refactor(testing): remove wrapping of Jasmine functions (#9564)
Instead, the async function now determines whether it should return a promise
or instead call a done function parameter. Importing Jasmine functions
from `@angular/core/testing` is no longer necessary and is now deprecated.

Additionally, beforeEachProviders is also deprecated, as it is specific
to the testing framework. Instead, use the new addProviders method directly.

Before:
```js
import {beforeEachProviders, it, describe, inject} from 'angular2/testing/core';

describe('my code', () => {
  beforeEachProviders(() => [MyService]);

  it('does stuff', inject([MyService], (service) => {
    // actual test
  });
});
```

After:
```js
import {addProviders, inject} from 'angular2/testing/core';

describe('my code', () => {
  beforeEach(() => {
    addProviders([MyService]);
  });

  it('does stuff', inject([MyService], (service) => {
    // actual test
  });
});
```
2016-06-24 17:48:35 -07:00
a33195dcf3 fix(core/testing compiler/testing): move TestComponentBuilder to core/testing (#9590)
TestComponentBuilder now lives in core/testing. compiler/testing contains a private
OverridingTestComponentBuilder implementation which handles the private behavior
we need to override templates. This is part of the effort to simplify the testing
imports and hide compiler APIs.

Closes #9585

BREAKING CHANGE:

`TestComponentBuilder` is now imported from `@angular/core/testing`. Imports
from `@angular/compiler/testing` are deprecated.

Before:

```
import {TestComponentBuilder, TestComponentRenderer, ComponentFixtureAutoDetect} from '@angular/compiler/testing';
```

After:
```
import {TestComponentBuilder, TestComponentRenderer, ComponentFixtureAutoDetect} from '@angular/core/testing';
```
2016-06-24 17:35:01 -07:00
c693c03f1d test(node): enable source maps in test.sh node (#9589) 2016-06-24 16:52:41 -07:00
de127109f9 feat(forms): make valueChanges and statusChanges available on abstract control directives 2016-06-24 14:37:19 -07:00
83208983b3 chore(router): bump up version number 2016-06-24 13:07:42 -07:00
327d04c9c6 chore(router): clang-format 2016-06-24 12:44:32 -07:00
54edce2bab fix(router): wildcard don't get notified on url changes 2016-06-24 12:44:32 -07:00
1a145ac500 fix(router): default exact to false in routerLinkActiveOptions 2016-06-24 12:44:32 -07:00
9f978cf49d test(router): add a test checking that you can use a slash in query params 2016-06-24 12:44:32 -07:00
41b781107b fix(router): doen't throw on canDeactive when route hasn't advanced 2016-06-24 12:44:32 -07:00
dcf75126bf fix(common/testing): remove internal MockLocationStrategy from common/testing (#9562)
BREAKING CHANGE:

MockLocationStrategy was intended to be internal only and is now removed
from the `@angular/common/testing` public api.

Use `SpyLocation` from `@angular/common/testing` for location testing.
2016-06-24 12:41:57 -07:00
1143b0389a fix(core/testing): move ComponentFixture to core (#9386)
BREAKING CHANGE:

`ComponentFixture` will be moving out of `@angular/compiler/testing` to `@angular/core/testing` in
this release. For now, it is deprecated from `@angular/compiler/testing`.
2016-06-24 12:41:49 -07:00
97a2119596 fix(forms): ngModel should emit valueChanges and statusChanges asynchronously 2016-06-24 12:37:46 -07:00
fbd2dd9ca2 fix(router): handle path:'' redirects and matches 2016-06-24 11:39:41 -07:00
f463e09b9f fix(ngc): work with typescript@next
This is required due to breaking change in TS, see
https://github.com/Microsoft/TypeScript/pull/8841#issuecomment-227300348
2016-06-24 10:27:31 -07:00
42a5b6cbda chore(testing): upgrade ts-api-guardian to 0.1.3 2016-06-23 18:19:32 -07:00
0ad1215a92 build(changelog): remove old changelog script and add gulp task 2016-06-23 17:35:57 -07:00
7733c97df3 build(npm): update conventional-changelog dependency
Fixes #5672
2016-06-23 17:35:57 -07:00
8a9e9c7bd3 fix(core/testing): clean up the core testing public API (#9466)
Previously, we were exporting internal mocks and helpers. Move these
to core/testing/testing_internal or remove them if they were
never used.

Remove deprecated items - injectAsync, clearPendingTimers.

BREAKING CHANGE:

Remove the following APIs from `@angular/core/testing`, which have been deprecated or were
never intended to be publicly exported:

```
injectAsync
clearPendingTimers
Log
MockAppliacationHref
MockNgZone
clearPendingTimers
getTypeOf
instantiateType
```

Instead of `injectAsync`, use `async(inject())`.

`clearPendingTimers` is no longer required.
2016-06-23 17:10:22 -07:00
3d8eb8cbca fix(platform-browser/testing): clean up public api for platform-browser/testing (#9519)
Mostly, removing things that were never intended to be exported publicy.

BREAKING CHANGE:

The following are no longer publicly exported APIs. They were intended as internal
utilities and you should use your own util:

```
browserDetection,
dispatchEvent,
el,
normalizeCSS,
stringifyElement,
expect (and custom matchers for Jasmine)
```
2016-06-23 16:42:25 -07:00
894747c34c fix(platform-browser/testing-e2e): clean up unused exports from e2e testing helpers (#9387) 2016-06-23 16:14:31 -07:00
8d5a312585 chore(api): clean up compiler/testing api (#9520)
Do not export MockXHR, which is a private helper.
2016-06-23 15:52:18 -07:00
8eb81b3741 ci: add updated ts-api-guardian check 2016-06-23 14:26:40 -07:00
22d8f73bc9 test: add public api golden files
Includes a few style fixes on "* as foo" imports.
2016-06-23 14:26:40 -07:00
249a6bdd98 test: upgrade ts-api-guardian to v0.1.2 2016-06-23 14:26:40 -07:00
3ad81b1beb test(security): simplify integration test. 2016-06-23 13:57:51 -07:00
5ab0534164 test(security): Ensure xlink:href is not bindable.
The DOM schema does not allow binding any properties to dangerous SVG
attributes/properties. This change adds a smoke test to verify that
behaviour, by testing that `xlink:href` (a sample dangerous property)
is not bindable.

Fixes #9510.
2016-06-23 13:57:51 -07:00
5150344213 fix(common): add license header to localization.ts 2016-06-23 13:27:43 -07:00
98cef76931 fix(security): no warning when sanitizing escaped html (#9392) (#9413) 2016-06-23 13:06:19 -07:00
6c5b653593 feat(core): add @Component.precompile and ComponentFactoryResolver
Part to #9467
Closes #9543
2016-06-23 12:10:04 -07:00
9ed8f2d26e fix(compiler): don't inject viewProviders into content child elements
E.g. in the following scenario,
`some-directive` should not be able to inject
any view provider that `my-comp-with-view-providers`
declares.

```
<my-comp-with-view-providers>
  <div some-directive></div>
</my-comp-with-view-providers>
```
2016-06-23 12:10:04 -07:00
33a2f86b28 chore: remove stale tsconfig.json
This tsconfig.json prevents fast round trip cycles in VsCode
as it relies on the package-dist folders to be filled.
2016-06-23 12:10:04 -07:00
fed1672a43 refactor(i18n): I18nPipe uses NgLocalization (#9313)
and some refactoring
2016-06-23 11:44:05 -07:00
54dbed4f48 fix(typings): don't test compiler-cli typings on TS 1.8 2016-06-23 10:57:03 -07:00
df759b8d4b fix(core): improve error message for broken bindings
Fixes #6820

Closes #9536
2016-06-23 19:28:56 +02:00
6edf0474cc feat(forms): add support for standalone ngModel dirs inside forms
Closes #9230
2016-06-23 10:16:47 -07:00
826f89f862 fix(ngc): correct dependencies for compiler-cli
Update compiler-cli dependencies to include minimist and also increment tsc-wrapped to 0.2.0.  There is signature mismatch between tsc-wrapped (v0.1.0) collector.js#getMetadata and compiler-cli reflector_host.js#getMetadataFor that caused an error anytime ngc was executed. The error received was as follows.

`TypeError: Cannot read property 'getSymbolsInScope' of undefined`

After forcing NPM to install @angular/tsc-wrapped@latest the error was resolved.

Fixes #9540
2016-06-23 10:16:04 -07:00
c43aec2182 fix(animations): make sure the easing value is passed into the web-animations player
Closes #9517
Closes #9523
2016-06-23 10:14:18 -07:00
ae75e3640a chore(lint): Added license headers to most TypeScript files
Relates to #9380
2016-06-23 09:47:54 -07:00
a5f2cc73f6 chore(lint): Add lint check for license headers
Added a tslint check to make sure all source files begin with a license
header (at the very beginning or after a `#!`).

Relates to #9380
2016-06-23 09:46:32 -07:00
e1e5c40ef7 fix(testing): remove the toThrowErrorWith matcher (jasmine has toThrowError)
BREAKING CHANGE:

Before:

    expect(...).toThrowErrorWith(msg);

After:

    expect(...).toThrowError(msg);
2016-06-23 08:58:52 -07:00
6420f75320 fix(testing): remove the toMatchPattern matcher (jasmine has toMatch)
BREAKING CHANGE:

Before:

    expect(...).toMatchPattern(pattern);

After:

    expect(...).toMatch(pattern);
2016-06-23 08:58:28 -07:00
5face35ae5 refactor: misc cleanup 2016-06-23 08:56:10 -07:00
398060d5ff fix(NgSwitch): display deprecation message only once 2016-06-23 08:56:10 -07:00
638fd744aa feat(forms): support updating of validators on exiting controls (#9516)
lint

fix

async

d

test

test
2016-06-23 08:18:07 -07:00
098b461b69 fix(core): report duplicate template bindings in templates
Fixes #7315

BREAKING CHANGES:

Previously multiple template bindings on one element
(ex. `<div *ngIf='..' *ngFor='...'>`) were allowed but most of the time
were leading to undesired result. It is possible that a small number
of applications will see template parse errors that shuld be fixed by
nesting elements or using `<template>` tags explicitly.

Closes #9462
2016-06-23 15:59:07 +02:00
9decc3d823 build: fix some issues on Windows platforms
Closes #9450
2016-06-23 10:46:01 +02:00
a5f2e205ef fix(http): add search param escaping for keys (#9166) 2016-06-22 18:23:15 -07:00
8899b83927 chore(typescript): Enabled noFallthroughCasesInSwitch
Turned on the noFallthroughCasesInSwitch flag in tsconfig and fixed
a few cases where there were fallthroughs.
2016-06-22 16:08:55 -07:00
f6a410a4a8 feat(QueryList): implement some() (#9464)
closes #9443
2016-06-22 13:13:31 -07:00
3d5bb23184 fix(Compiler): relax childIsRecursive check (#8705)
Fix how the compiler checks for recursive components by also considering
component descendants. Previously, it only checked if the current
component was evaluated previously. This failed in certain cases of
mutually recursive components, causing `createAsync` in tests to not
resolve.

closes [7084](https://github.com/angular/angular/issues/7084)
2016-06-22 07:02:11 -07:00
ef37d2ae0b example(router): add an example app for the new router 2016-06-21 23:19:26 -07:00
2eb234bc63 chore(router): enable bundling 2016-06-21 23:19:26 -07:00
758ee95880 fix(router): fix tsconfig to use es2015 modules 2016-06-21 23:19:26 -07:00
40e1112a8e chore(router): test karma config to rerun tests on change 2016-06-21 23:19:26 -07:00
397f5e2390 refactor(HtmlLexer): simplify the code 2016-06-21 18:03:22 -07:00
1a212259af refactor: cleanup lexers & parsers 2016-06-21 18:03:22 -07:00
f114dd300b fix(core): properly report missing providers and viewProviders (#9411)
Fixes #8237
2016-06-21 17:27:27 -07:00
5954a26bce docs: add latest changelog updates (#9415) 2016-06-21 17:16:22 -07:00
bdbbe5aa20 chore(release): 2.0.0-rc.3 2016-06-21 16:34:48 -07:00
15911367a2 refactor(router): removes a circualr dep 2016-06-21 12:17:30 -07:00
8dd3f59c81 chore(router): changes the router setup to align with other modules 2016-06-21 12:17:30 -07:00
c9d28492b7 chore(router): remove lint and format tasks from router 2016-06-21 12:17:30 -07:00
d1f93072a8 chore(router): clang-format 2016-06-21 12:17:30 -07:00
92d8bf9619 feat(router): add support for componentless routes 2016-06-21 12:17:30 -07:00
bd2281e32d fix(resolve): change resolve not to resolve root activate route 2016-06-21 12:17:30 -07:00
0c50bc6449 fix(router): url serializer should handle segments without primary children 2016-06-21 12:17:30 -07:00
f164715678 chore(README): fix a typo 2016-06-21 12:17:30 -07:00
2aa615b4ae chore(router): bump up version 2016-06-21 12:17:30 -07:00
42c89b1b9b docs(router): add a README to include a link to the guide 2016-06-21 12:17:30 -07:00
f6b75f56ad fix(router): typo in starts with slash validation error 2016-06-21 12:17:30 -07:00
280540e4a2 fix(router): change serialize not to require parenthesis in query string to be encoded 2016-06-21 12:17:30 -07:00
fea216db12 fix(router): fixes a type issue in a test 2016-06-21 12:17:30 -07:00
b260eb06f6 fix(router): change postinstall hook to devsetup to not require having 'typings' installed 2016-06-21 12:17:30 -07:00
1c937a10f9 chore(router): add changelog 2016-06-21 12:17:30 -07:00
ca23b4c55f feat(router): add route config validation 2016-06-21 12:17:30 -07:00
7e12208ca6 feat(router): do not support paths starting with / 2016-06-21 12:17:30 -07:00
2773281338 feat(router): drop index property
Use path: '/' instead of 'index: true'
2016-06-21 12:17:30 -07:00
f8e8d22e4e fix(router): stringify positional parameters when using routerLink 2016-06-21 12:17:30 -07:00
cf4a9236b9 chore(router): bump up version number 2016-06-21 12:17:30 -07:00
4450e7b246 cleanup(router): enable noImplicitAny and noImplicntReturns 2016-06-21 12:17:30 -07:00
cdbf67ee05 test(router): add a test checking that guards work for child routes 2016-06-21 12:17:30 -07:00
9a67f38728 fix(router): port fixes done on angular current router to the new one
The bugs were fixed on current angular router in the following commits:
angular/angular@b2a7fd05cb
angular/angular@fa2ce8100b
angular/angular@595bcdd1ac

Closes #12
2016-06-21 12:17:30 -07:00
25560ed048 feat(router): implement RouterLinkActive 2016-06-21 12:17:30 -07:00
2aa19fd078 feat(router): support navigating by url tree 2016-06-21 12:17:30 -07:00
cca9a58ded chore(router): bump up version number 2016-06-21 12:17:30 -07:00
d6a25325c7 chore(package): unpin version 2016-06-21 12:17:30 -07:00
a717da2d3e chore(router): bump up version number 2016-06-21 12:17:30 -07:00
b3e801ed9e feat(router): make it work with TypeScript 1.8 2016-06-21 12:17:30 -07:00
3683fb6886 refactor(router): minor refactoring 2016-06-21 12:17:30 -07:00
3bd0ce291e feat(router): mark the index property as deprecated
Use {path: '', component: A} instead of {index: true, component: A}

}#
2016-06-21 12:17:30 -07:00
523fc5536c fix(router): fix Params type to allow passing any value types when calling router.navigate 2016-06-21 12:17:30 -07:00
f5efccfb44 test(router): test update location when route does not change 2016-06-21 12:17:30 -07:00
b6ec22de6b test(router): test empty url with global redirect 2016-06-21 12:17:30 -07:00
15f27b5455 fix(providers): make providers static analysis friendly 2016-06-21 12:17:30 -07:00
127401598b feat(router): implement terminal 2016-06-21 12:17:30 -07:00
503b07f698 docs(router): add a README 2016-06-21 12:17:30 -07:00
f0a6329005 fix(router): fixes a typo 2016-06-21 12:17:30 -07:00
2982892acc cleanup(router): clang-format 2016-06-21 12:17:30 -07:00
9de56481f1 feat(router): add enableTracing option 2016-06-21 12:17:30 -07:00
777eb2f159 feat(router): emit an event when routes are recognized 2016-06-21 12:17:30 -07:00
05eebe0fed feat(router): provide meaningful toString impls 2016-06-21 12:17:30 -07:00
fdfbbd5bac chore(router): bump up version number 2016-06-21 12:17:30 -07:00
1f3f8ef6c8 fix(router): fix nested deactivation 2016-06-21 12:17:30 -07:00
820eeb49d1 chore(router): bump up version number 2016-06-21 12:17:30 -07:00
2d4be1c9eb fix(router): init is not triggered in certain scenarios 2016-06-21 12:17:30 -07:00
2fef30f619 fix(router): make stringify handle nulls 2016-06-21 12:17:30 -07:00
10113b63b5 chore(router): bump up version number 2016-06-21 12:17:30 -07:00
545caab433 fix(router): use bootstrap listener to trigger initial navigation 2016-06-21 12:17:30 -07:00
3f90659cc1 fix(router): supports index routes with path 2016-06-21 12:17:30 -07:00
131914ac94 fix(router): fix lazy loading issues 2016-06-21 12:17:30 -07:00
29a7c4538c Revert "fix(provider): fix a circular dependency & remove common providers"
This reverts commit 6375fdd4f2928d5ddeccaf11a8589a7668bc9049.
2016-06-21 12:17:30 -07:00
f195bb608c chore(router): update config before publishing to npm 2016-06-21 12:17:30 -07:00
66caabca0c feat(router): implement redirectTo 2016-06-21 12:17:30 -07:00
25c6a3715d fix(provider): fix a circular dependency & remove common providers 2016-06-21 12:17:30 -07:00
97cf0e40d5 fix(guards): Cancel in-flight guards if one returns false 2016-06-21 12:17:30 -07:00
6988a550ea cleanup(router): fix tslint errors 2016-06-21 12:17:30 -07:00
8a1cdc2dd5 chore: install typings with npm i 2016-06-21 12:17:30 -07:00
dadd5ddded chore: add lint and clang-format 2016-06-21 12:17:30 -07:00
56f8c95ee9 tests(router): add tests verifying that updating secondary segments using router link works 2016-06-21 12:17:30 -07:00
ed50e17e5b refactor(router): rename queryParameters into queryParams 2016-06-21 12:17:30 -07:00
33b518ad21 feat(router): update RouterLink to support query params and fragment 2016-06-21 12:17:30 -07:00
b0e7c14545 fix(router): add an app initializer to trigger initial navigation
Closes #10
2016-06-21 12:17:30 -07:00
5742d4720a fix(router): fix router to handle guards that return observable
Closes #19
2016-06-21 12:17:30 -07:00
9b356d9b86 fix(router): traverse route config in depth-first order
Closes #17
2016-06-21 12:17:30 -07:00
793ac3f6b4 Configure router with provided routes
Closes #9
2016-06-21 12:17:30 -07:00
9b094e42a3 chore(router): update the npm dist 2016-06-21 12:17:30 -07:00
6ce7a5a1ea docs(router): add docs 2016-06-21 12:17:30 -07:00
88920bfee1 feat(router): add support for basic events 2016-06-21 12:17:30 -07:00
2717bcc3af feat(router): implement cancelation 2016-06-21 12:17:30 -07:00
5d386dc426 chore(router): update build dir 2016-06-21 12:17:30 -07:00
f34af4f249 feat(router): add support for using classes as guard 2016-06-21 12:17:30 -07:00
f04b6978fb cleanup(router): add @internal to constructors where needed 2016-06-21 12:17:30 -07:00
ab958598d7 feat(router): implement CandDeactivate 2016-06-21 12:17:30 -07:00
1914847e72 cleanup(router): make strictNullChecks happy 2016-06-21 12:17:30 -07:00
d95f0fd83d fix(router): fix index routes 2016-06-21 12:17:30 -07:00
243612e36d refactor(router): rename candidate into snapshot 2016-06-21 12:17:30 -07:00
c5cca8e098 feat(router): add support for CanActivate guard 2016-06-21 12:17:30 -07:00
99f7404d8b refactor(router): remove rootNode function 2016-06-21 12:17:30 -07:00
9ff6b0828f feat(router): make activation sync 2016-06-21 12:17:30 -07:00
6f052d1daf feat(router): add a function to resolve components 2016-06-21 12:17:30 -07:00
63c194b71f feat(router): change recognize to return a router state candidate 2016-06-21 12:17:30 -07:00
46911117f1 feat(router): implement a function create router state out of a candidate 2016-06-21 12:17:30 -07:00
2de1030413 feat(router): add RouterStateCandidate 2016-06-21 12:17:30 -07:00
1f6ade894e cleanup(router): fix a typo 2016-06-21 12:17:30 -07:00
8407cfeac7 fix(router): fix router to take root component type instead of instance 2016-06-21 12:17:30 -07:00
91d64a2855 feat(router): export provideRouter via index 2016-06-21 12:17:30 -07:00
40a06af79b feat(router): add provideRouter to configure the router when bootstrapping an app 2016-06-21 12:17:30 -07:00
8aef86f4a0 feat(router): export all public api tokens via index 2016-06-21 12:17:30 -07:00
5bdc6ecec8 chore: adds the build dir to use with 'npm install' 2016-06-21 12:17:30 -07:00
c179b5033b feat(router): implement relative navigation 2016-06-21 12:17:30 -07:00
86f47273bc feat(router): changes router config not to use names 2016-06-21 12:17:30 -07:00
2e1bd46bb1 feat(router): add createUrlTree 2016-06-21 12:17:30 -07:00
a9e773b47b feat(router): serialize outlet names into the url 2016-06-21 12:17:30 -07:00
10d38cbb72 chore(router): change karma reporter 2016-06-21 12:17:30 -07:00
a5371bfb8a cleanup: cleanup tsconfig files 2016-06-21 12:17:30 -07:00
4b2740f270 refactor: move index.ts into src 2016-06-21 12:17:30 -07:00
5b371736b2 feat: add RouterLink 2016-06-21 12:17:30 -07:00
c9b4bcf689 refactor: move all utility functions into the utils dir 2016-06-21 12:17:30 -07:00
013f9a2bbc feat: add tree.siblings 2016-06-21 12:17:30 -07:00
5bf1c93ead docs: adds missing api docs 2016-06-21 12:17:30 -07:00
4f6ec01932 feat: implement a simple version of the router service 2016-06-21 12:17:30 -07:00
0f79e504c9 test: set up karma to support fakeAsync 2016-06-21 12:17:30 -07:00
1a4e911b8b cleanup: fix type errors when compiling with strictNullChecks enabled 2016-06-21 12:17:30 -07:00
1f98519380 feat: implement RouterOutletMap 2016-06-21 12:17:30 -07:00
aad7010952 feat: add RouterOutlet 2016-06-21 12:17:30 -07:00
1be9ea681b fix: fix source maps 2016-06-21 12:17:30 -07:00
f259a2204b feat: implement recognizer 2016-06-21 12:17:30 -07:00
4b1db0e61c feat: implement default url serializer 2016-06-21 12:17:30 -07:00
aee764d14d chore: update TS to enable non-nullable types 2016-06-21 12:17:30 -07:00
47585498af chore: updates typings to head 2016-06-21 12:17:30 -07:00
37c5320e33 feat: implement Tree 2016-06-21 12:17:30 -07:00
01111a1122 cleanup: removes a fake test 2016-06-21 12:17:30 -07:00
0b2bb1b6f5 chore(typings): use typings instead of tsd 2016-06-21 12:17:30 -07:00
f57df3cf8a chore: set up test and build infrastructure 2016-06-21 12:17:30 -07:00
c9c81e1fbc fix(XmbSerializer): add meaning attribute, escape attribute values 2016-06-21 11:52:11 -07:00
e38e04c1c2 refactor(MessageExtractor): pass the interpolationConfig around 2016-06-21 11:52:11 -07:00
99587ea4ed refactor(i18n): misc 2016-06-21 11:52:11 -07:00
58b18d7fe7 fix(partition): fix partition when <!-- i18n --> is the only child 2016-06-21 11:52:11 -07:00
04a50f5832 feat(MessageExtractor): do not expand ICU messages before extraction 2016-06-21 11:52:11 -07:00
e157a065b0 fix(compiler): codegen view query generic types 2016-06-21 11:51:54 -07:00
41ef4b3d4a chore(npm): add repository metadata 2016-06-21 11:21:04 -07:00
262650ab39 docs(test_injector): Fix documentation typo (#9403)
- Change 'teh' for 'the'
2016-06-21 11:19:08 -07:00
8c076d5a73 fix(upgrade): fix bundling issue and fix e2e test
the previous demo app was broken and is missing an e2e test.

I fixed the app, but was not able to get protractor to properly test
this app. Julie and I are looking into that. For now I manually verified
that the app works and that the original issue was fixed.

Closes #9244
2016-06-21 11:12:42 -07:00
c5c456120c refactor: delete containsRegexp() (there is escapeRegExp() in the lang facade)
BREAKING CHANGES:

`containsRegexp` is no more exported from `@angular/core/testing`. It should not have been part of the public API in the first place.
2016-06-21 09:15:21 -07:00
fdf6bc18dd fix(compiler): properly report unresolved dependencies
Fixes #9332

Closes #9341
2016-06-21 16:36:57 +02:00
297f0fd2c3 fix(core/testing): show full error
test(platform-browser): update fail capture

test(platform-browser-dynamic): update fail capture
2016-06-20 19:36:37 -07:00
86405345b7 doc: fix enableDebugTools import path (#9377) 2016-06-20 16:45:35 -07:00
12c49042ab fix(HTTP/XhrBackend): correctly set the status code on errors (#9355)
fixes #9329
fixes angular/http#54
2016-06-20 15:02:14 -07:00
ba46ca683b fix(animations): ensure starting styles are applied when a delay is present
Closes #9326
Closes #9328
2016-06-20 11:16:39 -07:00
ca42b49fa2 refactor: misc cleanup (#9369) 2016-06-20 10:55:29 -07:00
1b28cf71f5 feat(compiler): make interpolation symbols configurable (@Component config) (#9367)
closes #9158
2016-06-20 09:52:41 -07:00
6fd52dfb38 build(npm): update ts-api-guardian to v0.0.4 (#9366) 2016-06-20 09:32:27 -07:00
af2f5c3d7d cleanup(router): removes router 2016-06-20 08:47:54 -07:00
65be81baf8 doc(CheatSheet): update ngSwitch syntax in the cheatsheet (#9361) 2016-06-20 08:05:50 -07:00
7a3689f175 chore: formating 2016-06-19 22:27:29 +02:00
8675b8dc48 fix: cleanup public api of platform-server
BREAKING CHANGE: Parse5Adapter is no longer exported as public API, use serverBootstrap()

Parse5Adapter is an implementation detail not a public API

Closes #9237

Closes #9205
2016-06-19 09:03:01 -07:00
279e816ea7 chore: Remove unnecessary calls to Parse5DomAdapter
This reverts commit 80deac5cde.
2016-06-19 08:42:00 -07:00
2d60ff14ae bug(datePipe): date format pipe's 2-digit interpretation of minutes and seconds (#9338)
Closes #9333
2016-06-18 09:03:58 -07:00
dee1b774f2 docs(changelog): add note about forthcoming deprecation of Parse5DomAdapter (#9314) 2016-06-17 17:34:14 -07:00
c0f2a22a08 fix(perf): support prod mode again
After splitting the facades into multiple modules,
enabling prod mode for code had no effect for the compiler.

Also in a change between RC1 and RC2 we created the `CompilerConfig`
via a provider with `useValue` and not via a `useFactory`, which reads
the prod mode too early.

Closes #9318
Closes #8508
Closes #9318
2016-06-17 15:59:27 -07:00
5c8d3154d7 feat(datePipe): numeric string support 2016-06-17 15:58:06 -07:00
40f8a45b95 test(DatePipe): fixes 2016-06-17 15:48:26 -07:00
76a418760e fix(BrowserUtil): fix supportsIntlApi() 2016-06-17 15:34:12 -07:00
49bf3f5b3a fix(NumberPipe): fix broken RegExp
introduced in 7498050421 (#9308)
2016-06-17 15:33:25 -07:00
773c34900f fix(change_detection): ChangeDetectorRef reattach should restore original mode
After using ChangeDetectorRef detach, it should keep the ChangeDetector mode so that it is restored after calling reattach.

closes #7078
closes #7080
2016-06-17 15:00:41 -07:00
791153c93c fix(compiler): StaticReflector ignores unregistered decorators. (#9266)
Also modified static reflector to allow writing tests in using
the .ts and using the MetadataCollector.

Also made MetadataCollector be able to use SourceFiles that have
not been bound (that is, don't have the parent property set).

Fixes #9182
Fixes #9265
2016-06-17 13:11:00 -07:00
721f53f0d6 feat(I18N Expander): do not add extra <ul> & <li> around ICU messages (#9283)
fixes #9072
2016-06-17 11:38:24 -07:00
7498050421 refactor: misc (#9308) 2016-06-17 10:57:50 -07:00
5e3ccbcea9 refactor: add types (#9288) 2016-06-17 10:57:32 -07:00
8879aa1df4 feat(security): fail more detectably when using a safe value in an interpolation.
If a user ends up with a safe value in an interpolation context, that's probably
a bug. Returning `"SafeValue must use [property]= binding"` will make it easier
to detect and correct the situation. Detecting the situation and throwing an
error for it could cause performance issues, so we're not doing this at this
point (but might revisit later).

Part of #8511 and #9253.
2016-06-17 10:00:30 -07:00
44e0ad4987 refactor(forms): remove the facade local copy (#9276) 2016-06-17 08:53:17 -07:00
c449f325ba docs: remove and identify reverts in changelog 2016-06-17 08:45:40 -07:00
5fe60759f9 feat(QueryList): support index in callbacks
Closes #9278
2016-06-17 08:09:42 -07:00
6c389ed32f ci(local dev): fix test.sh 2016-06-16 15:46:08 -07:00
45549cda61 refactor(core): get rid of the bitwise operator facade helpers 2016-06-16 14:31:55 -07:00
a13052fc73 refactor(core): rename css_parser and css_lexer files
Closes #9273
2016-06-16 14:31:48 -07:00
4e7bb03e81 refactor(core): rename AST to Ast for all CSS parser code 2016-06-16 14:31:41 -07:00
935c39a7e2 feat(core): ensure CSS parser tracks start/end values and understands complex pseudo selectors 2016-06-16 14:31:34 -07:00
e0c1c13004 chore(.github): improve github issue and PR templates
the current templates accidentaly introduce tasks list which is confusing to both people
submitting the issue as well as triaging it.
2016-06-16 14:29:05 -07:00
c08ca22dba refactor(HtmlParser): clang format 2016-06-16 13:54:00 -07:00
a3f6e19881 docs: add missing breaking changes (#9267) 2016-06-16 13:49:29 -07:00
37b617dccf chore(tsickle): add @Annotation annotations
This lets users continue using runtime-sideeffect Decorators if they choose,
only down-leveling the marked ones to Annotations.

Also remove the "skipTemplateCodegen" option, which is no longer needed
since Angular compiles with tsc-wrapped rather than ngc. The former doesn't
include any codegen.
2016-06-16 12:29:46 -07:00
c60ef45bc8 fix(HtmlParser): add missing ; 2016-06-16 10:23:02 -07:00
2b1ac63e3a npm: bump the version number in package.json
this version string is currently not authoritative source see #9264 and #9233
2016-06-16 09:57:44 -07:00
6686bc62f6 feat(benchpress): add custom user metric to benchpress
This is a continuation of #7440 (@jeffbcross).

Closes #9229
2016-06-16 07:30:53 -07:00
1eaa193c51 feat(compiler-cli): add a debug option to control the output
fixes #9208
2016-06-15 18:13:57 -07:00
b620f4f456 feat(DomElementSchemaRegistry): add support for <ng-content> and <ng-container> 2016-06-15 18:13:57 -07:00
e484c62a8d fix(HtmlParser): do not add required parents to template root elements
fixes #5967
2016-06-15 18:13:57 -07:00
9ba400d7d5 fix(HtmlParser): consider <ng-container> when adding required parents 2016-06-15 18:13:57 -07:00
9cbd8f7afc test(ng-container): test nesting ng-containers 2016-06-15 18:13:57 -07:00
8e6e90e703 fix(forms): ngModel should export as ngModel 2016-06-15 17:46:45 -07:00
26676c4833 docs(webworker): add/correct experimental markers to web worker apis 2016-06-15 17:12:40 -07:00
39e0b4903c feat(radio): support radio button sharing a control 2016-06-15 15:27:34 -07:00
54c577cfe0 chore: update cheatsheet for new provider syntax (#9227) 2016-06-15 15:11:39 -07:00
27024915e4 docs: removed iframe from changelog (#9223) 2016-06-15 13:17:45 -07:00
80f3e7591e chore(docs): Update changelog for release (#9217) 2016-06-15 12:49:39 -07:00
933f45ef31 docs(webworkers): add experimental markers for all web worker public apis 2016-06-15 09:38:03 -07:00
4fc37aeabd fix(webworkers): rename all web worker apis with "render" to "ui"
"render" is gramatically incorrect and confusing to developers who used this api.

Since webworker apis were exposed only in master, renaming these before the rc2 release
is not a breaking change
2016-06-15 09:38:03 -07:00
2fd1e88199 fix(forms): suppress forms deprecation warning after first 2016-06-15 08:52:56 -07:00
17f317d31e fix: correct failing to push into builds repo on rerun 2016-06-14 21:28:37 -07:00
5941c92a31 fix: make ci fail when compiler integration test fails 2016-06-14 19:40:43 -07:00
d44d0852e5 Revert "fix: cleanup public api of platform-server"
This reverts commit ac84468f1c.
2016-06-14 19:40:43 -07:00
80deac5cde Revert "chore: Remove unnecessary calls to Parse5DomAdapter"
This reverts commit 387a90e546.
2016-06-14 19:40:43 -07:00
8a54c1a115 refactor(ViewBuilder): cleanup 2016-06-14 19:11:30 -07:00
0dbff55bc6 feat(Compiler): add support for <ng-container>
`<ng-container>` is a logical container that can be used to group nodes but is not rendered in the DOM tree as a node.

`<ng-container>` is rendered as an HTML comment.
2016-06-14 19:11:30 -07:00
22916bb5d1 feat(forms): add easy way to switch between forms modules (#9202) 2016-06-14 18:23:40 -07:00
fe01e2efb7 feat(I18nExtractor): Add file paths to error messages (#9177)
* feat(I18nExtractor): Add file paths to error messages

relates to #9071

* feat(i18n): allow i18n start comments without meaning

* refactor(i18n): cleanup

* test(HtmlParser): Add depth to expansion forms
2016-06-14 17:50:23 -07:00
7afee97d1b fix(platform-server): correctly import private DOMTestComponentRenderer 2016-06-14 17:26:55 -07:00
6fc267f22c fix: split dynamic bits in platform-browser into platform-browser-dynamic
Previously these symbols were exposed via platform-browser-dynamic, then we merged then into platform-browser
thinking that tools would know how to shake off the compiler and other dynamic bits not used with the offline
compilation flow. This turned out to be wrong as both webpack and rollup don't have good enough tree-shaking
capabilities to do this today. We think that in the future we'll be able to merge these two entry points into
one, but we need to give tooling some time before we can do it. In the meantime the reintroduction of the -dynamic
package point allows us to separate the compiler dependencies from the rest of the framework.

This change undoes the previous breaking change that removed the platform-browser-dynamic package.
2016-06-14 15:31:24 -07:00
ac84468f1c fix: cleanup public api of platform-server
BREAKING CHANGE: Parse5Adapter is no longer exported as public API, use serverBootstrap()

Parse5Adapter is an implementation detail not a public API
2016-06-14 13:21:28 -07:00
387a90e546 chore: Remove unnecessary calls to Parse5DomAdapter 2016-06-14 13:07:11 -07:00
6eeb9495d8 chore: have test.sh take platform argument 2016-06-14 13:07:11 -07:00
566b4ef481 ci(snapshots): publish tsc-wrapped snapshots 2016-06-13 18:30:38 -07:00
a191e9697c feat(forms): support setting control name in ngModelOptions 2016-06-13 16:57:10 -07:00
5504ca1e38 feat(compiler): Added support for limited function calls in metadata. (#9125)
The collector now collects the body of functions that return an
expression as a symbolic 'function'. The static reflector supports
expanding these functions statically to allow provider macros.

Also added support for the array spread operator in both the
collector and the static reflector.
2016-06-13 15:56:51 -07:00
5c0cfdee48 fix(forms): separate ngModelGroup from formGroupName 2016-06-13 13:41:39 -07:00
bc888bf3a1 refactor(compiler): Change arguments of CompilerConfig to named arguments
BREAKIKNG CHANGE:
`CompilerConfig` used to take positional arguments and now takes named arguments.

Closes #9172
2016-06-13 13:14:07 -07:00
1745366530 refactor(compiler): make PLATFORM_PIPES / PLATFORM_DIRECTIVES an option on CompilerConfig
This aligns the configuration of platform pipes / directives with offline compilation.

BREAKING CHANGE:
- `PLATFORM_PIPES` and `PLATFORM_DIRECTIVES` now are fields on `CompilerConfig`. 
  Instead of providing a binding to these tokens, provide a binding for `CompilerConfig` instead.
2016-06-13 13:13:45 -07:00
1fb0db4aeb chore(docs): Fixed pre-commit command for clang-format 2016-06-13 12:12:44 -07:00
61960c51a3 feat(forms): compose validator fns automatically if arrays 2016-06-13 11:41:32 -07:00
14a3ade662 chore(typescript): Changed double asterisks in #9100 to single asterisks
As in #9151, these comments are being read as JSDoc comments. This commit is smaller and only touches a few files that are causing errors.
2016-06-13 10:53:31 -07:00
de97687422 docs(i18n): fix typo (#9165) 2016-06-13 10:00:57 -07:00
a0251305ea doc(CompilerCli): Add a bootstrap example for a compiled application 2016-06-13 08:59:16 -07:00
2b8d12ddf0 chore(forms): rename ngControl to formControlName 2016-06-12 13:17:36 -07:00
1f6fd3c8fc refactor: add types (#9148) 2016-06-11 21:23:37 -07:00
55860e1621 fix(animations): ensure AUTO styles are cleared at the end of the state-change animation
Closes #9014
Closes #9015
2016-06-11 00:08:41 -07:00
4d51158b1a fix(animations): ensure the web-animations driver converts style props to camel-case
The web animations API now requires that all styles are converted to
camel case. Chrome has already made this breaking change and hyphenated
styles are not functional anymore.

Closes #9111
Closes #9112
2016-06-10 22:54:10 -07:00
7d9c1e1225 chore(forms): rename ngFormModel to formGroup 2016-06-10 19:10:17 -07:00
d53edfec47 chore(forms): rename ngFormControl to formControl 2016-06-10 17:28:19 -07:00
a6e5ddc5af feat(ComponentResolver): Add a SystemJS resolver for compiled apps (#9145) 2016-06-10 16:31:34 -07:00
b866f32832 chore(forms): rename Control, ControlGroup, and ControlArray classes 2016-06-10 12:00:18 -07:00
97833d48c1 chore(templateOutlet): fix linting 2016-06-10 11:32:09 -07:00
9c0031f7a5 fix: broken build due to bad noImplicitAny merge 2016-06-10 10:35:36 -07:00
164a091c71 feat(NgTemplateOutlet): add context to NgTemplateOutlet
Closes #9042
2016-06-10 10:25:44 -07:00
4ed6cf7519 feat(forms): allow ngModel to register with parent form 2016-06-10 10:24:01 -07:00
5267115481 feat(I18N): generate error on unknown cases
fixes #9094
2016-06-10 08:45:59 -07:00
43148d8233 feat(HtmlLexer): add support for alphabetic cases 2016-06-10 08:45:59 -07:00
537e99b4ea fix(http): respect custom Content-Type header in XHRConnection (#9131)
Fix a bug due to which setting a custom Content-Type header in the
Request led to multiple Content-Types being set on the sent
XMLHttpRequest: first the detected content type based on the request
body, followed by the custom set content type.

Fix #9130.
2016-06-10 08:18:49 -07:00
e1fcab777c fix(ngSwitch): use switchCase instead of switchWhen (#9076) 2016-06-09 22:52:30 -07:00
f39c9c9e75 style(lint): re-format modules/@angular 2016-06-09 17:00:15 -07:00
bbed364e7b chore(tsc-wrapped): update to newest tsickle 2016-06-09 16:45:16 -07:00
3aca5ff9e2 fix(compiler): properly report missing DI tokens (#9065)
Fixes #8245
2016-06-09 16:07:06 -07:00
9146bb0816 docs(DatePipe): Update date doc reference in date_pipe.ts (#9081) 2016-06-09 16:05:13 -07:00
0c9e8dbf60 docs(linker): document the injector argument as mandatory (#9098)
Fixes #8600
2016-06-09 16:03:42 -07:00
b34a04d53a chore(build): activate optional jobs in SL and BS (#8605) 2016-06-09 15:21:17 -07:00
0658eb4429 fix(compiler): Added unit test to ReflectorHost and fixed issues (#9052)
Refactored ReflectorHost to allow it to be tested.
Fixed an issue where the .d.ts was findable but it wasn't used by the project
  (This happens when the .metadata.json file references a module that was not
   needed, such as it doesn't declare any types, and the reference to it was
   elided by TypeScript when writing the .d.ts file).
Added tests for ReflectorHost
2016-06-09 14:51:53 -07:00
e178ee4ba0 fix(compiler): Added support for '* as m' style imports. (#9077)
Also includes fixes for broken test.
2016-06-09 13:23:29 -07:00
9f506cd330 chore(lint): remove unused lint checks
Now that we have --noImplicitAny we don't need these checks for explicit types in specific locations.

Also re-enable the check to disallow keywords as variable names.
2016-06-09 11:34:53 -07:00
729dc3b764 fix(security): support XSSI prefixes with and without commas.
Some implementations use an XSSI prefix with a trailing comma, some without.
This changes Angular to support both.
2016-06-09 11:32:07 -07:00
7ce0fc7d47 refactor: add types (#9116) 2016-06-09 11:04:15 -07:00
b60eecfc47 fix(build): update API spec to include the return value. 2016-06-09 10:11:02 -07:00
346304762e feat(security): document <iframe src> to be TRUSTED_URL.
Docs on the DomSanitizationService didn't match actual usage before.

Also fixes some minor docs and implementation issues.
2016-06-08 20:49:15 -07:00
e213939f28 chore(forms): fix implicit any 2016-06-08 17:08:59 -07:00
4c39eace52 feat(forms): add new forms folder 2016-06-08 16:41:08 -07:00
86fbd50c3d refactor(TypeScript): Add noImplicitAny
We automatically insert explicit 'any's where needed. These need to be
addressed as in #9100.

Fixes #4924
2016-06-08 16:20:50 -07:00
87d824e1b4 fix: add typescript test for our typings (#9096)
* Revert "fix(d.ts): enable angular2 compilation with TS flag --strictNullChecks (#8902)"

This reverts commit 7e352a27f7.

* test: add typescript test for our typings
2016-06-08 16:06:23 -07:00
50acb96130 fix(forms): update value and validity when controls are added
Closes #8826
2016-06-08 14:06:20 -07:00
29c2dcff61 fix(http): remove peerDep on @angular/common
it is not needed there because it will get transitively installed by @angular/platform-browser

we only need to declare this dependency in tsconfig.json because tsconfig.json's
do not support transitive dependencies in this way.
2016-06-08 12:20:42 -07:00
cea103a7ff test: add tree-shaking test
currently this doesn't throw or break the build, first we need to resolve all
of the existing issues.

to run execute: ./tools/tree-shaking-test/test.sh

then inspect dist/tree-shaking/test/**/*.bundle.js
2016-06-08 12:20:42 -07:00
d18694a1c3 Revert "WIP: test: add tree-shaking test (#8979)"
This reverts commit b746c64229.

Reason: bad merge via github ui
2016-06-08 12:20:17 -07:00
b746c64229 WIP: test: add tree-shaking test (#8979)
* test: add tree-shaking test

currently this doesn't throw or break the build, first we need to resolve all
of the existing issues.

to run execute: ./tools/tree-shaking-test/test.sh

then inspect dist/tree-shaking/test/**/*.bundle.js

* fix(http): remove peerDep on @angular/common

it is not needed there because it will get transitively installed by @angular/platform-browser

we only need to declare this dependency in tsconfig.json because tsconfig.json's
do not support transitive dependencies in this way.
2016-06-08 12:15:09 -07:00
d38aa5e25f chore(lint): sort imports in tools/ 2016-06-08 11:29:37 -07:00
efdc2d5118 chore(lint): upgrade clang-format 2016-06-08 11:29:37 -07:00
515a8e0765 fix(forms): rename old forms folder to forms-deprecated 2016-06-08 11:21:58 -07:00
45de65bd45 fix(router): don't mark the RouterOutletMap as internal
it's currently being reexported as public api which breaks anyone tryin to import
the router because the RouterOutletMap definition is missing
2016-06-08 11:18:35 -07:00
9d6b98794e chore: fix build break by using tsickle@0.1.2 2016-06-08 09:34:53 -07:00
7aa1790874 fix(travis): pin the version of tsickle for offline_compiler_test 2016-06-07 17:16:26 -07:00
5cd490eba2 test(public api): sort symbols case insensitive 2016-06-07 15:17:02 -07:00
ac1156739d feat(i18n): extract messages 2016-06-07 15:17:02 -07:00
7cefec77ef fix(ngUpgrade): prevent digest already in progress (#9054)
fix(ngUpgrade): prevent digest already in progress
2016-06-07 15:10:13 -07:00
36d25f2a07 feat(animations): support styling of the default animation state
It is now possible to set a fallback state that will apply its
styling when the destination state is not detected.

```ts
state("*", style({ ... }))
```

Closes #9013
2016-06-07 12:59:33 -07:00
c3d2459a4e fix(query): set fixed @ViewChild / @ContentChild right after the view is created
This is needed to have a true replacement of the previous
`DynamicComponentLoader.loadNextToLocation`, so that components
can be loaded into the view before change detection runs.

Closes #9040
2016-06-07 12:40:35 -07:00
8847580fd7 Revert "fix(compiler): add ability to parse : in * directives"
This reverts commit 53628e19ac.
as it breaks pipe arguments in `*ngFor`, ...

See #9062
Closes #9063
2016-06-07 12:39:17 -07:00
cf3548a02f fix(compiler): Improved error reporting of the static reflector.
StaticReflector provides more context on errors reported by the
collector.

The metadata collector now records the line and character of the node that
caused it to report the error.

Includes other minor fixes to error reporting and a wording change.

Fixes #8978
Closes #9011
2016-06-07 08:38:32 -07:00
c197e2bb42 Revert "fix(ngUpgrade): prevent digest already in progress (#9046)"
This reverts commit d1c989b8a5.

Breaks a Karma test.
2016-06-06 16:30:11 -07:00
d1c989b8a5 fix(ngUpgrade): prevent digest already in progress (#9046) 2016-06-06 13:34:27 -07:00
57c9a07fff chore: fix public api spec for beforeEachProviders
Closes #9043
2016-06-06 09:25:52 -07:00
a19c4e8f9a fix(upgrade): allow functions for template and templateUrl (#9022) 2016-06-04 19:53:51 -07:00
53083c0b52 refactor(testing): type beforeEachProviders (#9023)
these are valid otherwise
```typescript
beforeEachProviders(1)
beforeEachProviders('wat')
beforeEachProviders([
  Http
])
```
2016-06-04 19:52:51 -07:00
994d9212c1 docs(NgControlName): correct exports name (#9021) 2016-06-04 19:48:50 -07:00
52ddc96c9f refactor(compiler): remove obsolete @View-related code (#9019) 2016-06-04 19:46:55 -07:00
057abefe50 fix(compiler): report errors for queries without selectors (#9018)
Fixes #4489
2016-06-04 19:46:03 -07:00
f0e24b1a1e chore(karma): remove ref to legacy files (#9008) 2016-06-04 14:44:59 -07:00
a1e3004e62 docs(animations): provide API docs for the animation DSL
Closes #8970
2016-06-03 18:57:17 -07:00
e504d4eb05 fix(renderer): remove unecessary setElementStyles method
There is no need to expose this additional method inside of the Renderer
API. The functionality can be restored by looping and calling
`setElementStyle` instead.

Note that this change is changing code that was was introduced after
the last release therefore this fix is not a breaking change.

Closes #9000
Closes #9009
2016-06-03 15:20:34 -07:00
a6ad61d83e refactor: change provide(...) for {provide: ...}
- provide() is deprecated,
- {} syntax is required by the offline compiler
2016-06-03 15:03:49 -07:00
27a47e7841 refactor(imports): simplify paths 2016-06-03 14:46:04 -07:00
b00b9fe564 chore(README): update missing step in compiler-cli docs README 2016-06-03 14:36:16 -07:00
fa0718ba9a feat(animations): provide support for offline compilation 2016-06-03 14:36:11 -07:00
155b88213c feat(debug): collect styles and classes for the DebugElement 2016-06-03 14:36:06 -07:00
35ea02fb81 fix(compiler): Reflector generates imports for '..' relative modules.
Fixes #9003
Closes #9004
2016-06-03 12:54:30 -07:00
ddd2ac4f55 fix(core): fix type of DebugNode.properties (#8964)
Properties can have any value, not just strings.
2016-06-03 10:51:13 -07:00
6f281ab3c4 fix(HTMLParser): properly report errors for not properly closed tags (#8999)
Fixes #7849
2016-06-03 10:49:17 -07:00
fe8a7b0e82 fix(test-runner): make karma internal reporter compatible with 0.13.20 (#8977)
causes internal reporter to produce output messages again after upgrade to 0.13.20
2016-06-03 10:48:55 -07:00
76e6214b9b chore: remove angular_entry point (#8975)
this is obsolete and no longer needed. it wasn't part of the public api so it's ok to remove.
2016-06-03 10:47:55 -07:00
2d8f776e38 feat(build): Added a version stamp in .metadata.json files.
Also modified StaticReflector to handle multiple versions in a
single .metadata.json file.

Fixes #8974
Closes #8981
2016-06-03 10:40:49 -07:00
cf2d3cf920 style(pipes): cleanup unused imports
Remove unused imports from the pipes package. No impact on the code.
2016-06-03 09:58:57 -07:00
b160ada5d1 fix: QueryList documentation (#8976) 2016-06-02 16:24:26 -07:00
1090601e8b refactor(compiler): rename /compiler_cli to /compiler-cli 2016-06-02 13:50:32 -07:00
01dd7dde24 chore(tools): Remove use of TypeChecker from metadata collector.
The metadata collector was modified to look up references in the
import list instead of resolving the symbol using the TypeChecker
making the use of the TypeChecker vestigial. This change removes
all uses of the TypeChecker.

Modified the schema to be able to record global and local (non-module
specific references).

Added error messages to the schema and errors are recorded in
the metadata file allowing the static reflector to throw errors
if an unsupported construct is referenced by metadata.

Closes #8966
Fixes #8893
Fixes #8894
2016-06-02 13:39:15 -07:00
13c39a52c6 chore(compiler): Expose types needed by the language service
The language service needs access to the parser error ranges and
the static reflector.

Closes #8838
2016-06-02 13:36:22 -07:00
3b80ab51ba feat(SchemaRegistry): add Node.textContent
fixes #8413
2016-06-02 13:33:57 -07:00
1a386a58c8 Revert "feat(change_detection): make INTERPOLATE_REGEXP customizable (#7417)"
This reverts commit c3fafa0651.

The symbols should be configured at the component level and not be global to the compiler.
2016-06-01 17:53:45 -07:00
04220be8fd chore: remove old tests (#8954)
These tests are already captured in template_parser_spec.ts
2016-06-01 16:30:51 -07:00
48bf349c3c fix(upgrade): allow deeper nesting of ng2 components/directives (#8949)
* fix(upgrade): add test for upgrade adapter bug

* fix(upgrade): allow deeper nesting of ng2 components/directives

allow a nesting sequence of ng2 > ng1 > ng2 directives
2016-06-01 15:58:40 -07:00
21fc1bb655 fix(core): Keep core exports seperate from core/testing exports. (#8930) 2016-06-01 15:37:23 -07:00
d38d375fa6 test: enforce sorting of the public_api_spec
Closes #8950
2016-06-01 15:06:52 -07:00
602836800b feat(ChangeDetectorRef): make detectChanges() correct
Closes #8599
2016-06-01 12:50:47 -07:00
2953ea10a7 chore: fix public_api_spec after rebase 2016-06-01 10:57:45 -07:00
a738d0d54d chore: remove old public api tests
the tests under tools/public_api_guard have better coverage - we no longer need
to maintain two copies.
2016-06-01 10:43:57 -07:00
d781e69948 style(public_api_spec): reformat 2016-06-01 10:43:22 -07:00
c9b71fb5e2 test: add lots of missing stuff to the public_api_spec
- many entry points were previously missing (e.g. all testing entry points, http, etc)
- upgrade ts-api-guardian to 0.0.3 that adds support for more api surface
- add all info to the spec that was surfaced by ts-api-guardian@0.0.3
2016-06-01 10:43:22 -07:00
dd6cb233b5 build: add missing testing.ts entry points to tsconfigs 2016-06-01 10:43:22 -07:00
a3cf58b67a docs: add info about __moduleName into to DirectiveMetadata docs 2016-06-01 10:43:22 -07:00
70d944a59c refactor: correct api modifier flags 2016-06-01 10:43:22 -07:00
a5a422f8e7 refactor(NumberPipe): remove NumberPipe and replace it with private helper function
NumberPipe was just an implementation detail that we were accidentaly exposing as a public api.
2016-06-01 10:43:22 -07:00
e93b3d2360 fix(Location): make Location#platformStrategy:LocationStrategy property private
BREAKING CHANGE: Location#platformStrategy property was previously accidentaly exported as public

If any application requires access to the current location strategy, it should be accessed via DI instead
by injecting the LocationStrategy token.

The likelyhood of anyone actually depending on this property is very low.
2016-06-01 10:43:22 -07:00
7bc2d9a93a docs: add api stability indicators for @angular/common 2016-06-01 10:43:22 -07:00
1c929031a2 feat(router): export RouterLink and RouterOutlet (#8912)
Makes it possible to individually import RouterLink and RouterOutlet from the router module
2016-06-01 09:48:55 -07:00
f2809d1ed8 fix(PostMessageBus):Add the worker scope to please Closure 2016-06-01 09:24:05 -07:00
f4f6b8721a fix(core): Keep core exports seperate from core/testing exports. 2016-05-31 20:09:43 -07:00
7e352a27f7 fix(d.ts): enable angular2 compilation with TS flag --strictNullChecks (#8902)
Eliminate the following compiler error when using TS 1.9+ with strict null checking enabled:

node_modules/@angular/core/src/util/decorators.d.ts(9,5): error TS2411: Property 'extends' of type 'Type | undefined' is not assignable to string index type 'Type | Function | any[]'.

https://github.com/angular/angular/issues/8720
2016-05-31 18:25:48 -07:00
Tom
0c6b16c208 docs(async-pipe): include observable example (#8900)
Adds the missing example, usage case and a bit more description to the async-pipe.
2016-05-31 18:23:29 -07:00
10475b859d chore: remove deep imports to fix build. 2016-05-31 17:13:41 -07:00
4d793c4eb8 feat(security): Automatic XSRF handling.
Automatically recognize XSRF protection cookies, and set a corresponding XSRF
header. Allows applications to configure the cookie names, or if needed,
completely override the XSRF request configuration by binding their own
XSRFHandler implementation.

Part of #8511.
2016-05-31 16:12:33 -07:00
3ae29c08ac chore(build.sh): make tsc-wrapped installable 2016-05-31 10:43:21 -07:00
3331321f64 build(ngc): run integration test hermetically
This ensures we run in a clean directory, using our real distribution. It finds bugs like @internal
APIs needed to type-check in the offline compiler, as well as problems in package.json.

Also move tsc-wrapped under tools/@angular
2016-05-27 17:21:35 -07:00
c6064a30a1 chore(package.json): make the packages installable 2016-05-27 17:21:34 -07:00
040b101842 feat(security): complete DOM security schema.
This addresses several oversights in assigning security contexts to DOM schema
elements found by our security reviewers (thanks!).

This also adds some more precise unit tests for the interaction between
(Dom)ElementSchemaRegistry and the TemplateParser, and extracts the security
specific parts into dom_security_schema.ts.

Comparison of (potentially) dangerous property names is done case insensitive,
to avoid issues like formAction vs formaction.

Part of issue #8511.
2016-05-27 11:47:33 -07:00
a78a43c816 chore(readme): add CircleCI status badge 2016-05-27 11:40:30 -07:00
ec198b0dc6 docs(MockConnection) add mockError usage example (#8888) 2016-05-27 10:10:53 -07:00
b5d14c26d2 chore(lint): enable requireInternalWithUnderscore tslint check 2016-05-27 09:31:18 -07:00
2019050db2 chore(lint): enable duplicateModuleImport tslint check 2016-05-27 09:17:08 -07:00
5f999225ba chore(lint): upgrade tslint and its gulp plugin 2016-05-27 09:17:08 -07:00
307d105d2c fix(DomRegistry): fix svg support 2016-05-26 16:46:00 -07:00
0b6967fd74 chore(lint): fix missing semicolon to make Circle green (#8877) 2016-05-26 16:43:15 -07:00
ef0c32512c chore(lint): enable semicolon and variable-name tslint checks 2016-05-26 15:46:03 -07:00
9096481744 chore(lint): format tools dir 2016-05-26 15:46:03 -07:00
5936624d11 chore(lint): re-enable clang-format on tools/ 2016-05-26 15:46:03 -07:00
83723671af chore(lint): upgrade clang-format and gulp plugin 2016-05-26 15:46:03 -07:00
4d825dd9fd refactor(intl facade): remove outdated code 2016-05-26 14:19:26 -07:00
8e38291156 chore: enable the compiler_cli tests again.
Closes #8864
2016-05-26 13:33:26 -07:00
f93512bf27 feat(ViewEncapsulation): default ViewEncapsulation to configurable
BREAKING CHANGES:

DirectiveNormalizer takes new constructor arguments, `config:CompilerConfig`.

Closes #7883
2016-05-26 13:23:37 -07:00
c3fafa0651 feat(change_detection): make INTERPOLATE_REGEXP customizable (#7417)
BREAKING CHANGES:

`Parser` constructor required new parameter `config: CompilerConfig` as second argument.
2016-05-26 13:08:39 -07:00
9036f78b74 fix(compiler): throw an error if variable with the same name is already defined. (#7209)
* fix(compiler): throw an error if variable with the same name is already defined. Closes #6492

* fix(compiler): Clean up formatting for issue #6492

* fix(compiler): throw an error if reference with the same name is already defined.

Closes #6492
2016-05-26 13:04:17 -07:00
263122ea5f upgrade karma to v0.13.20 due to 'invalid characters in the headers on Node 5.6.0'(#1884) issue
Closes #7885
2016-05-26 12:12:35 -07:00
324f0147f6 feat(common/datePipe): change date formatter to use correct pattern closes #7008 (#8154)
- add regular expression to parse date parts
- add date part creator function
- replace tokens in pattern to parsed parts
2016-05-26 12:06:29 -07:00
Jon
be48ba1b06 fix(ngRouteShim): update anchors to function similar to angular 1.x (#8478)
* fix(ngRouteShim): update anchors to function similar to angular 1.x when setting the target

* fix(ngRouteShim): update anchor target check to use indexOf instead of contains on array
2016-05-26 12:03:23 -07:00
b2a7fd05cb fix(router): replace state when path is equal to current path (#8766)
Same as 2bf21e1747 but for new router.

This also fixes an issue where when application loads it clears forward history
because Router constructor calls navigateByUrl which was causing a push state to happen.
2016-05-26 12:02:24 -07:00
84f859d7b2 fix(Control): Support <select multiple> with Control class (#8069) 2016-05-26 12:01:49 -07:00
cbc8d0adf8 fix(upgrade): Ensure upgrade adapter works on angular.js 1.2 (#8647) 2016-05-26 11:58:47 -07:00
420e83a396 feat(forms): add the submitted flag to NgForm and NgFormModel directives
Closes #2960

Closes #7449
2016-05-26 10:48:13 -07:00
89f61087c7 feat(http): implement Response.prototype.toString() to make for a nicer error message
Added a toString method to the Response class displaying status / status and
the URL of the request.

Closes: https://github.com/angular/http/issues/89

Closes #7511
2016-05-26 10:38:29 -07:00
1dcb663917 readme: update badges
Closes #8853
2016-05-26 10:35:22 -07:00
b7b56785d1 fix(Renderer): update signatures to make RenderDebugInfo optional
The code does not force the user to provider `RenderDebugInfo`. The
current implementation lists this as a mandatory parameter. Update
the parameter to be optional.

Fixes #8466

Closes #8859
2016-05-26 10:32:03 -07:00
3a62023260 docs(LifecycleHooks): correct ngDoCheck description (#8807)
The current behavior is for ngDoCheck to supplement, not override, the default change detector. OnChanges will still be called when DoCheck is implemented (fixes #7307).
2016-05-26 10:27:42 -07:00
fa2ce8100b fix(router): openning links in new tab
Clicks on router-link should not prevent browser default action when
any mouse button other than left mouse button or ctrl/meta key is pressed.

router-link href should use location strategy external url.

Closes #5908
Closes #6806
Closes #7749
Closes #8806

Closes #8821
2016-05-26 10:26:57 -07:00
172a5663ef feat(platform-browser-dynamic): re-add a deprecated platform-browser-dynamic 2016-05-26 10:22:39 -07:00
f4b972815b fix(platform-browser): fix rollup config 2016-05-26 10:21:24 -07:00
0cb93a436d fix(testing): add discardPeriodicTasks to be used with fakeAsync (#8629)
Closes #8616
2016-05-26 10:19:30 -07:00
b2e804c961 fix(metadata): Allow spacing in multiple selectors (#7418) 2016-05-26 10:18:31 -07:00
85ce184197 refactor(compiler): remove obsolete code (#8837) 2016-05-26 10:15:47 -07:00
29c77df4be fix(playground): fix WebWorker single_thread example 2016-05-26 09:58:40 -07:00
c39e0463a3 removes tailing slashes from url in lines 229 231
Closes #8718
2016-05-26 09:58:14 -07:00
c27bc1956b docs(API): correct formatting of EmbeddedViewRef API doc example (#8584)
remove ... from EmbeddedViewRef API example doc comments
2016-05-26 09:53:18 -07:00
72707d80ab refactor(api): Correct public api variable names (#8552)
Correct public api variable names (ROUTER_DEPRETACED_TESTING -> ROUTER_DEPRECATED_TESTING)
2016-05-26 09:52:56 -07:00
f18356307b chore: consistent badge shape (#7053) 2016-05-26 09:52:06 -07:00
00475f25c8 fix(doc): Add missing comma in example (#8769) 2016-05-26 09:50:59 -07:00
bab6023eee fix(router): Added pushState fallback for IE 9 browser.
Closes #6506

Closes #7929
2016-05-26 09:49:07 -07:00
5e12a95789 test(security): test case for quoted URL values.
Test case that fixes #8701. This is already supported with the latest sanitizer
changes, but it's good to have an explicit test case.
2016-05-26 09:39:23 -07:00
e5904f4089 fix(facade): change EventEmitter to be sync by default (#8761) 2016-05-26 09:34:04 -07:00
cf1122cf9e chore(contributing): fix link to styleguide (#8781)
- Change to new link to Google's JavaScript style guide
2016-05-26 09:30:31 -07:00
6da79673c2 test(e2e): workaround flaky e2e tests 2016-05-25 17:52:30 -07:00
352ee53202 Revert "feat(AsyncPipe): allow onError argument"
This reverts commit 390046d7b3.
CI fails for IE on win8.
PR #7990
2016-05-25 17:23:20 -07:00
a20639558b npm: invoke webdriver-manager update after install 2016-05-25 16:51:30 -07:00
b9347eb01c build: remove dependency on tsd and use @types/* instead 2016-05-25 16:42:28 -07:00
4dbd8ed6b8 refactor: remove unnecessary annotations 2016-05-25 16:42:28 -07:00
cb980d3e43 fix(ci): incorrect import 2016-05-25 16:22:55 -07:00
f154e2c6cf fix(ci): extra API in public_api_spec 2016-05-25 16:14:03 -07:00
d0a64f9c86 fix: broken build 2016-05-25 16:03:11 -07:00
16ef21d086 fix(ngc): depend on correct tsc-wrapped package 2016-05-25 15:34:43 -07:00
39ecd01b86 chore: audit @angular/core API classification (#8808) 2016-05-25 15:00:05 -07:00
5e0f8cf3f0 feat(core): introduce support for animations
Closes #8734
2016-05-25 13:56:50 -07:00
6c6b316bd9 chore(ngc): fix release instructions 2016-05-25 13:25:26 -07:00
b49dac7be5 chore(git): update .gitignore 2016-05-25 10:03:42 -07:00
4c26397937 chore(ngc): refactor out tsc-wrapped
This allows angular's build to depend on some extensions, but not on code generation, and breaks a cycle in the angular build
We now merge ts-metadata-collector into tsc-wrapped and stop publishing the former.
2016-05-25 09:45:55 -07:00
e26e4f922e build(npm): trigger tsd install from npm postinstall 2016-05-25 08:24:59 -07:00
2ab1085dfb fix(core): remove @internal annotation from PLATFORM_CORE_PROVIDERS
This symbol is no longer reexported at the top level, so it's safe to not mark it as internal.

This fixes the offline compilation which got broken by this symbol not being present in the d.ts
files when the compiler tries to do a deep import.

Closes #8819
2016-05-25 08:22:07 -07:00
acc3a2de83 build: add comment for the tools watch option 2016-05-25 08:22:07 -07:00
12abdd8782 ci: disable the lint job since it's currently not doing anything
the tslint rules have not been executed on the right files when this the lint was reenabled
after package splitup making the linting useless. When reenabled lots of errors show up.
I'm disabling this for now until we come back and fix up the code issues.
2016-05-25 08:22:07 -07:00
2bcdec5aaf build: use connect web server instead of SimpleHTTPServer
Several flakes on CI have been tied to long running SimpleHTTPServer that was
put in place temporarily after the package splitup.
2016-05-25 08:22:07 -07:00
16dfe3c63f build: consolidate tsc to ease migration to @types/ based typings delivery
I actually tried to use @types/* directly but came across several issues which prevented me
from switching over:
- https://github.com/Microsoft/TypeScript/issues/8715
- https://github.com/Microsoft/TypeScript/issues/8723
2016-05-25 08:22:07 -07:00
b4a467e387 ci: disable debug output from env.sh 2016-05-25 08:22:07 -07:00
53628e19ac fix(compiler): add ability to parse : in * directives
- Add ability to parse bindings properly when `:` is present when using a directive with the `*` prefix

Closes #6038
2016-05-24 21:43:10 -07:00
62dd3ceb64 Revert "Don't update the location during initial router navigation"
This reverts commit d5066a9a0f.
2016-05-24 21:43:10 -07:00
d5066a9a0f Don't update the location during initial router navigation
Closes #6069
2016-05-24 21:34:59 -07:00
32b37432b0 docs: Remove text about Dart Editor bundle; clean up text
* Remove text about Dart Editor bundle.
* Fix links.
* Fix some grammar and Markdown formatting.

Closes #5928
2016-05-24 21:34:18 -07:00
5f3d02bc7c fix(Animation): Problem decimals using commas as decimal separation
Tests where failing due to `.` character being used as decimal separator in some regional settings (like spanish for example)

Closes #6335

Closes #6338
2016-05-24 21:23:46 -07:00
97e94dd6e0 docs(DEVELOPER): JDK is needed for protractor to run selenium
Closes #6343
2016-05-24 21:23:18 -07:00
846c031ec9 changelog: Update name of a lifecycle hook
Closes #6418
2016-05-24 21:22:57 -07:00
92340350d2 fix(http): Set response.ok
The ok property was not being set on response objects.
It's now set based on the status code.

Closes #6390
Closes #6503
2016-05-24 21:20:27 -07:00
d4827caa08 refactor(DomRootRenderer): allow registeredComponents access
Closes #6584
2016-05-24 21:17:11 -07:00
6ce13b68fa Typo in web_workers.md
Closes #6694
2016-05-24 21:16:11 -07:00
e82b700ad0 docs(directives): add a deprecation comment for properties, events
Closes #7059
2016-05-24 21:13:56 -07:00
60a2ba87d4 fix(#7099): support for comment finishing with multiple dashes
<!-- xxxx ------->

The issue came from a lack of support for backtracking on string
matching.
The way it is done, if the "end pattern" for consumeRawText starts with
twice the same character, you end up having problem when your string
being parsed has 3 times this char

Example
End string: xxyz
string to parse: aaaaaaxxxyz

calling consumeRawText(false, 'x', attemptParseStr('xyz')) would fail

Closes #7119
2016-05-24 21:01:41 -07:00
83c19a1fbc fix(pipes): handle undefined value in slice
Closes #7152
2016-05-24 20:58:14 -07:00
1513e201bb docs(ContentChild): add example with refs
Add missing example on how ContentChild works with refs
Closes #7160
Closes #7162
2016-05-24 20:57:25 -07:00
a38c9a1ef7 fix(Router) Fix detect active route in depth. 2016-05-24 20:51:51 -07:00
d5f5ce82ca Revert "fix(compiler): support string tokens with . inside."
This reverts commit 67c80fbb5e.
2016-05-24 20:51:51 -07:00
6dc88f5b61 fix(forms): radio buttons with different names should not share state
Closes #7051
2016-05-24 20:07:57 -07:00
7a2ce7ff21 fix(forms): update accessor value when native select value changes
Closes #8710
2016-05-24 20:03:49 -07:00
1ac38bd69a feat(renderer): add a setElementStyles method 2016-05-24 18:42:05 -07:00
982fad0c45 test(public API): Update the public API to the new world 2016-05-24 14:00:00 -07:00
390cefac72 refactor(testing/server): optimize imports 2016-05-24 14:00:00 -07:00
798bface7f fix(typings): remove rxjs workaround
Closes #7198
2016-05-24 12:45:46 -07:00
276fec6e50 build(serve): added cors support for the js.server task
Added and used the cors middleware:
- add the module as a dev depedency in the package.json file
- require the module in the jsserve.js file
- add the module in the middleware list

Closes #7273

Closes #7274
2016-05-24 12:00:40 -07:00
95af14b97c feat(http): added withCredentials support
Taken into account the withCredentials property within the request options:
- added corresponding property in the RequestOptions class
- added corresponding property in the Request class
- handle this property when merging options
- set the withCredentials property on the XHR object when specified

Added a test in the xhr_backend_spec.ts to check that the property is actually
set on the XHR object

Closes https://github.com/angular/http/issues/65

Closes #7281

Closes #7281
2016-05-24 11:53:43 -07:00
0f0a8ade7c feat(http): automatically set request Content-Type header based on body type
Implement the ability to provide objects as request body. The following use cases
are supported:
* raw objects: a JSON payload is created and the content type set to `application/json`
* text: the text is used as it is and no content type header is automatically added
* URLSearchParams: a form payload is created and the content type set to `application/x-www-form-urlencoded`
* FormData: the object is used as it is and no content type header is automatically added
* Blob: the object is used as it is and the content type set with the value of its `type` property if any
* ArrayBuffer: the object is used as it is and no content type header is automatically added

Closes https://github.com/angular/http/issues/69

Closes #7310
2016-05-24 11:42:37 -07:00
e0c83f669e fix(build): force a compatible baseURL for systemjs-builder
Closes #7167

Closes #7360
2016-05-24 11:16:33 -07:00
016f0d8e9e fix: Typo in async.ts
Closes #7382
2016-05-24 11:15:51 -07:00
3e5716ec16 feat(i18n): support implicit tags/attributes 2016-05-24 10:12:19 -07:00
75e6dfb9ab fix(browser): platform code cleanup 2016-05-23 17:57:28 -07:00
9634e8d14a fix(tests): Execute the security specs only once 2016-05-23 17:57:28 -07:00
f95a604b59 fix(bootstrap): swap coreBootstrap() and coreLoadAndBootstrap() arguments 2016-05-23 17:57:28 -07:00
3ff20cd7e3 fix(core): fix build 2016-05-23 17:56:48 -07:00
17f73cb7bc docs(http): fix MockBackend example
imports & syntax error fix

Closes #7383
2016-05-23 17:22:36 -07:00
fba0e2ff12 docs(browser.ts): update bootstrap injector override argument name
Closes #7387
2016-05-23 17:21:07 -07:00
b62415c962 refactor(chore): remove unused mapToObject and objectToMap methods from serializer
resolves #7402

Closes #7416
2016-05-23 17:20:25 -07:00
adc135e6c8 refactor(async_pipe): use subscription strategy interface
The strategies for Promise and Observable based subscriptions
have (nearly) the same method signatures. They should implement
a common interface.

Closes #7573
2016-05-23 17:02:56 -07:00
ceac045a7f fix(compiler): have CSS parser support nested parentheses inside functions
Closes #7580
2016-05-23 16:58:15 -07:00
4d6da7b1a7 chore: Update ISSUE_TEMPLATE.md
Closes #7591
2016-05-23 16:56:38 -07:00
01b9de7a15 Bump reflect-metadata to 0.1.3
Closes #7663
2016-05-23 16:40:24 -07:00
0a872ffd38 feat(core/linker): add SimpleChanges type to lifecycle_hooks to simplify OnChanges signature
Closes #8557
2016-05-23 16:11:52 -07:00
6f3a6a55a0 Update url_search_params.ts
Values need to be encoded on `toString()`
Closes #7686
2016-05-23 14:43:26 -07:00
0795dd307b refactor(chore): Replace all 'bindings' with 'providers'
BREAKING CHANGE

Deprecated `bindings:` and `viewBindings:` are replaced with
`providers:` and `viewProviders:`

Closes #7687
2016-05-23 13:31:08 -07:00
49fb7ef421 refactor(router): Remove deprecated 'as' from ‘RouteConfig’
BREAKING CHANGE
Remove deprecated 'as' from ‘RouteConfig’ in favour of ‘name’
2016-05-23 13:24:36 -07:00
cbeeff2bd6 fix(Router): do not kill event-emitter on navigation failure
Closes #7692
Closes #7532

Closes #7692
2016-05-23 13:21:09 -07:00
ce013a3dd9 feat(regex_url_paths): add regex_group_names to handle consistency with serializers
By adding `regex_named_groups` to regex route configurations we can consistently map
regex matching groups to component parameters.

This should fix #7554.

Closes #7694
2016-05-23 13:18:21 -07:00
1f7449ccf4 fix(changelog): add View decorator breaking change
Closes #7707
2016-05-23 13:14:25 -07:00
c43636f2bb fix(di): type error in InvalidProviderError
- when trying to instantiate an injector with an unknown provider
Closes #7729
2016-05-23 13:08:10 -07:00
9c2fe660a3 (docs) Fix pony greeting equivalence
Cheatsheet claims equivalence between two statements which are not equivalent
Closes #7761
2016-05-23 13:07:20 -07:00
abc266fa35 feat(common): DatePipe supports ISO string
Closes #7794
2016-05-23 12:30:02 -07:00
7d853dd9ad fix(router): ensuring MatchedUrl pass query params 2016-05-23 12:24:08 -07:00
3ac2821a1b Update CHANGELOG.md
remove duplicated content
Closes #7852
2016-05-23 10:27:38 -07:00
666dc75c15 chore(lint): disallow duplicate imports from a module
Closes #7859
2016-05-20 15:59:33 -07:00
e9332c66d2 chore(lint): re-enable linter and fix violations
fixes #7798
2016-05-20 15:49:18 -07:00
830aecd1a7 fix(router/parsing): change route rule error to say PascalCase instead of CamelCase
Closes #7874
2016-05-20 15:39:30 -07:00
ac6959c819 docs(DEVELOPER.md): add a clearer explanation about Windows symlinks issue. (#7886)
add a clearer explanation about Windows symlinks issue.
2016-05-20 15:34:59 -07:00
2bf21e1747 fix(Router): replace state when normalized path is equal to current normalized path
Make sure the same path is not added multiple times to the history.
It is replacing the state, instead of skipping it completely,
because the current path in the browser might not be normalized,
while the given one is normalized.

Closes #7829

Closes #7897
2016-05-20 15:30:15 -07:00
9105ab9596 docs(ng_control_group): update API docs syntax
Closes #7357
2016-05-20 13:19:08 -07:00
165357bfa3 refactor(forms): remove useless imports
Remove useless imports and change `bindings` to `providers` inside
`@Component` and `@Directive`.

Closes #7904
2016-05-20 13:13:27 -07:00
33c7f74cb9 style(platform/browser): fix type spacing
Closes #7980
2016-05-20 13:05:40 -07:00
cb84cbf545 feat(shadow_css): add encapsulation support for CSS @supports at-rule
Closes #7944
2016-05-20 13:02:24 -07:00
b2e0946696 fix(Request): Change Request.text's return type to string
Change the return typing for the .text method to `string` so typescript treats it
like a normal string.

Closes #8138
2016-05-20 12:57:31 -07:00
67c80fbb5e fix(compiler): support string tokens with . inside.
Closes #8178
2016-05-20 12:56:02 -07:00
c574e6808f build: update to rollup@0.26.3 and remove a hack from the build 2016-05-20 11:00:49 -07:00
9175a049d3 feat: add minified bundles
BREAKING CHANGE: bundles are now in the bundles/ subdirectory within each package
2016-05-20 11:00:49 -07:00
0035575c82 build: turn on tsc's stripInternal when producint public d.ts file
I also made some changes to fix imports and remove some stuff that caused
breakage once stripInternals was turned on.
2016-05-20 10:59:57 -07:00
7bfe8aa553 build: fix the build.sh script to work out of the box 2016-05-20 10:59:32 -07:00
1bff47f97d docs: fix syntax errors in HostBinding metadata example
add missing parentheses to getters, remove erroneous brackets in HostBindings

Closes #8269
2016-05-20 10:56:11 -07:00
e8e61de28d refactor(WebWorker): move XHR worker side 2016-05-20 10:48:55 -07:00
54f8308999 refactor(browser): merge static & dynamic platforms 2016-05-20 10:48:55 -07:00
6c99746f0b Update tools.ts
Closes #8296
2016-05-20 10:12:54 -07:00
4086b49046 feat(enableDebugTools): return ComponentRef
allows for 
```
bootstrap(App, [
  ...HTTP_PROVIDERS,
  ...ROUTER_PROVIDERS
])
.then(enableDebugTools)
```
without breaking the rule of always returning a value in a promise
2016-05-20 10:12:54 -07:00
0fad9c2786 docs(changelog): fix ViewQuery read usage example in beta.16 (#8330) 2016-05-20 10:06:46 -07:00
d75f928fca fix(docs): Fix a missing opening bracket (#8331) 2016-05-20 10:06:15 -07:00
68f9aaf214 chore: Update CHANGELOG.md (#8342) 2016-05-20 10:05:52 -07:00
efe4633b15 refactor(empty.ts): typo (#8398)
emppty -> empty
2016-05-20 10:02:47 -07:00
19e65382f7 fix(core): accurate dev mode message for dart (#8403) 2016-05-20 10:01:39 -07:00
5a9eda73b4 docs(changelog): line numbers to master can go out of sync (#8404) 2016-05-20 10:00:47 -07:00
c6f2b3e96b docs(router): fix import and replace RouteConfig (#8433) 2016-05-20 10:00:04 -07:00
55921be1af fix(router-deprecated): export RootRoute
Fix #8449

Closes #8450
2016-05-20 09:59:21 -07:00
9b1b5b393d fix(angular1_router): ngLink should not throw an error if routeParams are undefined (#8460)
If routeParams are undefined, no href attribute should be rendered. This matches the behaviour of ngHref.
2016-05-20 09:48:03 -07:00
3857c8226e docs(MockConnection) update mockRespond usage example (#8487)
Closes: #8486
2016-05-20 09:19:59 -07:00
595bcdd1ac fix(router): browser back and forward buttons not working correctly.
Closes #8524

Closes #8532
2016-05-19 22:42:08 -07:00
27c25bd0e8 chore(readme): update README: no longer beta (#8405) 2016-05-19 22:23:33 -07:00
aec95015f8 docs(http): update http docs to mention .map operator
Closes #5947
2016-05-19 22:15:26 -07:00
b2db6401cc chore(build): remove use of q.denodeify
This change also makes webpack properly reject
promise on build errors

Closes #6546
2016-05-19 22:01:52 -07:00
ebe14720eb chore(typing): enforce --noImplicitAny for tools directory.
Exposed a couple of bugs.

Closes #6645
2016-05-19 21:38:01 -07:00
7112d008d0 fix(IE): make shim work with instrumented code
Closes #6944
2016-05-19 20:50:01 -07:00
90af4763d8 fix(angular1_router): delay view update until activate promise gets fullfilled
If the $routerOnActivate hook returns a promise, the navigation
is commited, once the promise gets fullfilled but the view
is updated immediately.

This commit delays the view update so that both (view and url) are
updated at the same time.

Closes #7777
2016-05-19 20:42:06 -07:00
390046d7b3 feat(AsyncPipe): allow onError argument
Closes #7990
2016-05-19 16:49:28 -07:00
587c119c75 feat(NgZone): isStable
Closes #8108
2016-05-19 16:10:08 -07:00
3019140e7e feat(http): set the statusText property from the XMLHttpRequest instance
Closes #4162
2016-05-19 15:58:59 -07:00
7a80f0d1e1 change property and event to use camel case
Closes #6580
2016-05-19 15:21:54 -07:00
0894318f50 change property and event to use camel case:
* some-property -> someProperty
* some-event -> someEvent

Closes #6609
2016-05-19 15:20:34 -07:00
f9fc524a74 chore(contributing): added commit message samples (#7375)
Each time I am pointing someone to this contribution doc
to read and apply commit message style guide
each time type part of message formatting is missed
2016-05-19 15:19:01 -07:00
9019c6f937 Condition using component as well.
Closes #7755
2016-05-19 15:15:25 -07:00
166b73f4f3 Condition using urlPath, component is null on UnresolvedException of child AsyncRoute. 2016-05-19 15:15:25 -07:00
cb94111f18 style(Forms): remove unused imports
remove unused imports from the forms package
2016-05-19 14:50:33 -07:00
a88b887a05 docs(cheatsheet): Document SVG idiosyncrasies (#6055) 2016-05-19 10:20:08 -07:00
6e62217b78 fix(WebWorker): remove the platform-browser dependency on compiler 2016-05-18 16:23:09 -07:00
a01a54c180 adds 'repository' metadata to npm modules (#8649) 2016-05-17 23:17:15 -07:00
db8290632f fix(upgrade): fallback to root ng2 injector when element is compiled outside the document (#8684)
Currently downgraded ng2 elements fail inside a ui-router view because they are unable
to require an ng2 Injector via the require attribute of the DDO, because ui-router compiles
its templates before they are inserted in a ui-view. This adds a "fallback" behavior if
a parent injector cannot be found to go to the root ng2 Injector.
2016-05-17 15:55:53 -07:00
7bb5167239 chore(security): fix CHANGELOG formatting. (#8687)
Turns out the fenced code block needs to be in its own paragraph.
2016-05-17 15:00:28 -07:00
6cdc53c497 fix(UpgradeNg1ComponentAdapter): make bindings available on $scope in controller & link function (#8645)
Delays NG1 Directive controller instatiation where possible and pre-link function always
to the ngOnInit() lifecycle hook. This way bindings are always available on $scope in both
the controller and the link function.
2016-05-17 14:53:59 -07:00
15ae710d22 feat(security): allow url(...) style values.
Allows sanitized URLs for CSS properties. These can be abused for information
leakage, but only if the CSS rules are already set up to allow for it. That is,
an attacker cannot cause information leakage without controlling the style rules
present, or a very particular setup.

Fixes #8514.
2016-05-17 11:23:31 +02:00
dd50124254 feat(security): allow data: URLs for images and videos.
Allows known-to-be-safe media types in data URIs.

Part of #8511.
2016-05-17 10:57:14 +02:00
ff36b0384a fix(compiler_cli): normalize used directives
- e.g. needed for content projection.

Closes #8677
2016-05-16 13:07:13 -07:00
50c9bed630 feat(security): expose the safe value types.
This allows users to properly type their `SafeHtml`, `SafeStyle`, etc values.

Fixes #8568.
2016-05-15 11:47:06 +02:00
8b1b427195 feat(security): support transform CSS functions for sanitization.
Fixes part of #8514.
2016-05-14 13:25:45 +02:00
9a05ca95f6 fix(build): Release compiler_cli packages along with rest of @angular packages and use ANGULAR_VERSION for package version and peer dependencies. 2016-05-13 13:35:10 -07:00
05266241af build(npm): short-circuit npm install if node_modules are healthy
Closes #8627
2016-05-13 22:07:41 +02:00
4ddf5536b4 docs(DEVELOPER.md): state that git-clang-format must be in PATH
To use ```git clang-format``` your have to make sure that
```git-clang-format``` is in your path.

Closes #7778
2016-05-13 12:25:09 -07:00
f389b5a961 docs(changelog): add missing breaking changes for testing providers
Closes #8440
2016-05-13 12:21:33 -07:00
bac1a6eab3 fix(build): Fix an error in package publishing step where the script errors when a UMD bundle is not found for compiler-cli package. 2016-05-12 16:49:03 -07:00
ff400726ca fix(build): Declare the secure GITHUB_TOKEN_ANGULAR for package publishing from Travis 2016-05-12 15:08:28 -07:00
267d864976 fix(build): Fix broken e2e test Travis task by running the right variation of sed on Travis 2016-05-12 13:58:42 -07:00
97a1084c99 fix(build): Hook up publish-build-artifacts to Travis 2016-05-12 12:01:53 -07:00
61b339678d test(compiler): test schema generation only in Chrome
Closes #8581
2016-05-11 17:01:26 -07:00
d537a26297 chore(build): reenable optional jobs in SL and BS
Closes #8558
2016-05-11 17:00:43 -07:00
d414734aac fix(build): Change publish-build-artifacts.sh to work with new packaging system 2016-05-11 16:58:18 -07:00
817ddfa847 fix(compiler): allow --noImplicitAny 2016-05-11 16:56:12 -07:00
c1154b30c7 fix(compiler): allow decorators defined in the same file 2016-05-11 16:56:12 -07:00
0d71345b93 fix(codegen): codegen all files in the program, not just roots
fixes #8475
2016-05-11 16:56:12 -07:00
f235454dd6 ci: temporarily disable Edge because of SauceLabs issues
https://github.com/angular/angular/issues/8604

On Sauce we've been getting the following error:

11 05 2016 00:58:35.765:ERROR [launcher.sauce]: Heartbeat to microsoftedge 20.10240 (Windows 10) failed
  [title()] Error response status: 13, , UnknownError - An unknown server-side error occurred while processing the command. Selenium error: Unknown error (WARNING: The server did not provide any stacktrace information)
Command duration or timeout: 285 milliseconds
Build info: version: '2.52.0', revision: '4c2593c', time: '2016-02-11 19:06:42'
System info: host: 'WIN-SB3ER6JQ6ME', ip: '172.20.60.246', os.name: 'Windows 10', os.arch: 'x86', os.version: '10.0', java.version: '1.8.0_73'
Driver info: org.openqa.selenium.edge.EdgeDriver
Capabilities [{acceptSslCerts=true, browserVersion=25.10586.0.0, platformVersion=10, browserName=MicrosoftEdge, takesScreenshot=true, pageLoadStrategy=normal, takesElementScreenshot=true, platformName=windows, platform=ANY}]
Session ID: XXXXXXXX-XXXX-XXXX-XXXX-XXXX478C1C1A
11 05 2016 00:58:35.766:ERROR [launcher]: microsoftedge 20.10240 (Windows 10) on SauceLabs failed 2 times (failure). Giving up.
2016-05-11 11:06:37 -07:00
6a80578d05 build: create the dist directory before building 2016-05-11 10:11:59 -07:00
d33cd43db1 docs(PULL_REQUEST_TEMPLATE.md): reorganize and improve the pull request template
Closes #7921
2016-05-10 10:55:35 -07:00
9e3df8eefe chore(tsickle): remove redundant jsdoc types
tsickle doesn't like them, and anyway they are bound to get out-of-sync with the inline TS types
2016-05-10 17:38:10 +02:00
cf73ad7c8f chore(security): document sanitization breaking change.
Sanitizing style and URL values breaks specific patterns, see #8491 for
an example. This documents and acknowledges the breaking change while we
work on improving CSS sanitization to allow more values through.
2016-05-10 17:36:36 +02:00
3e68b7eb1f feat(security): warn users when sanitizing in dev mode.
This should help developers to figure out what's going on when the sanitizer
strips some input.

Fixes #8522.
2016-05-09 16:46:31 +02:00
9fbafba993 chore(parsing): change internal usage of @ to : for namespaced values
Closes #8346
2016-05-09 16:20:32 +02:00
7a524e3deb feat(security): add tests for URL sanitization. 2016-05-09 16:00:24 +02:00
7b6c4d5acc feat(security): add tests for style sanitisation. 2016-05-09 16:00:24 +02:00
99c0d503d7 chore(build): run security tests in NodeJS, too. 2016-05-09 16:00:24 +02:00
f86edae9f3 feat(security): add an HTML sanitizer.
This is based on Angular 1's implementation, parsing an HTML document
into an inert DOM Document implementation, and then serializing only
specifically whitelisted elements.

It currently does not support SVG sanitization, all SVG elements are
rejected.

If available, the sanitizer uses the `<template>` HTML element as an
inert container.

Sanitization works client and server-side.

Reviewers: rjamet, tbosch , molnarg , koto

Differential Revision: https://reviews.angular.io/D108
2016-05-09 16:00:24 +02:00
df1b1f6957 feat(security): strip XSSI prefix from XHR responses. 2016-05-05 14:25:44 -07:00
9099160038 chore: fix comment indent. 2016-05-05 12:46:07 -07:00
119abe7bb9 chore: fail build if a command from tsc-watch fails.
This bug was introduced with eba6e7946d
to integrate the compiler_cli into the build properly.

Closes #8480
2016-05-04 20:30:10 -07:00
67ed2e2c0a feat(security): fill in missing security contexts.
Reviewers: koto, rjamet, molnarg

Differential Revision: https://reviews.angular.io/D109
2016-05-04 19:28:50 -07:00
6d36a7a45f chore: fix unit tests on node.js
Closes #8476
2016-05-04 18:00:29 -07:00
e2b1e1577d fix(core): don’t detach nested view containers when destroying a view
When a view is destroyed, we destroy all
views in view containers and should not detach them. However, previously, we also detached them which lead to problems during the iteration loop.

Closes #8458
Closes #8471

Introduced by 0c600cf6e3
2016-05-04 16:27:20 -07:00
b30ddfbfc5 chore(router): clang-format 2016-05-04 15:01:27 -07:00
abfb522f83 refactor(router): reuse existing segmentes when constructing new route trees 2016-05-04 14:51:04 -07:00
b8136cc26e fix(router): provide a top-level route segment for injection 2016-05-04 14:51:04 -07:00
d00b26d941 refactor(router): update link to reuse url segments when possible 2016-05-04 14:51:04 -07:00
12637a761c refactor(router): make names consistent 2016-05-04 14:50:00 -07:00
1a0aea67a0 feat(core): add a component resolver that can load components lazily using system.js 2016-05-04 14:50:00 -07:00
0f1465b899 feat(router): update router to support lazy loading 2016-05-04 14:50:00 -07:00
c0cfd3c6ed chore: remove ts-metadata-collector from shrinkwrap
We need to use the locally installed one.

Closes #8467
2016-05-04 12:29:47 -07:00
a81923b793 fix(compiler): emit correct types for literal arrays and maps. 2016-05-04 12:14:44 -07:00
7150ace7c7 fix(compiler): support lifecycle hooks in compiler_cli 2016-05-04 12:14:43 -07:00
bdce154282 chore: add test script for compiler_cli 2016-05-04 12:14:43 -07:00
5a84048f72 chore: adjust build for tools/metadata name change 2016-05-04 12:14:38 -07:00
188bda813e chore: rename tools/metadata into tools/ts-metadata-collector
Needed to that we can use the locally compiled one during
our tests.
2016-05-04 10:53:28 -07:00
29700aa188 feat(metadata): emit all methods
This is needed to detect lifecycle hooks.
2016-05-04 09:11:04 -07:00
3229bf1665 docs(changelog): add changelog for rc.1 2016-05-04 01:12:40 -06:00
52595f52f9 chore: make workaround_empty_observable_list_diff @internal 2016-05-04 01:12:40 -06:00
edec158dd8 fix(compiler_cli): allow to use builtin directives like NgIf, …
Related to #8448
Closes #8454
2016-05-03 20:49:17 -07:00
0297398f5e chore: clang-format master 2016-05-03 18:56:39 -07:00
9485f5a813 fix: platform-server should declare it's dependency on parse5 via package.json 2016-05-03 18:07:52 -06:00
8f8c017882 chore: remove angular2-template-compiler from package.json
Closes #8429
2016-05-03 16:25:55 -07:00
eba6e7946d refactor(compiler_cli): move it into modules/@angular and integrate properly into the build
This also does no more depend on a version
on npm for the compiler_cli.

Also runs the tests for tools/metadata
2016-05-03 15:57:49 -07:00
3cfe281790 fix(tsickle): put the tsickle support code at EOF 2016-05-03 15:15:22 -07:00
6eac4b68bc build: bump to rc.1 in package.json 2016-05-03 15:28:45 -06:00
fcbfacb6d5 build: do not use tsickle to lower router-deprecated
if we do this, we can break existing code that is inheriting from RouterOutlet and expects the metadata
in the old location
2016-05-03 15:09:35 -06:00
b600915953 build: add licensing preamble to umd bundles 2016-05-03 13:59:30 -06:00
905f38acb8 build: fix source map paths for npm packages
the bundles have source mpas disabled for now because when we downlevel
the esm bundle to es5 tsc doesn't consider the original source map so
we end up with a source map pointing to the esm bundle instead which is
not useful.
2016-05-03 13:36:33 -06:00
38f4c5f155 chore(compiler): update README following package move 2016-05-03 13:26:59 -06:00
509f4ec611 fix(testing): Check for pending macrotasks in ComponentFixture.whenStable() and ComponentFixture.isStable()
Closes #8389
2016-05-03 11:50:35 -07:00
27a7b51d99 fix(docs): upgrade deprecated ngFor-Syntax 2016-05-03 11:49:33 -07:00
a033f8335b fix(compiler): use rootDirs compilerOption to affect genDir layout.
Also update package.json to something releasable.
2016-05-03 12:41:28 -06:00
b98c9e74e1 docs(router): add api docs 2016-05-03 11:35:07 -07:00
9f784dcc5a cleanup(router): fix e2e tests 2016-05-03 10:25:44 -07:00
b625f2471a feat(router): make RouterLink accept single values 2016-05-03 10:25:44 -07:00
3aa2606ff1 cleanup(router): make names consistent 2016-05-03 10:25:44 -07:00
89704e0f93 fix(router): add support for ../ 2016-05-03 10:25:44 -07:00
908a102a87 feat: security implementation in Angular 2.
Summary:
This adds basic security hooks to Angular 2.

* `SecurityContext` is a private API between core, compiler, and
  platform-browser. `SecurityContext` communicates what context a value is used
  in across template parser, compiler, and sanitization at runtime.
* `SanitizationService` is the bare bones interface to sanitize values for a
  particular context.
* `SchemaElementRegistry.securityContext(tagName, attributeOrPropertyName)`
  determines the security context for an attribute or property (it turns out
  attributes and properties match for the purposes of sanitization).

Based on these hooks:

* `DomSchemaElementRegistry` decides what sanitization applies in a particular
  context.
* `DomSanitizationService` implements `SanitizationService` and adds *Safe
  Value*s, i.e. the ability to mark a value as safe and not requiring further
  sanitization.
* `url_sanitizer` and `style_sanitizer` sanitize URLs and Styles, respectively
  (surprise!).

`DomSanitizationService` is the default implementation bound for browser
applications, in the three contexts (browser rendering, web worker rendering,
server side rendering).

BREAKING CHANGES:
*** SECURITY WARNING ***
Angular 2 Release Candidates do not implement proper contextual escaping yet.
Make sure to correctly escape all values that go into the DOM.
*** SECURITY WARNING ***

Reviewers: IgorMinar

Differential Revision: https://reviews.angular.io/D103
2016-05-03 09:21:43 -07:00
dd6e0cf1b5 fix(compiler): fix where pipes live
Impure pipes need to live on the view
that used them and need a new instance for
each call site.

Impure pipes need to live on the component view, cached across all child views,
and need a new pure proxy for each for
each call site that lives on the view
of the call site.

Fixes #8408

This bug was introduced not long ago by 152a117d5c
2016-05-03 09:00:23 -07:00
52a6ba7ed9 fix(compiler): use absolute paths for comparing module urls 2016-05-02 22:40:00 -06:00
43e0fa513b fix(compiler): don’t emit metadata for generated files 2016-05-02 22:40:00 -06:00
de978229b2 chore(compiler): Run compiler_cli tests in new CI.
Also update docs/peer deps, display errors from running jasmine, use the right location of cjs-jasmine
2016-05-02 22:39:32 -06:00
ba62fe974b refactor(static_reflector): remove unnecessary check. 2016-05-02 22:36:32 -06:00
3a40cb1a85 fix(compiler): calculate the right moduleUrl 2016-05-02 22:36:32 -06:00
883e0c48b1 docs(CHANGELOG): document 2.0.0-rc.0 2016-05-02 21:34:27 -06:00
18b6a55764 build: update publish scripts and package.json 2016-05-02 20:45:05 -06:00
00d3b6083c fix(compiler): support css stylesheets in offline compiler 2016-05-02 15:06:46 -07:00
c386fc8379 chore: make compiler_cli build again 2016-05-02 15:06:46 -07:00
43527172ed chore: don’t shadow tsconfig.json used for editors by build specific tsconfig.json 2016-05-02 15:06:46 -07:00
b88384eed8 build: add router-deprecated to the publishing scripts 2016-05-02 15:10:58 -06:00
107016ec12 chore: router move import changes 2016-05-02 13:27:03 -07:00
d930ad1816 chore: router move-only 2016-05-02 13:27:03 -07:00
072446aed3 feat(offline compiler): add metadata emit
Also add a configuration switch to disable the codegen, so we can
still use the metadata emit and tsickle pre-processing in the
build pipeline for angular itself.
2016-05-02 11:47:59 -07:00
2e1f3f003d build: adding basic e2e testing infrastructure 2016-05-02 08:15:10 -07:00
fdd8bd1a36 chore: use ng2tc for compiling and running tests on ci 2016-05-01 23:40:59 -07:00
7db911fdd4 chore: update to tsickle 0.1.2 2016-05-01 23:40:59 -07:00
b6fd81169b feat(core): support the decorator data that tsickle produces 2016-05-01 23:40:59 -07:00
3ae856ab8b build(tsc): Use angular2-template-compiler in place of tsc
This lets us down-level Decorators with tsickle and produce .metadata.json
files for users to reference when offline-compiling their app.
2016-05-01 23:40:59 -07:00
ce5b37239e chore: add lint job to travis 2016-05-01 22:59:41 -07:00
3e17c99f4e chore: clang-reformat 2016-05-01 22:59:41 -07:00
bb8976608d fix: karma timouts for saucelabs 2016-05-01 22:27:55 -07:00
cd52318f48 fix: parse browser detection lazily 2016-05-01 22:27:55 -07:00
2570b72158 fix: textSelection on FireFox 2016-05-01 22:27:55 -07:00
6e79de794c fix: function name shim test 2016-05-01 22:27:55 -07:00
c4be30d2e8 Revert "build(tsc): Use angular2-template-compiler in place of tsc"
This reverts commit 3d25294f706e0fd6224b20372be1e961959c0af8.
2016-05-01 20:51:00 -07:00
57240c85a5 build(tsc): Use angular2-template-compiler in place of tsc
This gives us tsickle pre-processing of Decorators, and produces
.metadata.json files for users to consume in their offline compilation.
2016-05-01 20:51:00 -07:00
a66cdb469f repackaging: all the repackaging changes squashed 2016-05-01 20:51:00 -07:00
505da6c0a8 repackaging: all the file moves 2016-05-01 20:51:00 -07:00
4fe0f1fa65 feat(router): set router-link-active when RouterLink is active
Closes #8376
2016-05-02 01:03:22 +00:00
ec4ca0eace feat(router): implements support for router-link-active 2016-05-02 01:03:22 +00:00
db95fd6ca9 build(offline compiler): package the compiler-cli for users
Closes #8341
2016-05-02 00:38:54 +00:00
ca13f1c024 fix(router): create a route tree when creating the router service
Closes #8365
2016-05-01 21:38:25 +00:00
277b1fc473 feat(router): add RouteTree and UrlTree as aliases to Tree<RouteSegment> and Tree<UrlSegment> 2016-05-01 21:38:25 +00:00
8836219b16 feat(router): add support for wildcards 2016-05-01 21:38:25 +00:00
6f5e3f9390 Update evaluator.spec.ts
Fix stray `fit` that skips tools tests
Closes #8373
2016-05-01 18:43:32 +00:00
a84c2d7fee fix(typescript): strip abstract keyword from properties in .d.ts
Fixes angular2/src/alt_router/metadata/metadata.d.ts

Closes #8339
2016-05-01 02:59:15 +00:00
f114d6c560 fix(compiler): fix cross view references and providers with useValue.
Before, we would create all fields in the generated views
with visibility `private`. This does not work if an embedded
view references a directive / element in a parent view.
In Dart, this was no problem so far as it does not have
a `private` modifier.

Before, `useValue` in a provider did not work when doing
offline compile, as so far the `MetadataResolver` was only
used for jit mode. Now, `useValue` supports any kind of value
that the static reflector can return. E.g. primitives,
arrays, string maps, …

Closes #8366
2016-05-01 02:30:33 +00:00
163d80adb7 fix(compiler_cli): make sure the generated code gets compiled via tic 2016-05-01 02:30:33 +00:00
9e05814212 chore: move StaticReflector into compiler_cli, part 2
Adjust tests and build to run the unit tests again
after the move.
Closes #8363
2016-04-30 20:53:54 +00:00
ab56be46e1 chore: move static_reflector into compiler_cli
Most of the bugs discovered so far in the offline compiler were related to the StaticReflector. As it was part of angular2 core, it was hard to update. Moving it into the compiler_cli allows to release more often until the compiler_cli gets more stable.

Note: Moving the unit test next to the sources is the simplest option for now in terms of build setup.

Note: This commit only does the move. The next commit updates the build to run it again.
2016-04-30 20:53:54 +00:00
6a0cbb8a57 refactor(core): type ComponentRef, ComponentFactory and ComponentFixture by the component type
BREAKING CHANGE:
- `ComponetRef`, `ComponentFactory`, `ComponentFixture` now all require a type
  parameter with the component type.
Closes #8361
2016-04-30 19:47:54 +00:00
4e2c68354e chore: update ts2dart to 0.9.10 2016-04-30 19:47:54 +00:00
62a0809e81 feat(router): listen to location changes
Closes #8362
2016-04-30 19:02:14 +00:00
76d6f5fa0d fix(router): canDeactivate should not change the url when returns false
Closes #8360
2016-04-30 17:50:28 +00:00
0f1b370117 feat(tests): add ROUTER_FAKE_PROVIDERS to angular2/alt_router/router_testing_providers
This change adds providers for fake router dependecies.
This allows TestComponentBuilder to create components with RouterLink and RouterOutlet directives
without the test writer needing to override them.
2016-04-30 09:42:15 -07:00
e589f9949b chore: align badges in README.md 2016-04-30 08:47:49 -07:00
0f774df811 fix(compiler): project using the right directive as component.
Closes #8344
2016-04-29 18:30:30 -07:00
351f24e8eb fix(core): return the ChangeDetectorRef of the component also for embedded views. 2016-04-29 18:30:30 -07:00
aecb60a604 refactor(core): use Function.bind for referring to event listeners instead of a closure. 2016-04-29 18:30:30 -07:00
4d691b61ee fix(core): check components if an event handler inside of an embedded view fires.
BREAKING CHANGE:
- ViewRef.changeDetectorRef was removed as using ChangeDetectorRefs
  for EmbeddedViewRefs does not make sense. Use ComponentRef.changeDetectorRef
  or inject ChangeDetectorRef instead.

Fixes #8242
2016-04-29 18:30:29 -07:00
11955f9b13 fix(compiler): support empty array and map literals.
This was broken after 152a117d5c

Fixes #8336
2016-04-29 18:30:29 -07:00
5ff31f0713 build(router): create alt_router bundle 2016-04-29 18:05:36 -07:00
deba804671 feat(router): add CanDeactivate 2016-04-29 18:05:06 -07:00
e5b87e55da feat(router): implement relative navigation 2016-04-29 18:04:55 -07:00
d097784d57 chore: adjust public API spec to API changes. 2016-04-29 17:34:32 -07:00
15f6b27ae0 refactor(compiler): support referenced OpaqueTokens correctly in offline compiler. 2016-04-29 16:53:51 -07:00
176e55927c refactor(compiler): support hash syntax for providers. 2016-04-29 16:53:50 -07:00
365be6a309 chore: clang-format after various changes. 2016-04-29 16:53:50 -07:00
713e6d4aff chore: adjust router to /*@ts2dart...*/ 2016-04-29 16:53:50 -07:00
a8e277b067 chore: remove const Provider() in favor of /* @ts2dart_Provider */ {provide:} 2016-04-29 16:53:50 -07:00
3aa322a9c6 chore: replace @CONST() with /*@ts2dart_const*/ 2016-04-29 16:53:50 -07:00
a02614beaa chore: replace CONST_EXPR with /*@ts2dart_const*/ 2016-04-29 16:53:49 -07:00
d2527b504a chore: upgrade to ts2dart@0.9.9 2016-04-29 16:53:49 -07:00
46cd868827 feat(di): support map literals as providers 2016-04-29 16:53:49 -07:00
b1a9e445b3 fix(perf): don’t use try/catch in production mode
The previous code that had `try/catch` statements in methods could not be optimized by Chrome. 

This change separates `AppView` (no `try/catch`) form `DebugAppView` (always `try/catch`). Our codegen will use `AppView` in production mode and `DebugAppView` in debug mode.

Closes #8338
2016-04-29 10:18:57 -07:00
5297c9d9cc refactor(core): deprecate DynamicComponentLoader and DebugNode.inject
BREAKING CHANGE:
- `DynamicComponentLoader` is deprecated. Use `ComponentResolver` and `ViewContainerRef` directly.
- `DebugNode.inject` is deprecated. use `DebugNode.injector.get` instead.
2016-04-29 01:37:58 -07:00
ee7caceec7 Revert "docs: deprecate DynamicComponentLoader and DebugNode.inject"
This reverts commit a0b5964a63.
2016-04-29 01:36:58 -07:00
a0b5964a63 docs: deprecate DynamicComponentLoader and DebugNode.inject 2016-04-29 01:34:06 -07:00
cacdead96d feat(core): introduce template context
BREAKING CHANGE:
- Before, a `EmbeddedViewRef` used to have methods for 
  setting variables. Now, a user has to pass in a context
  object that represents all variables when an `EmbeddedViewRef`
  should be created.
- `ViewContainerRef.createEmbeddedViewRef` now takes
   a context object as 2nd argument.
- `EmbeddedViewRef.setLocal` and `getLocal` have been removed.
  Use `EmbeddedViewRef.context` to access the context.
- `DebugNode.locals` has been removed. Use the new methods `DebugElement.references`
  to get the references that are present on this element,
  or `DebugElement.context` to get the context of the `EmbeddedViewRef` or the component to which the element belongs.

Closes #8321
2016-04-29 01:22:13 -07:00
96ae348648 chore(build): fix formatting and tests
Closes #8098
2016-04-28 23:59:06 -07:00
78946fe9fa feat(offline compiler): a replacement for tsc that compiles templates
see #7483.
2016-04-28 21:57:16 -07:00
33e53c9a59 chore(compiler): add dependency on tsickle
This tool lets us re-write TypeScript sources before entering the emit pipeline.
For example, we lower Decorators to the tree-shakable Annotation form.
2016-04-28 21:55:18 -07:00
c493d88405 chore(compiler): refactoring for offline compiler cli
- pass a baseUrl for asset resolution from static symbols
- fixes in StaticReflector to work with a path-aware host

see #7483
2016-04-28 21:54:02 -07:00
8bf6ef6544 fix(metadata): expose Providers in metadata
These worked in Dart because they were effectively exported even without the export keyword.
Without exporting these symbols, they are not produced in .metadata.json files, which leaves
dangling references from the Decorators that use them.
2016-04-28 21:31:28 -07:00
ca40ef5ac7 fix(codegen): event handler has boolean return type 2016-04-28 21:31:28 -07:00
7c0d4976b1 fix(8223): Preserve Provider expressions
Preserves constructor calls in addition to function calls.
Introduced a special case for forwardRef() similar to CONST_EXPR.
2016-04-28 21:31:28 -07:00
30de2db349 cleanup(router): make analyzer happy
Closes #8220
2016-04-29 02:45:57 +00:00
602641dffd feat(router): adds an example app using the new router 2016-04-29 02:45:57 +00:00
560cc14d97 feat(router): change location when navigating 2016-04-29 02:45:57 +00:00
de56dd5f30 feat(router): add RouterLink 2016-04-29 02:45:57 +00:00
fa5bfe4b64 feat(router): add link that support only absolute urls 2016-04-29 02:45:57 +00:00
446657bdd5 feat(router): update recognize to handle matrix parameters 2016-04-29 02:45:57 +00:00
79830f1c75 feat(router): add RouterUrlSerializer 2016-04-29 02:45:57 +00:00
6e1fed42b7 feat(router): add Router and RouterOutlet to support aux routes 2016-04-29 02:45:57 +00:00
d35c109cb9 feat(router): update recognize to support aux routes 2016-04-29 02:45:57 +00:00
fad3b6434c feat(router): update url parser to handle aux routes 2016-04-29 02:45:57 +00:00
073ec0a7eb chore(CHANGELOG): Fix breaking changes description
Adds more info and examples about how ngFor is affected by the change.
2016-04-28 19:03:54 -07:00
70b23ae2ca refactor(compiler): make static reflector work
Also adjust `RuntimeMetadataResolver` to
be able to use it. 

Also rename `RuntimeMetadataResolver` into `CompileMetadataResolver`.
Closes #8313
2016-04-28 23:06:17 +00:00
769835e53e feat(testing): Use NgZone in TestComponentBuilder.
Instantiating the test component within an NgZone will let us track async tasks in event handlers and change detection.

We can also do auto change detection when triggering events through dispatchEvent and not have to do fixture.detectChange() manually in the test.

New API:
ComponentFixture.autoDetectChanges() - This puts the fixture in auto detect mode that automatically calls detectChanges when the microtask queue is empty (Similar to how change detection is triggered in an actual application).

ComponentFixture.isStable() - This returns a boolean whether the fixture is currently stable or has some async tasks that need to be completed.

ComponentFixture.whenStable() - This returns a promise that is resolved when the fixture is stable after all async tasks are complete.

Closes #8301
2016-04-28 22:37:37 +00:00
ac55e1e27b fix(build): Resolve Dart analyzer issues with the Dart dev channel
Closes #8316
2016-04-28 20:35:56 +00:00
0700c8a252 docs(changelog): update change log to 2.0.0-beta.17. 2016-04-28 11:47:32 -07:00
c79e657fcd chore(release): bump version to 2.0.0-beta.17. 2016-04-28 11:39:20 -07:00
d9648887b8 fix(metadata): Do not attach module names to metadata.
The filename contains the module name as resolved by users, so the top-level module name is uneeded.
Module names on references are replaced by capturing the import syntax from the module.
This allows readers of the metadata to do the module resolution themselves.

Fixes #8225
Fixes #8082

Closes #8256
2016-04-28 01:58:13 +00:00
35cd0ded22 chore(testing): Refactor test methods to have a uniform interface.
Remove FunctionWithParamTokens.

All test wrappers async, fakeAsync and inject now return just a Function instead of FunctionWithParamTokens. This makes them directly consumable by the test framework. Also the test framework code does not have to handle a union of Function and FunctionWithParamTokens everywhere.

The Function returned by the above methods are considered asynchronous by the test framework if they return a Promise, synchronous otherwise.

Closes #8257
2016-04-28 01:16:25 +00:00
d2efac18ed feat(core): separate refs from vars.
Introduces `ref-` to give a name to an element or a directive (also works for `<template>` elements), and `let-` to introduce an input variable for a `<template>` element.

BREAKING CHANGE:
- `#...` now always means `ref-`.
- `<template #abc>` now defines a reference to the TemplateRef, instead of an input variable used inside of the template.
- `#...` inside of a *ngIf, … directives is deprecated.
  Use `let …` instead.
- `var-...` is deprecated. Replace with `let-...` for `<template>` elements and `ref-` for non `<template>` elements.

Closes #7158

Closes #8264
2016-04-28 01:13:40 +00:00
ff2ae7a2e1 fix(testing): allow test component builder to override directives from lists
When a component uses a list of directives, such as `ROUTER_DIRECTIVES`,
make `TestComponentBuilder#overrideDirective` work properly for members
of the list.

Closes #7397

Closes #8217
2016-04-28 00:56:45 +00:00
e1058a4d8a Revert "feat(compiler): ElementSchema now has explicit DOM schema information"
This reverts commit d327ac4b43.
2016-04-27 17:41:57 -07:00
d327ac4b43 feat(compiler): ElementSchema now has explicit DOM schema information
This makes the schema available for offline compile compiler as well.

Closes #8179
2016-04-27 22:57:28 +00:00
1ad2a02b11 fix(core): properly evaluate expressions with conditional and boolean operators
Fixes #8235
Fixes #8244

Closes #8282
2016-04-27 21:25:50 +00:00
1e8864c4a5 fix(compiler): Allow templates to access variables that are declared afterwards.
Fixes #8261
2016-04-27 11:22:44 -07:00
c209836fd0 fix(changelog): fix changelog script.
One charecter missing from
140a878a3d
2016-04-26 16:12:59 -07:00
7d1b6af073 style(global): group multiple imports from same module
Closes #7802

Closes #8209
2016-04-26 22:40:30 +00:00
140a878a3d chore(changelog): regenerate changelog for beta.16 including refactor
- the script explicitly takes input for the starting tag.
- the script only prepends changes.
2016-04-26 15:33:35 -07:00
b62bccf254 build(npm): update rxjs to 5.0.0-beta.6
Closes #6871
Closes #8047
2016-04-26 21:16:37 +00:00
39eb34739a chore(changelog): fix changelog with messages about testing zone deps
Closes #8253
2016-04-26 19:01:12 +00:00
969b55326c docs(changelog): update change log to beta.16 2016-04-25 22:08:32 -07:00
2c8371654a chore(release): bump version to beta.16 2016-04-25 21:48:58 -07:00
5a897cf299 feat(router): add Router and RouterOutlet
Closes #8173
2016-04-25 22:41:33 +00:00
ef67a0c57f feat(router): add router metadata 2016-04-25 22:41:33 +00:00
ef6163e652 feat(router): implement recognizer 2016-04-25 22:41:33 +00:00
f6985671dd feat(router): implement RouterUrlParser 2016-04-25 22:41:33 +00:00
90a1f7d5c4 feat(router): add UrlSegment, RouteSegment, and Tree 2016-04-25 22:41:33 +00:00
3e114227f8 refactor(core): support importUri in StaticReflector
Closes #8195
2016-04-25 22:14:29 +00:00
6103aa0a46 fix(release): Fix the package.json zone.js requirement to 0.6.12 2016-04-25 15:10:06 -07:00
b7c6feff28 chore(release): release the metadata collector
Closes #8197
2016-04-25 21:48:07 +00:00
b48d907697 docs(template-syntax): rename elvis operator
Closes #8196
2016-04-25 21:26:33 +00:00
676ddfa065 docs(cheatsheet): add Directive to cheatsheet
Closes #8170
2016-04-25 20:41:34 +00:00
c8d00dc191 fix(codegen): add explicit any to class fields
fixes #8204

Closes #8205
2016-04-25 20:21:18 +00:00
0b6865d6c6 chore: Remove AngularEntrypoint from TS
Closes #8158
2016-04-25 17:35:05 +00:00
67d05eb65f fix(compiler): use DI order for change detection order.
Closes #8198
2016-04-25 09:36:46 -07:00
152a117d5c fix(compiler): properly implement pure pipes and change pipe syntax
Pure pipes as well as arrays and maps are
implemented via proxy functions. This is
faster than the previous implementation
and also generates less code.

BREAKING CHANGE:
- pipes now take a variable number of arguments, and not an array that contains all arguments.
2016-04-25 09:04:22 -07:00
d6626309fd Revert "fix(compiler): only call pure pipes if their input changed."
This reverts commit 8db62151d2.
2016-04-25 07:53:49 -07:00
c3daccd83b fix(forms): ensure select model updates in firefox and ie
Closes #6573

Closes #8148
2016-04-22 21:23:20 +00:00
8db62151d2 fix(compiler): only call pure pipes if their input changed. 2016-04-21 16:20:19 -07:00
bab81a9831 feat(test): Implement fakeAsync using the FakeAsyncTestZoneSpec from zone.js.
Update the version of zone.js to @0.6.12 that contains the new FakeAsyncTestZoneSpec.

The new fakeAsync zone handles errors better and clearPendingTimers() is no longer required to be called after handling an error and is deprecated.

The fakeAsync test zone will now throw an error if an XHR is attemtped within the test since that cannot be controlled synchronously in the test(Need to be mocked out with a service implementation that doesn't involve XHRs).

This commit also allows fakeAsync to wrap inject to make it consistent with async test zone.

BREAKING CHANGE:

inject can no longer wrap fakeAsync while fakeAsync can wrap inject. So the order in existing tests with inject and fakeAsync has to be switched as follows:

Before:
```
inject([...], fakeAsync((...) => {...}))
```

After:
```
fakeAsync(inject([...], (...) => {...}))
```

Closes #8142
2016-04-21 22:11:00 +00:00
cc86fee1d1 fix(compiler): support string tokens with . inside. 2016-04-21 11:17:49 -07:00
386cc5dbb6 fix(transformers): support query.read
Closes #8172
2016-04-21 10:12:59 -07:00
2f7045720a fix(core): various minor compiler fixes
Closes #8162
2016-04-21 09:13:02 -07:00
9889c21aaa fix(metadata): emit metadata rooted at 'angular2'
fixes #8144

closes #8147
2016-04-20 17:14:53 -04:00
e69cb40de3 fix(forms): number input should report null when blank
Closes #6932

Closes #8141
2016-04-20 20:33:18 +00:00
12837e1c17 fix(forms): improve error message when ngFormModel is missing a form
Closes #8136

Closes #8143
2016-04-20 20:10:50 +00:00
9092ac79d4 refactor(core): support non reflective bootstrap.
This changes Angular so that it can be used without reflection (assuming a codegen for injectors).

BREAKIKNG CHANGE:
- Drops `APP_COMPONENT` provider. Instead, inject
  `ApplicationRef` and read its `componentTypes` property.
- long form bootstrap has changed into the following:
  ```
  var platform = createPlatform(ReflectiveInjector.resolveAndCreate(BROWSER_PROVIDERS));
  var appInjector =
    ReflectiveInjector.resolveAndCreate([BROWSER_APP_PROVIDERS, appProviders], platform.injector);
  coreLoadAndBootstrap(appInjector, MyApp);
  ```
2016-04-20 11:34:11 -07:00
0a7d10ba55 refactor(core): separate reflective injector from Injector interface
BREAKING CHANGE:
- Injector was renamed into `ReflectiveInjector`,
  as `Injector` is only an abstract class with one method on it
- `Injector.getOptional()` was changed into `Injector.get(token, notFoundValue)`
  to make implementing injectors simpler
- `ViewContainerRef.createComponent` now takes an `Injector`
  instead of `ResolvedProviders`. If a reflective injector
  should be used, create one before calling this method.
  (e.g. via `ReflectiveInjector.resolveAndCreate(…)`.
2016-04-20 11:28:13 -07:00
efbd446d18 refactor(core): add Query.read and remove DynamicComponentLoader.loadIntoLocation.
This adds the feature for `@ViewChild`/`@ViewChildren`/`@ContentChild`/`@ContentChildren` to define what to read from the queried element.

E.g. `@ViewChild(`someVar`, read: ViewContainerRef)` will locate the element with a variable `someVar` on it and return a `ViewContainerRef` for it.

Background: With this change, Angular knows exactly at which elements there will be `ViewConainerRef`s as the user has to ask explicitly of them. This simplifies codegen and will make converting Angular templates into server side templates simpler as well.

BREAKING CHANGE:
- `DynamicComponentLoader.loadIntoLocation` has been removed. Use `@ViewChild(‘myVar’, read: ViewContainerRef)` to get hold of a `ViewContainerRef` at an element with variable `myVar`.
- `DynamicComponentLoader.loadNextToLocation` now takes a `ViewContainerRef` instead of an `ElementRef`.
- `AppViewManager` is renamed into `ViewUtils` and is a mere private utility service.
2016-04-20 11:28:00 -07:00
c06b0a2371 refactor(codegen): produce .ngfactory.dart/ts files instead of .template.dart/ts files.
This is needed as we will soon store other
things into the generated files, not
only the templates.
2016-04-20 11:27:34 -07:00
0c600cf6e3 refactor(core): introduce ComponentFactory.
Each compile template now exposes a `<CompName>NgFactory` variable
with an instance of a `ComponentFactory`.
Calling `ComponentFactory.create` returns a `ComponentRef` that can
be used directly.

BREAKING CHANGE:
- `Compiler` is renamed to `ComponentResolver`,
  `Compiler.compileInHost` has been renamed to `ComponentResolver.resolveComponent`.
- `ComponentRef.dispose` is renamed to `ComponentRef.destroy`
- `ViewContainerRef.createHostView` is renamed to `ViewContainerRef.createComponent`
- `ComponentFixture_` has been removed, the class `ComponentFixture`
  can now be created directly as it is no more using private APIs.
2016-04-20 11:27:26 -07:00
41404057cf fix(build): ignore Dart warnings for external code. 2016-04-20 10:51:58 -07:00
f4e6994634 feat(NgTemplateOutlet): add NgTemplateOutlet directive
This commits adds a new NgTemplateOutlet directive that can be
used to create embeded views from a supplied TemplateRef.

Closes #7615

Closes #8021
2016-04-20 04:28:59 +00:00
b602bd8c83 refactor(Location): out of router and into platform/common
closes https://github.com/angular/angular/issues/4943

BREAKING CHANGE:

`Location` and other related providers have been moved out of `router` and into `platform/common`. `BrowserPlatformLocation` is not meant to be used directly however advanced configurations may use it via the following import change.

Before:

```
import {
  PlatformLocation,
  Location,
  LocationStrategy,
  HashLocationStrategy,
  PathLocationStrategy,
  APP_BASE_HREF}
from 'angular2/router';

import {BrowserPlatformLocation} from 'angular2/src/router/location/browser_platform_location';
```

After:

```
import {
  PlatformLocation,
  Location,
  LocationStrategy,
  HashLocationStrategy,
  PathLocationStrategy,
  APP_BASE_HREF}
from 'angular2/platform/common';

import {BrowserPlatformLocation} from 'angular2/src/platform/browser/location/browser_platform_location';
```

Closes #7962
2016-04-20 04:28:47 +00:00
30c43521d3 fix(http) : set response.ok based on given status code
Closes #8056
2016-04-20 04:28:27 +00:00
45f5df371d docs(markdown): add missing space between markdown ### and text, turn h1 into h3s, remove bold.
Closes #7996
2016-04-20 04:09:31 +00:00
43e31c5abb docs(): fix a typo: patform -> platform
Closes #8081
2016-04-18 19:53:19 -07:00
13c8b13343 Add BREAKING CHANGE to CHANGELOG for b.15
Dart apps that import angular2/bootstrap.dart ran in beta.14, but fail in beta.15.

Closes #8071
2016-04-18 19:53:19 -07:00
0fc9ec248e fix(upgrade): clean up scope when element is destroyed
Closes #8102
2016-04-19 01:06:14 +00:00
d094a85647 fix(angular_1_router): Removed arrow function from module template
Closes #8076
2016-04-19 00:44:17 +00:00
22c05b0834 fix(tests): remove payload size check 2016-04-18 17:08:55 -07:00
8490921fb3 feat(tests): manage asynchronous tests using zones
Instead of using injectAsync and returning a promise, use the `async` function
to wrap tests. This will run the test inside a zone which does not complete
the test until all asynchronous tasks have been completed.

`async` may be used with the `inject` function, or separately.

BREAKING CHANGE:

`injectAsync` is now deprecated. Instead, use the `async` function
to wrap any asynchronous tests.

Before:
```
it('should wait for returned promises', injectAsync([FancyService], (service) => {
  return service.getAsyncValue().then((value) => { expect(value).toEqual('async value'); });
}));

it('should wait for returned promises', injectAsync([], () => {
  return somePromise.then(() => { expect(true).toEqual(true); });
}));
```

After:
```
it('should wait for returned promises', async(inject([FancyService], (service) => {
  service.getAsyncValue().then((value) => { expect(value).toEqual('async value'); });
})));

// Note that if there is no injection, we no longer need `inject` OR `injectAsync`.
it('should wait for returned promises', async(() => {
  somePromise.then() => { expect(true).toEqual(true); });
}));
```

Closes #7735
2016-04-18 15:59:07 -07:00
ecb9bb96f0 docs(): fix broken links
Closes #8028
2016-04-18 20:12:37 +00:00
75463cd8df chore(perf): return perf metrics from AngularProfiler
Closes #8075
2016-04-18 19:48:52 +00:00
c6244d1470 feat(i18n): add support for nested expansion forms
Closes #7977
2016-04-18 19:38:12 +00:00
22ae2d0976 cleanup(html_parser): cleanup to fix analyzer warnings 2016-04-18 19:38:12 +00:00
88b0a239c4 feat(i18n): support plural and gender special forms 2016-04-18 19:38:12 +00:00
7c9717bba8 feat(html_parser): support special forms used by i18n { exp, plural, =0 {} } 2016-04-18 19:38:11 +00:00
7f297666ca feat(html_lexer): support special forms used by i18n { exp, plural, =0 {} } 2016-04-18 19:38:11 +00:00
d99823e2fd docs(core): fix some grammar
Closes #8055
2016-04-18 19:34:17 +00:00
bb9fb21fac feat(i18n): add custom placeholder names
Closes #7799

Closes #8057
2016-04-18 19:14:15 +00:00
b64672b23c fix(dart) reverts protobuf to last working version
Closes #8125
2016-04-18 18:28:43 +00:00
930f58718b Revert "feat(i18n): add support for custom placeholder names"
This reverts commit 2abb414cfb.
2016-04-14 15:36:40 -07:00
2b34c88b69 refactor(view_compiler): codegen DI and Queries
BREAKING CHANGE:
- Renderer:
  * renderComponent method is removed form `Renderer`, only present on `RootRenderer`
  * Renderer.setDebugInfo is removed. Renderer.createElement / createText / createTemplateAnchor
    now take the DebugInfo directly.
- Query semantics:
  * Queries don't work with dynamically loaded components.
  * e.g. for router-outlet: loaded components can't be queries via @ViewQuery,
    but router-outlet emits an event `activate` now that emits the activated component
- Exception classes and the context inside changed (renamed fields)
- DebugElement.attributes is an Object and not a Map in JS any more
- ChangeDetectorGenConfig was renamed into CompilerConfig
- AppViewManager.createEmbeddedViewInContainer / AppViewManager.createHostViewInContainer
  are removed, use the methods in ViewContainerRef instead
- Change detection order changed:
  * 1. dirty check component inputs
  * 2. dirty check content children
  * 3. update render nodes

Closes #6301
Closes #6567
2016-04-13 14:43:48 -07:00
45f09ba686 docs(changelog): update changelog to beta.15 2016-04-13 14:39:17 -07:00
bb62905bef chore(release): bump version to beta.15 2016-04-13 14:39:17 -07:00
7bc9b19418 cleanup(tests): remove unused imports
Closes #6784
2016-04-13 13:24:04 -07:00
e9f7a00910 docs(metadata): Add more docs of ViewChild and ViewChildren
Closes #7174
2016-04-13 13:24:04 -07:00
a5d6b6db8b fix(WebWorker): Fix textarea value not being sent to the worker
Closes #7439
Closes #7828
2016-04-13 13:24:04 -07:00
fc496813e2 fix(7877): StaticReflector returns empty results instead of undefined.
Reflector always returns either an empty object or an empty list if no
metadata is recorded for the class. StaticReflector matches this
behavior.

Closes #7986
2016-04-13 13:23:54 -07:00
2abb414cfb feat(i18n): add support for custom placeholder names
Closes #7799
Closes #8010
2016-04-13 13:23:42 -07:00
0e56aaf189 fix: remove typescript references to d.ts files from benchpress and e2e tests
using "/// <reference" is incorrect because it makes our code non-portable. The correct solution is to provide
these typings as ambient typings as an additional entry point - which we already do.

Closes #8050
2016-04-13 13:23:27 -07:00
3412aba46e feat(typescript): update to 1.9 nightly.
To workaround https://github.com/Microsoft/TypeScript/issues/7573
we must remove the readonly keyword from generated .d.ts files.
This solution will not scale, but will probably buy enough time to require our users move to a 2.0 beta.

Closes #8003
2016-04-13 18:54:58 +00:00
347e71af7d chore(travis): enable the typescript@next build
Fixes #7050
2016-04-13 18:54:58 +00:00
d24df799d3 Revert "feat(iterable_differ): support immutable lists"
In Dart, ImmutableLists are just a projection of an underlying list.
I.e. if the underlying list changes, the ImmutableList also changes.
So we can't make optimizations based on checking whether a collection
is an ImmutableList.

This reverts commit a10c02cb41.

Closes #8023
2016-04-13 17:02:48 +00:00
01e6b8c7ed fix(build): ignore dart warnings The name … is shown, but not used
See https://github.com/angular/angular/issues/8044

Closes #8045
2016-04-13 09:49:33 -07:00
60727c4d2b revert(format): Revert "chore(format): update to latest formatter"
This reverts commit 03627aa84d.
2016-04-12 09:41:01 -07:00
03627aa84d chore(format): update to latest formatter
Closes #7958
2016-04-11 22:15:23 +00:00
83b8f59297 feat(transformers): special case Profiler 2016-04-11 14:32:22 -07:00
c6f454f51d docs: remove duplicate 'directives' from example
Closes #7963
2016-04-11 21:29:21 +00:00
ccff17599a feat(ngFor): Support convenience view local in ngFor
Closes #8013
2016-04-11 21:27:48 +00:00
fb2773b8f3 docs(router): fix wording of hashchange explanation
Closes #7776
2016-04-11 21:11:11 +00:00
5110121f6e docs(ViewQuery): fix typo in documentation
fix typo that confusingly refers to `@Query` rather than `@ViewQuery`.

Closes #7870
2016-04-11 21:04:08 +00:00
27cf897239 chore: upgrade zone.js to v0.6.10
Closes #7818
Closes #7721

Closes #7888
2016-04-11 21:03:59 +00:00
5d33a12af4 chore(gulpfile): turn off mangle for prod
Closes #7988
2016-04-11 20:50:14 +00:00
08b295603c fix(7987): Incremental build works with new trees
Closes #7989
2016-04-11 20:28:34 +00:00
3b60503d2b feat(transformers): changes transformers to collect information about providers and resolve identifiers during linking 2016-04-10 19:36:16 -07:00
3c2473bac6 Fixed typo in documentation
Closes #7943
2016-04-08 23:27:36 +00:00
f9426709ef chore(build): Fix errors reported using 1.9.
Closes #7954
2016-04-08 21:53:50 +00:00
e1e44a910e fix(select): set value individually from ngModel
Closes #7975

Closes #7978
2016-04-08 21:08:05 +00:00
f371c9066d build(broccoli): Clean-up TypeScript build
The TypeScript parser now only references files that are in broccoli trees.

Closes #7941
2016-04-08 19:30:39 +00:00
85c1927993 build(broccoli): AngularBuilder compiles with TypeScript 1.8+.
Beginning with 1.8, if a modules has both a .ts and .d.ts file, the .js
file is not written.

Closes #7947
2016-04-08 19:24:04 +00:00
a596b887ff feat(compiler): Add an implementation for XHR that uses a template cache to load template files.
Useful for avoiding doing an actual XHR during testing.
Part of the solution for #4051 (Other part is a Karma plugin that will create the template cache).

Closes #7940
2016-04-08 19:05:05 +00:00
6cbf99086e feat(gestures): allow override of Hammer default configuration
Closes #7924
2016-04-08 18:53:58 +00:00
26a3390549 refactor(dart/transform): Remove deprecated angular2/bootstrap
BREAKING CHANGE

Remove the deprecated angular2/bootstrap.ts &
angular2/bootstrap_static.ts libraries.

Browser entry points should import angular2/platform/browser which
supplies the `bootstrap` function.

Closes #7650
2016-04-08 18:28:35 +00:00
9a1959f77a build(tslint): re-enable linter and fix violations
fixes #7798

Closes #7800
2016-04-07 23:11:02 +00:00
226e662cf1 feat(parser): TemplateParser.tryParse() returns both the AST and errors
The language service (#7482) always needs the AST even if there are errors
in the template.

Closes #7858
2016-04-07 22:00:46 +00:00
7a1a1b80ed Roll forward to 0.1.24
Closes #7867
2016-04-07 21:58:48 +00:00
529988bc81 Fix DDC errors 2016-04-07 21:58:48 +00:00
c17dc1c057 fix(7837): MetadataCollector takes no parameters for the constructor.
MetadataCollector no longer requires a ts.LanguageService parameter
it didn't use.

Closes #7838
2016-04-07 21:38:07 +00:00
09a95a692e docs(cheatsheet/dart): fix imports
Closes #7872
2016-04-07 21:23:14 +00:00
247964af62 fix(upgrade): make upgradeAdapter upgrade angular 1 components correctly
With this fix, the $onInit function of an upgraded angular 1 component is called and input bindings (<) are created.

Closes #7951
2016-04-07 20:19:46 +00:00
5e2bc5c593 fix(RouterLink): ignore optional parameters when checking for active routes
fixes #6459
Closes #7834
2016-04-07 19:41:14 +00:00
28e657d857 fix(payload): increase payload size limit temporarily 2016-04-07 11:42:13 -07:00
06ad112998 docs(changelog): update change log to beta.14 2016-04-07 10:19:35 -07:00
cfa1d17afe chore(release): bump version to beta.14 2016-04-07 10:09:46 -07:00
3ca6df87b8 fix(select): update name from ng-value to ngValue
Closes #7939
2016-04-06 22:47:21 +00:00
e310bee9e2 feat(dart/transform): Avoid print in transformer code.
Replace direct uses of `print` in the transformer with explicit uses of
stderr.

Add a few @override annotations for clarification of other `print`
implementations.

Clarify error message on incorrect custom_annotations value.

Closes #7855
2016-04-06 22:31:08 +00:00
4902244cce fix(router): allow forward slashes in query parameters
Closes #7824
2016-04-06 22:01:12 +00:00
8db97b0b7a fix(forms): support both value and ng-value 2016-04-06 14:37:57 -07:00
9be04f8d38 fix(upgrade): leak when angular1 destroys element
Fixes #6401

Closes #7935
2016-04-06 19:58:10 +00:00
74e2bd7e3e fix(select): support objects as select values
Closes #4843

Closes #7842
2016-04-06 17:05:38 +00:00
52d3980d02 Revert "feat(transformers): changes transformers to collect information about providers and resolve identifiers during linking"
This reverts commit 4e9809bcb2.
2016-04-06 09:26:03 -07:00
4e9809bcb2 feat(transformers): changes transformers to collect information about providers and resolve identifiers during linking
Closes #7380
2016-04-04 22:59:43 +00:00
bd8a4215dd refactor(core): remove @Injectable as only classes that are instantiated via DI need it 2016-04-04 22:59:43 +00:00
d23b973e7a refactor(forms): extract Validators.required into a variable as transformers cannot resolve statics 2016-04-04 22:59:43 +00:00
0dbf959548 feat(static-reflector): Added StaticReflector
Added a static reflector that uses metadta produced during build
or, additionally, directly from typescript, to produce the metadata
used by Angular code generation compiler.
2016-04-01 14:45:43 -07:00
20812f446f docs(changelog): fix formatting 2016-03-30 18:10:41 -07:00
27a4d0ce11 docs(changelog): update change log to beta.13 2016-03-30 17:23:38 -07:00
9dec4c7485 chore(release): bump version to beta.13 2016-03-30 17:21:51 -07:00
90c87fa6ad fix(codegen): stringify using an opaque ID when toString contains parens.
Using toString results in 'function (_arg1, arg2) {' when using closure compiler for 6-to-5.

Closes #7825
2016-03-30 23:43:35 +00:00
291928feb1 chore(scripts): log out of npm 2016-03-30 16:20:21 -07:00
c9c52fb353 build(pubspec): Clean up pubspec files
Add a direct dependency on package:path.
Remove dependency on package:quiver
2016-03-29 14:30:00 -07:00
0bcfcde63d fix(Router): handling of special chars in dynamic segments
Closes #7804
2016-03-29 20:24:28 +00:00
1c20a62611 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-29 10:55:06 -07:00
8430927e6b feat(i18n): update transformers to read a xmb file when provided and use I18nHtmlParser in this case
Closes #7790
2016-03-28 19:54:13 +00:00
d2ca7d81c8 feat(i18n): reexport I18nHtmlParser through the i18n barrel 2016-03-28 19:54:13 +00:00
756121acc1 feat(i18n): update I18nHtmlParser to accept parsed messages 2016-03-28 19:54:13 +00:00
d7e1175df0 feat(i18n): implement xmb deserialization 2016-03-28 19:54:12 +00:00
66cd84e0d5 refactor(i18n): rename serialize into serializeXmb 2016-03-28 19:54:12 +00:00
506f4ce1e5 feat(compiler): Resolvers now use DI to create reflector
Also introduced ReflectorReader when only read-only access to the reflector
is needed.

Closes #7762
2016-03-28 13:52:32 -05:00
a0387d2835 doc(Router): improve the example for routerOnActivate 2016-03-28 10:25:03 -07:00
9bdd5951d9 docs(forms): update the docs to reflect the current behavior
Closes #6504
2016-03-28 10:22:24 -07:00
111afcdff1 fix(build): MetadataCollector correctly collects property metadata
Fixes #7772

Closes #7773
2016-03-25 21:52:06 +00:00
85f3dc2fb5 chore(build): Produce .d.ts files for build tools
Closes #7763
2016-03-25 18:23:00 +00:00
430f367c2f fix(upgrade): make ngUpgrade work with testability API
Closes #7603
2016-03-25 17:27:45 +00:00
d272f96e23 feat(i18n): implement an i18n-aware html parser
Closes #7738
2016-03-24 20:36:19 +00:00
73a84a7098 refactor(i18n): remove utility functions into a separate file 2016-03-24 20:36:19 +00:00
17c8ec8a5d feat(html_parser): change HtmlElementAst to store both the start and the end positions 2016-03-24 20:36:19 +00:00
a1880c3576 feat(facade): add ListWrapper.flatten 2016-03-24 20:36:19 +00:00
91999e016e feat(facade): add RegExpWrapper.replaceAll to replace all matches using the provided function 2016-03-24 20:36:19 +00:00
aa966f5de2 feat(Compiler): Allow overriding the projection selector
fixes #6303

BREAKING CHANGE:

For static content projection, elements with *-directives are now matched against the element itself vs the template before.

    <p *ngIf="condition" foo></p>

Before:

    // Use the implicit template for projection
    <ng-content select="template"></ng-content>

After:

    // Use the actual element for projection
    <ng-content select="p[foo]"></ng-content>
Closes #7742
2016-03-24 20:09:34 +00:00
3e593b8221 chore(test.typings): instrument against examples folder
chore(typing_spec): delete unused typing_spec files

Closes #7743
2016-03-24 19:25:07 +00:00
440aca86a3 chore(examples): fix implied imports in examples for testing built typings 2016-03-24 19:25:07 +00:00
09f4d6f52d chore(refactor): Refactored metadata collector
Renamed MetadataExtractor to MetadataCollector
Reorganized to split src from tests

Closes #7492
2016-03-24 18:52:06 +00:00
3f57fa6e0e chore(build): Added tests for metadata extractor
Adds unit test to metadata extractor classes
Fixes issues found while testing
2016-03-24 18:52:06 +00:00
ae876d1317 feat(build): Persisting decorator metadata
This allows determing what the runtime metadata will be for a
class without having to loading and running the corresponding
.js file.
2016-03-24 18:52:06 +00:00
6de68e2f1f feat(compiler): assert that Component.style is an array
Part of #7481 (effort to improve error messages)

Closes #7559
2016-03-24 15:21:16 +00:00
49527ab495 fix(ngFor): give more instructive error when binding to non-iterable
Before, you'd get an error like:

```
EXCEPTION: Cannot find a differ supporting object ‘[object Object]’ in [users in UsersCmp@2:14]
```

Now, you get:

```
EXCEPTION: Cannot find a differ supporting object ‘[object Object]’ of type 'Object'. Did you mean to bind ngFor to an Array? in [users in UsersCmp@2:14]
```
2016-03-24 15:21:16 +00:00
0898bca939 chore: bump version to beta.12 w/ changelog 2016-03-23 15:56:38 -07:00
d940387beb chore: upgrade zone.js to v0.6.6
Closes #7730
Closes #7737
2016-03-23 20:48:23 +00:00
27c45b05cf Update ISSUE_TEMPLATE.md 2016-03-23 12:53:10 -07:00
90f09d551f Update ISSUE_TEMPLATE.md 2016-03-23 12:50:01 -07:00
3739588e97 chore: build cjs code prior to launching e2e tests
Closes #6783

Closes #7725
2016-03-22 22:09:33 +00:00
0730b753f2 chore(ddc): add e2e test infra + first test 2016-03-22 22:09:33 +00:00
cad693de0f refactor(NgZoneImpl): ensure zone spec is available
trace could be true (in dev mode) while there is no long stack trace spec
Closes #7702
2016-03-22 18:46:12 +00:00
bf911fc992 chore: upgrade zone.js@0.6.5
Closes #7700
2016-03-22 08:21:10 -07:00
fb6d791ce9 test(angular1_router): check that link generation works with baseHref
Closes #7489
2016-03-22 02:19:09 +00:00
0f8efce799 fix(angular1_router): support link generation with custom hashPrefixes 2016-03-22 02:19:09 +00:00
69c1405196 fix(angular_1_router): ng-link is generating wrong hrefs
At the moment ng-link is generating html5mode URLs for `href`s.
Instead it should check whether or not html5mode is enabled and create
the `href`s accordingly. The renaming in the `getLink` function is
aligning it to `RouterLink`'s `_updateLink`.

Closes #7423
2016-03-22 02:19:09 +00:00
980491b08f chore(angular1_router): tighten up the build regex replacement 2016-03-22 02:19:09 +00:00
72e24663ad refactor(dart/transform): Migrates tests to use package:test
Pt 4 of migrating from package:guinness + package:unittest =>
package:test.

This PR migrates DeferredRewriter & DirectiveMetadataLinker unit tests.

Closes #7703
2016-03-22 02:13:16 +00:00
363ed5140e docs(bundles/overview.md): fix typo
Closes #7677
2016-03-22 01:53:35 +00:00
b0f585ab08 build(npm): bump Angular version in our shrinkwrap files 2016-03-22 01:53:35 +00:00
8b67b07580 fix(package.json): remove es6-promise from the peerDependency list
As of zone.js@0.6* we no longer require an es6-promise polyfill. The polyfill is only needed on browsers that don't
have native Promise support.
2016-03-22 01:53:35 +00:00
5c330ea492 chore(publish): run router publish with additional memory
Closes #7408
2016-03-22 00:20:08 +00:00
06eaaf0ac5 chore(angular1_router): bump version to v0.2.0 2016-03-22 00:20:08 +00:00
9820271243 chore(script): publish angular 1 router to latest tag in npm 2016-03-22 00:20:08 +00:00
b6507e37ef feat(dart/transform): Use angular2/platform/browser as bootstrap lib
Update the Angular 2 transformer to recognize
`package:angular2/platform/browser.dart` as the library which exports
the `bootstrap` function.

Update playground, examples, benchmarks, & tests to import bootstrap from
platform/browser.

Closes #7647
2016-03-21 00:58:17 +00:00
c194f6695d chore: bump version to beta.11 w/ changelog 2016-03-18 14:42:31 -07:00
967ae3e1b8 fix(common): remove @internal annotation on SwitchView
Closes #7657
2016-03-18 13:16:50 -07:00
a5e6eaaebc chore: upgrade es6-shim to v0.35.0
Closes #7653
2016-03-18 13:16:15 -07:00
d4e9b55fb6 fix: make sure that Zone does not show up in angular2.d.ts
Closes #7655
2016-03-18 19:41:30 +00:00
048bd280dd chore: re-enable all tests (accidental ddescribe checkin) 2016-03-18 19:41:30 +00:00
8326ab3240 feat(i18n): add a simple dart script extracting all i18n messages from a package
Closes #7620
2016-03-17 16:45:16 -07:00
a7fe983be2 feat(i18n): create i18n barrel 2016-03-17 16:45:15 -07:00
e1f8e54e34 feat(i18n): implement xmb serializer 2016-03-17 16:45:15 -07:00
2b165944ea refactor(i18n): move message and id into a separate file 2016-03-17 16:45:15 -07:00
ea11b3f1f8 docs(changelog): update change log to beta.10 2016-03-17 15:06:28 -07:00
3bd87147ab chore(release): bump version to beta.10 2016-03-17 15:06:27 -07:00
2f581ffc88 fix(router): RouterOutlet loads component twice in a race condition
Closes #7497

Closes #7545
2016-03-16 22:34:54 +00:00
d61aaac400 chore(): remove all angular2_material code. 2016-03-16 13:37:37 -07:00
310620fd12 chore: upgrade to new Zone.js API v0.6.2
BREAKING CHANGE

Removed deprecated API from NgZone
- `NgZone.overrideOnTurnStart`
- `NgZone.overrideOnTurnDone`
- `NgZone.overrideOnEventDone`
- `NgZone.overrideOnErrorHandler`

Rename NgZone API
- `NgZone.onTurnStart` => `NgZone.onUnstable`
- `NgZone.onTurnDone` => `NgZone.onMicrotaskEmpty`
- `NgZone.onEventDone` => `NgZone.onStable`

Closes #7345
2016-03-16 18:05:09 +00:00
f9fb72fb0e chore(core): remove @View annotation
Closes #7495
2016-03-14 23:26:20 +00:00
095db673c5 feat(i18n): implement a simple version of message extractor
Closes #7454
2016-03-14 21:50:00 +00:00
70d18b5b53 feat(compiler): change html parser to preserve comments 2016-03-14 21:50:00 +00:00
f1796d67f4 feat(facade): add .values to StringMapWrapper 2016-03-14 21:50:00 +00:00
cb38d72ff4 feat(shadow_css): support /deep/ and >>>
Fixes #7562

Closes #7563
2016-03-11 22:14:26 +00:00
b72bab49aa feat(core): introduce a CSS lexer/parser 2016-03-11 13:54:01 -08:00
201475e8d8 cleanup(testing): clean up public api spec
Change the old public api spec to check only the exported top-level symbols. This will make sure that Dart and JS do not diverge. The new public api spec verifies the TS api.

Closes #7447
2016-03-11 19:24:29 +00:00
c25b9fcf97 docs(core): update <content> to <ng-content> 2016-03-11 11:19:52 -08:00
8755a8e188 docs(): fix typo 2016-03-11 11:18:59 -08:00
127fbfd5a6 Revert "feat(core): introduce a CSS lexer/parser"
This reverts commit 293fa5505b.

The rebased commit broke CI: https://travis-ci.org/angular/angular/jobs/115388814
2016-03-11 11:14:58 -08:00
f33dda79e9 chore(build): disable broken FirefoxBeta on SauceLabs
See #7560.
2016-03-11 10:57:44 -08:00
293fa5505b feat(core): introduce a CSS lexer/parser 2016-03-11 10:42:29 -08:00
df1f78e302 feat(i18n): add ngPlural directive 2016-03-10 09:55:21 -08:00
43bb31c6c6 refactor(dart/transform): Use targeted transformers
TL;DR: Modify pubspec.yaml files to use the recommended "targeted"
transformers. The unified "simple" angular2 transformer still works as
always, but we want to encourage use of the targeted transformers
whereever possible.

See [the wiki](https://github.com/angular/angular/wiki/Advanced-Transformer-Configuration)
for details about targeted transformers.

See #1872
2016-03-10 09:30:47 -08:00
169869a195 test(matchers): add support for toMatchPattern in tests 2016-03-09 21:31:15 -08:00
b691da26af chore(facade): add enum index lookup support 2016-03-09 21:30:03 -08:00
8e3e45097a fix(router): handle URL that does not match a route
Closes #7349
Closes #7203
2016-03-09 20:48:52 -08:00
aa43d2f87b docs(changelog): update change log to beta 9 2016-03-09 16:37:30 -08:00
128acbb6eb fix(change_detection): fix a memory leak 2016-03-09 16:24:51 -08:00
5824866a83 fix(closure): don't throw from top-level
workaround for http://b/27151095
2016-03-09 16:22:35 -08:00
0d58b137a7 fix(router/instruction): ensure toLinkUrl includes extra params
Closes #7367
2016-03-09 16:21:43 -08:00
b5c769e1e4 chore(release): bump version to beta.9 2016-03-09 14:38:23 -08:00
7f22bd62ab test(angular_1_router): apply annotations to controller constructors
Until Angular 1.5.1 is released, the `$routeConfig` and `$routerCanActivate`
annotations for components must live on the controller constructor.

In Angular 1.5.1, it will automatically copy these annotations across from
the component definition file.

Closes #7319
2016-03-09 21:50:24 +00:00
83f0e7c975 test(angular_1_router): fix router_spec tests
These tests were registering new components after the application had
been bootstrapped, which is not a valid use case for synchronous routes
in Angular 1.

In particular it was registering the "root" component, which caused the
`$rootRouter` to blow up, when it was instantiated, pointing to a root
component that did not yet exist.
2016-03-09 21:50:24 +00:00
adef68b4d6 refactor(angular_1_router): remove directiveIntrospector
The directiveIntrospector was a bit of a hack to allow the router to
read the `$routeConfig` annocation and `$routerCanActivate` hook from
directives when they were registered.

It turns out that if we put these properties on the component controller's
constructor function (i.e. as static class methods) then we can simply
use the `$injector` to access it as required.

Currently, people put the properties directly on their component definition
objects. In Angular 1.5.1, we will copy these properties onto the controller
constructor to maintain a simple migration path. But going forward it may be
better to encourage people to add the properties directly to the controller
constructor.
2016-03-09 21:50:24 +00:00
14f0e9ada8 chore: fix DDC errors / warnings
Closes #7195
2016-03-08 22:17:32 +00:00
ef9e40e82b refactor(dart/transform): Migrates tests to use package:test
Pt 3 of migrating from package:guinness + package:unittest => package:test.

This PR migrates DirectiveProcessor & InlinerForTest unit tests.

Closes #7475
2016-03-08 01:39:23 +00:00
41e38e4330 fix hammer_gestures infinite loop 2016-03-08 01:37:40 +00:00
2c7c3e3c69 feat(TAG_DEFINITIONS): include <meta> and <base>
needed to parse index.html as a component template
Closes #7455
2016-03-08 01:03:20 +00:00
756f5d884f refactor(dart/transform): AnnotationMatcher tests
These were previously not being run.

Bring them up to modern usage, move them to package:test, and include
them in transform.server.spec.dart.

Closes #7463
2016-03-08 00:47:35 +00: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
cee2318110 feat(ngFor): add custom trackBy function support
Make it possible to track items in iterables in custom ways (e.g. by ID or index), rather than simply by identity.

Closes #6779
2016-02-03 01:03:31 +00:00
cfef76f683 refactor(dart/transform): Error in name convert funcs on unexpected input
Issue raised in PR #6745.
Previously, the transformer name conversion functions could return the
input string on unexpected input, which is almost certainly an error.

`throw` in this case instead, so we know early that something has likely
gone wrong.

Closes #6753
2016-02-02 23:46:27 +00:00
f56df65d48 perf(dart/transform): Only process deferred libs when necessary
Previously, every .dart file in a package was processed to ensure proper
initialization of deferred loaded libraries.

Update the transformer to avoid processing libraries which we know do
not import any deferred libraries.

Closes #6745
2016-02-02 23:06:36 +00:00
3a40cd79f0 build(router): make the build.js script portable to g3 2016-02-02 13:58:51 -08:00
6acc99729c build(router): refactor angular1 router build script 2016-02-02 13:58:51 -08:00
99e6500a2d feat(upgrade): support bindToController with binding definitions
Since angular 1.4 we can also pass controller bindings directly to bindToController, making this syntax more convenient

Closes #4784
2016-02-02 13:27:22 -08:00
5c782d6ba8 Typo: Hash_consing wiki link wrong Markdown syntax 2016-02-02 11:53:30 -08:00
4e43d6f769 docs(http): Added base request options for test example 2016-02-02 11:51:42 -08:00
3529ee9973 docs(cheatsheet): change as to name in routing section 2016-02-02 11:43:29 -08:00
29aa6a6c1c change event to use camel case 2016-02-02 11:42:26 -08:00
7918f3c1fc docs(changelog): fix header for 2.0.0-beta.0 2016-02-02 11:39:23 -08:00
2f4e176054 docs(developer): add linting instructions 2016-02-02 11:34:00 -08:00
d4565fdaf3 Update CHANGELOG.md
Fixed phrasing on breaking changes. It's just one breaking change so just requires one bullet point to explain the before/after.
2016-02-02 11:24:35 -08:00
2a302aa73a fix(docs): rxjs/add/operators/map -> rxjs/add/operator/map (no 's').
Was it with 's' before?
2016-02-02 11:22:10 -08:00
31b819e9c2 test: fix transformer tests 2016-02-02 10:21:40 -08:00
27daeaff5e fix(karma): fix running karma via gulp
As described below, the karma server showdown process can crash, if the done() function, provided
by gulp is referenced directly instead of wrapped in a closure function
http://stackoverflow.com/questions/26614738/issue-running-karma-task-from-gulp

Reformat the gulpfile.js

Squashing the two commits
2016-02-02 06:22:53 -08:00
3e9b532409 fix(dart/transform): Handle edge cases in ReflectionRemover
Handle some cases which would previously result in broken code.

- Importing bootstrap.dart deferred
- Using combinators when importing bootstrap.dart
- Importing bootstrap.dart with a prefix

Closes #6749
2016-02-01 15:17:44 -08:00
c5aa6d17ef docs: Fix small formatting issue in changeling
Closes #6660
2016-02-01 15:16:35 -08:00
e480b0798e docs: Kebab-case does not work. Change to camelCase.
Closes #6630
2016-02-01 15:15:46 -08:00
8a645d5e44 chore(zone.js): update to 0.5.11
Closes #6751
2016-02-01 21:53:42 +00:00
321193889f fix(zone): correct incorrect calls to zone 2016-02-01 21:53:42 +00:00
566d3ede04 test(dart/transform): Update unit tests to expect code in <file>.template.dart
Closes #6711
2016-02-01 21:21:38 +00:00
8c36aa866a feat(dart/transform): Generate all code into <file>.template.dart
Previously, we generated the code to initialize the reflector into the
<file>.ng_deps.dart and the compiled template and change detector code
into <file>.template.dart.

Update the transformer to generate all code into <file>.template.dart to
avoid the additional HTTP requests necessary when debugging
applications in Dartium.
2016-02-01 21:21:37 +00:00
ed2dbf2db7 ci(dart): comment out dart.dev build
See #6823
2016-02-01 12:53:27 -08:00
36a0e04604 fix(circle): pre-dependencies npm install npm
Fixes #6777
2016-02-01 11:26:25 -08:00
8867afdaab fix(build): Revert "build(dart): Make Dart dev build required"
This reverts commit a199772508.
2016-02-01 11:17:57 -08:00
a199772508 build(dart): Make Dart dev build required
Previously, we allowed the Dart dev build to fail presubmit tests.
Make it a required build from now on.

Closes #6410
2016-02-01 07:12:40 -08:00
b008f542fa chore(changelog): improve breaking change description for beta.2 2016-01-30 19:45:17 -08:00
a78dcfa5f3 chore(tests): fix broken linker integration test and fix DebugNode export
Fixes two small issues introduced with pr #6555
2016-01-29 14:29:49 -08:00
e1bf3d33f8 feat(debug): replace DebugElement with new Debug DOM
Now, using `ng.probe(element)` in the browser console returns
a DebugElement when in dev mode.

`ComponentFixture#debugElement` also returns a new DebugElement.

Breaking Change:

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;
```
2016-01-29 11:28:10 -08:00
ae7d2ab515 fix(bundle): add angular2/platform/testing/browser to SystemJS testing bundle 2016-01-29 13:58:06 +01:00
c6adbf602c fix(query): don’t cross component boundaries
Closes #6759
2016-01-28 23:38:40 +00:00
1f7a41c963 fix(query): update view queries that query directives in embedded views
Fixes #6747
2016-01-28 14:40:53 -08:00
f4f614f3a9 docs(changelog): update change log to beta.2 2016-01-28 12:01:18 -08:00
94139c351f chore(release): bump version to beta.2 2016-01-28 12:01:18 -08:00
fc5b128b43 chore(ci): deflake test and turn on saucelabs_required
Fixes #6725

Closes #6733
2016-01-28 03:18:08 +00:00
68a799af2e tools: implement public api spec
Closes #6309
2016-01-27 21:19:05 +00:00
16d9c60a0e chore(circleci): use a github token when running tsd
Also increase the heap size limit as a workaround for
https://github.com/angular/angular/issues/5229

Fixes #6602
2016-01-27 13:16:57 -08:00
c0b5e7a672 chore(ci): turn off saucelabs_required
Reenable once #6723 fixed.
2016-01-27 11:52:32 -08:00
6932b29acb chore(ci): bump up the error count for dart DDC. 2016-01-27 11:19:56 -08:00
c2a38c05aa fix(WebWorkers): Add support for transitionend events.
Closes #6649
2016-01-26 21:09:01 -08:00
8bea667a0b feat(WebWorker): Add Router Support for WebWorker Apps
Closes #3563.
2016-01-26 21:07:12 -08:00
800c8f196f chore(ddc): make DDC build non-experimental 2016-01-26 21:04:34 -08:00
42231f5719 feat(change_detection): allow all legal programs in the dev mode
BEFORE:

The following would throw in the dev mode because `f` would return a new array when called by checkNoChanges.

@Component({
  template: `
    {{f()}}
  `
})
class A {
  f() { return [1]; }
}

AFTER:

The checkNoChanges function compares only primitives types for equality, and deeply compares iterables. Other objects cannot cause checkNoChanges to throw. This means that the dev mode would never fail given a legal program, but may allow some illegal programs.
2016-01-26 21:01:19 -08:00
db87baeb98 fix(ddc): router, compiler, web worker fixes for DDC
Also enable DDC checks across all non-web worker playground apps. We are
now down to 2 DDC errors across all of them. The remaining two need to be
fixed in package:analyzer, not in angular.

BREAKING CHANGE:

- there's a chance of breakage as router's Instruction constructor
  signature changed.

Closes #6693
2016-01-27 02:30:20 +00:00
c4c43f5a77 docs(contributing.md): add docs for "ci" commit type/scope 2016-01-26 14:55:42 -08:00
0ae77753f3 fix(core): always remove DOM listeners and stream subscriptions
This is needed to prevent memory leaks. The DOM
listeners don’t need to be removed for simple examples,
but a big internal app shows memory leaks because of them.

BREAKING CHANGE:
- `Renderer.listen` now has to return a function that
  removes the event listener.
2016-01-26 07:37:31 -08:00
5f0baaac73 fix(dart/transform): Ensure template codegen is completed sync
Previously, template codegen was done asynchronously, which could result
in reflector state being overwritten and leading to compile errors.

Update the codegen to run synchronously to ensure this does not happen.

Closes #6603
2016-01-26 04:18:11 +00:00
b5b6ece65a chore(ddc): reduce DDC warning cap 260->180 as we fixed a bunch
Closes #6625
2016-01-26 03:58:22 +00:00
4282297c24 fix(ddc): type fixes necessary to bring DDC severe count to 0 2016-01-26 03:58:22 +00:00
9c96b8affc chore: track size of a "Hello world" app built with SystemJS
Closes #6621
2016-01-26 02:54:03 +00:00
132829e5e2 chore(core): deactivate the tests that use Dart isolates
These are broken with Dart 1.13.2 stable, and will soon be obsolete (see #6270).
2016-01-25 17:20:06 -08:00
4a414420e9 fix(bundles): testing bundle should include browser platform
Closes #6626
2016-01-22 06:12:58 +00:00
fb6335ab60 build(gulp): fail hard w/ legacy node version and provide clear upgrade instructions
Closes #6213
2016-01-22 03:25:34 +00:00
89bd008445 docs(DEVELOPER.md): update framework developer instructions 2016-01-22 03:25:34 +00:00
caafb41eb5 build(node): upgrade to node 5.4.1 2016-01-22 03:25:34 +00:00
31b81a7439 build(npm): update to karma-browserstack-launcher@0.1.9 2016-01-22 03:25:34 +00:00
f7b1973358 build(npm): bump Angular's version number in shrinkwrap files to 2.0.0-beta.1 2016-01-22 03:25:34 +00:00
32f01da49a build(npm): update to karma@0.13.17
Mark says that karma-runner/karma#1768 is resolved in 0.13.17
2016-01-22 03:25:34 +00:00
59684c97b0 build(npm): update to minimatch@3.0.0 2016-01-22 03:25:34 +00:00
a32a0a3a97 build(npm): update to semver@5.1.0 2016-01-22 03:25:33 +00:00
96f5b0929d build(npm): lock karma at 0.13.15 due to karma-runner/karma#1768 2016-01-22 03:25:33 +00:00
8e6cf7fca8 build(npm): update to yargs@3.31.0 2016-01-22 03:25:33 +00:00
fdbe8741c9 build(npm): update to proxy-middleware@0.15.0 2016-01-22 03:25:33 +00:00
775fb2c340 build(npm): remove googleapis
it was previously used by benchpress (see d02c0accbb) but that's no longer the case.

I also removed a bunch of extranous dependencies that should never have been part of node_modules (npm bug?)
2016-01-22 03:25:33 +00:00
b60f594798 build(npm): update to gulp-connect@2.3.1 2016-01-22 03:25:33 +00:00
cc49790bdb build(npm): update gulp-connect's dependencies 2016-01-22 03:25:33 +00:00
a4bc19c530 build(npm): lock broccoli-slow-trees to the 1.x.x range 2016-01-22 03:25:33 +00:00
f7985dbdb7 build(npm): update to lodash@3.10.1 2016-01-22 03:25:33 +00:00
0bdcb5c1e0 build(npm): remove del devDependency - use fs-extra instead 2016-01-22 03:25:33 +00:00
a0d25db4a5 build(npm): add tools/npm/reshrinkwrap script and update docs 2016-01-22 03:25:33 +00:00
625474c4e2 build(npm): update to mock-fs@3.6.0 2016-01-22 03:25:33 +00:00
1cd2a6328a build(npm): update to fs-extra@0.26.3 2016-01-22 03:25:33 +00:00
d6bafe4fe3 build(npm): update to minimist@1.2.0 2016-01-22 03:25:33 +00:00
6a2ef15355 build(npm): update to jasmine@2.4.1 2016-01-22 03:25:33 +00:00
a8ca560503 build(npm): upgrade to broccoli@0.16.9 2016-01-22 03:25:33 +00:00
ad361808ec build(npm): upgrade to broccoli-funnel@1.0.1 2016-01-22 03:25:33 +00:00
c47639f2b1 build(npm): upgrade to protractor@3.0.0 2016-01-22 03:25:33 +00:00
ba90a85f7b build(npm): downgrade to karma@0.13.15 due to RangeError bug
Ref https://github.com/karma-runner/karma/issues/1768
2016-01-22 03:25:33 +00:00
d3b569557f build(npm): upgrade to karma-sauce-launcher@0.3.0 2016-01-22 03:25:33 +00:00
c9090ffa31 build(npm): revert clang-format upgrade
there are too many reformatting changes require that would add noise to the upgrade commit
2016-01-22 03:25:33 +00:00
341bf39d23 build(npm): update all npm dependencies + reshrinkwrap 2016-01-22 03:25:32 +00:00
3778ac26aa build(tools): npm/copy-npm-shrinkwrap should gracefully handle situation when shrinkwrap file is missing
This situation occurs during mas update of all dependencies, so we should not throw errors when this happens.
2016-01-22 03:25:32 +00:00
6cfc6f5bb2 build(npm): upgrade to npm v3
Closes #3193
2016-01-22 03:25:32 +00:00
47a3b4d56b feat(dart/transform): Promote missing Directive warning to error
Closes #6519

Closes #6568
2016-01-22 00:46:56 +00:00
c72ed991ad fix(testing): remove test zone for now and rely on returned promises
Adds tests for public Dart and TS frameworks to make sure that
components with templateUrl can be created by the TestComponentBuilder.

Closes #6359

Closes #6601
2016-01-22 00:28:48 +00:00
78bfdf78ea feat(dart/transform): DirectiveProcessor: do not process generated files
Prevent `DirectiveProcessor` from processing files which were generated
by the Angular2 Dart transformer.

Closes #6517
2016-01-22 00:21:27 +00:00
a24ee6add4 fix(HtmlLexer): fix for unicode chars
fixes #6036
Closes #6061
2016-01-21 23:45:41 +00:00
df3074fdfe feat(core/application_ref): Allow asyncronous app initializers.
closes #5929.

Closes #6063
2016-01-21 01:45:24 +00:00
f7424d5aeb chore: track size of a "Hello world" app built with WebPack
Closes #6434
2016-01-21 01:28:35 +00:00
a593ffa6f3 fix(transformer): record HostBinding annotations applied to getters
Closes #6283
2016-01-21 01:02:56 +00:00
761c6d0df7 fix(perf): faster looseIdentical implementation
Remove String type check in looseIdentical in JS-mode. It is not necessary as dart2js already compiles `identical` to `===` which compares string contents. Inline call sites.

This improves change detection of plain fields by 40%. On a large internal app the improvement is 5%.

Closes #6364
2016-01-21 01:01:36 +00:00
3e65d1458e fix(Dart): make some playground samples run with Dart Dev Compiler
Resolve all invalid field override errors, workaround current
reflection limitations in Dart Dev Compiler. todo, hello_world and
key_events samples now work with Dart Dev Compiler.

BREAKING CHANGE: remove TemplateRef.elementRef setter

Closes #6441
2016-01-21 00:41:42 +00:00
a4b5cb8376 build(node): split test and src compilation units 2016-01-19 21:15:16 -08:00
c785a1e474 fix(ddc): use dynamic types in reflection typedefs
Closes #6437
2016-01-19 21:49:11 +00:00
3adc472f06 chore(build): fix race condition for the !bundles.js.docs task 2016-01-19 11:40:30 -08:00
e7081b8b7c chore: don't track size of non-bundle files 2016-01-19 11:32:40 -08:00
9b3a548f6f docs(template_parser.ts): typo 2016-01-19 10:58:04 -08:00
90b3502bb8 ci(circle config): add a circle CI config
This only runs the JS build (no tests) as an easy place to start.

Green build on my branch: https://circleci.com/gh/alexeagle/angular/5

Note, we are just experimenting with Circle at this point...

Closes #6520
2016-01-16 00:17:15 +00:00
e19b31db29 refactor(test): Remove unnecessary noSuchMethod
Remove trivial implementations in many spy objects which just calls the
parent's `noSuchMethod`.

Closes #6410

Closes #6491
2016-01-15 22:53:09 +00:00
bd015f14e8 build(dartanalyzer): Ignore TODOs during build
The newest version of the analyzer emits hints when it encounters TODOs
in code, which is breaking the Dart dev version of our build.

Ignore TODOs for the purpose of build health.

See #6410
2016-01-15 22:53:09 +00:00
ca7ba12fc6 chore(travis): update name of sync branch to be ignored
Closes #6518
2016-01-15 22:26:40 +00:00
ae05ec69c4 Update overview.md
Closes #6478
2016-01-15 17:44:30 +00:00
92dc3b91d8 doc(*): change package.json license field to MIT
Align the package.json license with the LICENSE file from the repo
Closes #6432
2016-01-15 17:42:43 +00:00
8bd697b316 docs(DEVELOPER): fix saucelabs gulp task name
Task is test.unit.js.sauce, not test.unit.js.saucelabs.

Closes #6435
2016-01-15 17:23:14 +00:00
eda4c3eb4c fix(template_compiler): Fix erroneous cycle detection
Before, the check for cycles was wrong and lead to false positives.

Fixes #6404

Closes #6474
2016-01-14 23:08:30 +00:00
4d0c2ed1f6 test(dart/transform): Update dependencies & fix Dart tests
Widen version dependencies for `package:angular`, `package:code_transformers`,
and `package:observe`.

`package:guinness` uses `package:unittest` while
the newest versions of `package:code_transformers` use `package:test`.
This causes our end-to-end Dart transformer tests (which use testing
code in `package:code_transformers`) to be skipped.

To fix this:
- Move e2e tests to run in a separate file
- Run `gulp test.server.dart` tests serially

Closes #5922

Closes #5935
2016-01-14 00:29:03 +00:00
eda6a5d52a refactor(WebWorker): Rename WORKER_RENDER_APP to WORKER_RENDER_APPLICATION
BREAKING CHANGE

WORKER_RENDER_APP is now deprecated. Use WORKER_RENDER_APPLICATION instead
WORKER_RENDER_APP_COMMON has been replaced by WORKER_RENDER_APPLICATION_COMMON

closes #6184

Closes #6378
2016-01-14 00:07:13 +00:00
c1c54ed0f2 refactor(dart/transform): Avoid using package:code_transformers
Replace uses of `package:code_transformers`, which is only used to
convert from uri to `AssetId`, with calls to the utility methods in
`src/transform/common/url_resolver.dart`.

Closes #5931
2016-01-13 22:24:42 +00:00
6b73d09ba1 chore(build): make experimental Dart build useful
Previously we grepped all hand-written Dart code and ran analyzer in strong mode against it.

Now we run it against transformed playground apps, which:

1. does not analyze unnecessary code (we primarily care about stuff that runs in the browser)
2. analyzes generated code, which does run in the browser and which we failed to analyze in the previous version of the build

Closes #6436
2016-01-13 21:09:55 +00:00
ac85cbb28a fix(web_workers): support @AngularEntrypoint in web workers
And enable transformers on all playground apps

Closes #6013
2016-01-13 17:55:01 +00:00
b0cebdba6b feat(test): allow tests to specify the platform and application providers used
With providers split into bundles, the test injector is now able to
use providers for a given bundle. Suggested provider lists for tests are
available in `angular2/platform/testing/<platform>`.

Change the providers for a test suite using `setBaseTestProviders`. This
should be done once at the start of the test suite, before any test cases
run.

BREAKING CHANGE: Tests are now required to use `setBaseTestProviders`
to set up. Assuming your tests are run on a browser, setup would change
as follows.

Before:

```js
// Somewhere in test setup
import {BrowserDomAdapter} from 'angular2/src/platform/browser/browser_adapter';
BrowserDomAdapter.makeCurrent
```

After:

```js
// Somewhere in the test setup
import {setBaseTestProviders} from 'angular2/testing';
import {
  TEST_BROWSER_PLATFORM_PROVIDERS,
  TEST_BROWSER_APPLICATION_PROVIDERS
} from 'angular2/platform/testing/browser';

setBaseTestProviders(TEST_BROWSER_PLATFORM_PROVIDERS,
                     TEST_BROWSER_APPLICATION_PROVIDERS);
```

Closes #5351, Closes #5585

Closes #5975
2016-01-13 02:11:06 +00:00
933a9112da fix(ChangeDetection): chain expressions evaluate to the last expression (codegen)
fixes #4782
Closes #5892
2016-01-12 17:10:42 +00:00
8c37b7e8f2 fix(directive): throw if output the same event more than once
Close: #4798
2016-01-11 16:48:57 -08:00
c8e909f8c9 docs(cheatsheet): fix pipe name in an example
Closes #6399
2016-01-12 00:12:00 +00:00
69ae3634c7 feat(testability): Expose function frameworkStabilizers
Closes #5485
2016-01-11 23:10:51 +00:00
95248f46a1 build(npm): update to ts2dart@0.7.19
Closes #6254
2016-01-09 01:44:01 +00:00
b3c7df1783 docs(cheatsheet): fix bootstrap ts namespace
Fix https://github.com/angular/angular.io/issues/615
Closes #6159
2016-01-09 01:01:17 +00:00
c56679e8e1 Update change from Apache to MIT license 2016-01-08 13:41:58 -08:00
041c599511 docs(changelog): update change log to beta.1 2016-01-08 12:12:38 -08:00
2373 changed files with 167244 additions and 157971 deletions

3
.gitattributes vendored
View File

@ -3,3 +3,6 @@
# JS files must always use LF for tools to work
*.js eol=lf
# Must keep Windows line ending to be parsed correctly
scripts/windows/packages.txt eol=crlf

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

@ -0,0 +1,33 @@
**I'm submitting a ...** (check one with "x")
```
[ ] bug report
[ ] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
```
**Current behavior**
**Expected/desired behavior**
**Reproduction of the problem**
If the current behavior is a bug or you can illustrate your feature request better with an example, 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-rc.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]

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

@ -0,0 +1,36 @@
**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?** (check one with "x")
```
[ ] Bugfix
[ ] Feature
[ ] Code style update (formatting, local variables)
[ ] Refactoring (no functional changes, no api changes)
[ ] Build related changes
[ ] CI related changes
[ ] Other... Please describe:
```
**What is the current behavior?** (You can also link to an open issue here)
**What is the new behavior?**
**Does this PR introduce a breaking change?** (check one with "x")
```
[ ] Yes
[ ] No
```
If this PR contains a breaking change, please describe the impact and migration path for existing applications: ...
**Other information**:

7
.gitignore vendored
View File

@ -21,10 +21,6 @@ tmp
*.js.deps
*.js.map
# Or type definitions we mirror from github
**/typings/**/*.d.ts
**/typings/tsd.cached.json
# Include when developing application packages.
pubspec.lock
.c9
@ -48,3 +44,6 @@ npm-debug.log
# built dart payload tests
/modules_dart/payload/**/build
# rollup-test output
/modules/rollup-test/dist/

2
.nvmrc
View File

@ -1 +1 @@
4.2.1
5.4.1

View File

@ -1,113 +1,196 @@
language: node_js
sudo: false
node_js:
- '4.2.1'
- '5.4.1'
addons:
# firefox: "38.0"
apt:
sources:
# needed to install g++ that is used by npms's native modules
- ubuntu-toolchain-r-test
packages:
- g++-4.8
branches:
except:
- g3sync
- g3_v2_0
cache:
directories:
- node_modules
- $HOME/.pub-cache
- ./node_modules
- ./.chrome/chromium
# - $HOME/.pub-cache
#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="
# - KARMA_JS_BROWSERS=ChromeNoSandbox
# - E2E_BROWSERS=ChromeOnTravis
# - LOGS_DIR=/tmp/angular-build/logs
# - ARCH=linux-x64
# GITHUB_TOKEN_ANGULAR
# This is needed for the e2e Travis matrix task to publish packages to github for continuous packages delivery.
- 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
- 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_experimental 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
- CI_MODE=js
- CI_MODE=e2e
- CI_MODE=saucelabs_required
- CI_MODE=browserstack_required
- CI_MODE=saucelabs_optional
- CI_MODE=browserstack_optional
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"
- env: "MODE=dart_experimental 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: "CI_MODE=saucelabs_optional"
- env: "CI_MODE=browserstack_optional"
addons:
firefox: "38.0"
before_install:
- 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
install:
- node tools/analytics/build-analytics start ci install
# Check the size of caches
- du -sh ./node_modules || true
# Install npm dependecies
- npm install
- node tools/analytics/build-analytics success ci install
- ./scripts/ci-lite/install.sh
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
- ./scripts/ci-lite/build.sh && ./scripts/ci-lite/test.sh
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
- ./scripts/ci-lite/cleanup.sh
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/1ef62e23078036f9cee4
# trigger Buildtime Trend Service to parse Travis CI log
- https://buildtimetrend.herokuapp.com/travis
on_success: always # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: false # default: false
slack:
secure: EP4MzZ8JMyNQJ4S3cd5LEPWSMjC7ZRdzt3veelDiOeorJ6GwZfCDHncR+4BahDzQAuqyE/yNpZqaLbwRWloDi15qIUsm09vgl/1IyNky1Sqc6lEknhzIXpWSalo4/T9ZP8w870EoDvM/UO+LCV99R3wS8Nm9o99eLoWVb2HIUu0=
#branches:
# except:
# - g3_v2_0
#
#cache:
# directories:
# - $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:
# # 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
# - 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
#
#matrix:
# allow_failures:
# - env: "MODE=saucelabs_optional"
# - env: "MODE=browserstack_optional"
#
#addons:
# firefox: "38.0"
# apt:
# sources:
# - ubuntu-toolchain-r-test
# packages:
# - g++-4.8
#
#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
# # 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
# # we need to manually kick off the postinstall script if check-node-modules exit(0)s
# - node tools/npm/check-node-modules --purge && npm install || npm run postinstall
# - 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
#
#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
# - 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
# - 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: never # default: never
# slack:
# secure: EP4MzZ8JMyNQJ4S3cd5LEPWSMjC7ZRdzt3veelDiOeorJ6GwZfCDHncR+4BahDzQAuqyE/yNpZqaLbwRWloDi15qIUsm09vgl/1IyNky1Sqc6lEknhzIXpWSalo4/T9ZP8w870EoDvM/UO+LCV99R3wS8Nm9o99eLoWVb2HIUu0=

File diff suppressed because it is too large Load Diff

View File

@ -95,7 +95,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
* In GitHub, send a pull request to `angular:master`.
* If we suggest changes then:
* Make the required updates.
* Re-run the Angular 2 test suites for JS and Dart to ensure tests are still passing.
* Re-run the Angular 2 test suites to ensure tests are still passing.
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
```shell
@ -166,6 +166,19 @@ The **header** is mandatory and the **scope** of the header is optional.
Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
to read on GitHub as well as in various git tools.
Footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages/) if any.
Samples: (even more [samples](https://github.com/angular/angular/commits/master))
```
docs(changelog): update change log to beta.5
```
```
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.
```
### Revert
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
@ -180,7 +193,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)
* **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
@ -224,8 +238,8 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
[github]: https://github.com/angular/angular
[gitter]: https://gitter.im/angular/angular
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
[js-style-guide]: http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
[jsfiddle]: http://jsfiddle.net/
[js-style-guide]: https://google.github.io/styleguide/javascriptguide.xml
[jsfiddle]: http://jsfiddle.net
[plunker]: http://plnkr.co/edit
[runnable]: http://runnable.com/
[runnable]: http://runnable.com
[stackoverflow]: http://stackoverflow.com/questions/tagged/angular

View File

@ -5,15 +5,9 @@ JS and Dart versions. It also explains the basic mechanics of using `git`, `node
* [Prerequisite Software](#prerequisite-software)
* [Getting the Sources](#getting-the-sources)
* [Environment Variable Setup](#environment-variable-setup)
* [Installing NPM Modules and Dart Packages](#installing-npm-modules-and-dart-packages)
* [Build commands](#build-commands)
* [Installing NPM Modules](#installing-npm-modules)
* [Building](#building)
* [Running Tests Locally](#running-tests-locally)
* [Formatting](#clang-format)
* [Project Information](#project-information)
* [CI using Travis](#ci-using-travis)
* [Transforming Dart code](#transforming-dart-code)
* [Debugging](#debugging)
See the [contribution guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md)
if you'd like to contribute to Angular.
@ -23,30 +17,17 @@ if you'd like to contribute to Angular.
Before you can build and test Angular, you must install and configure the
following products on your development machine:
* [Dart](https://www.dartlang.org) (version ` >=1.12.0 <2.0.0`), specifically the Dart-SDK and
Dartium (a version of [Chromium](http://www.chromium.org) with native support for Dart through
the Dart VM). One of the **simplest** ways to get both is to install the **Dart Editor bundle**,
which includes the editor, SDK and Dartium. See the [Dart tools](https://www.dartlang.org/tools)
download [page for instructions](https://www.dartlang.org/tools/download.html).
You can also download both **stable** and **dev** channel versions from the [download
archive](https://www.dartlang.org/tools/download-archive). In that case, on Windows, Dart must be added
to the `Path` (e.g. `path-to-dart-sdk-folder\bin`) and a new `DARTIUM_BIN` environment variable must be
created, pointing to the executable (e.g. `path-to-dartium-folder\chrome.exe).`
* [Git](http://git-scm.com) and/or the **GitHub app** (for [Mac](http://mac.github.com) or
[Windows](http://windows.github.com)); [GitHub's Guide to Installing
Git](https://help.github.com/articles/set-up-git) is a good source of information.
* [Node.js](http://nodejs.org), (version `>=4.2.1 <5`) which is used to run a development web server,
* [Node.js](http://nodejs.org), (version `>=5.4.1 <6`) which is used to run a development web server,
run tests, and generate distributable files. We also use Node's Package Manager, `npm`
(version `>=2.14.7 <3.0`), which comes with Node. Depending on your system, you can install Node either from
(version `>=3.5.3 <4.0`), which comes with Node. Depending on your system, you can install Node either from
source or as a pre-packaged bundle.
* [Chrome Canary](https://www.google.com/chrome/browser/canary.html), a version of Chrome with
bleeding edge functionality, built especially for developers (and early adopters).
* [Bower](http://bower.io/).
* [Java Development Kit](http://www.oracle.com/technetwork/es/java/javase/downloads/index.html) which is used
to execute the selenium standalone server for e2e testing.
## Getting the Sources
@ -69,42 +50,7 @@ cd angular
# Add the main Angular repository as an upstream remote to your repository:
git remote add upstream https://github.com/angular/angular.git
```
## Environment Variable Setup
Define the environment variables listed below. These are mainly needed for the testing. The
notation shown here is for [`bash`](http://www.gnu.org/software/bash); adapt as appropriate for
your favorite shell.
Examples given below of possible values for initializing the environment variables assume **Mac OS
X** and that you have installed the Dart Editor in the directory named by
`DART_EDITOR_DIR=/Applications/dart`. This is only for illustrative purposes.
```shell
# DARTIUM_BIN: path to a Dartium browser executable; used by Karma to run Dart tests
export DARTIUM_BIN="$DART_EDITOR_DIR/chromium/Chromium.app/Contents/MacOS/Chromium"
```
Add the Dart SDK `bin` directory to your path and/or define `DART_SDK` (this is also detailed
[here](https://www.dartlang.org/tools/pub/installing.html)):
```shell
# DART_SDK: path to a Dart SDK directory
export DART_SDK="$DART_EDITOR_DIR/dart-sdk"
# Update PATH to include the Dart SDK bin directory
PATH+=":$DART_SDK/bin"
```
And specify where the pubs dependencies are downloaded. By default, this directory is located under .pub_cache
in your home directory (on Mac and Linux), or in AppData\Roaming\Pub\Cache (on Windows).
```shell
# PUB_CACHE: location of pub dependencies
export PUB_CACHE="/Users/<user>/.pub-cache"
```
## Installing NPM Modules and Dart Packages
## Installing NPM Modules
Next, install the JavaScript modules and Dart packages needed to build and test Angular:
@ -128,229 +74,67 @@ use in these instructions.
*Option 2*: defining a bash alias like `alias nbin='PATH=$(npm bin):$PATH'` as detailed in this
[Stackoverflow answer](http://stackoverflow.com/questions/9679932/how-to-use-package-installed-locally-in-node-modules/15157360#15157360) and used like this: e.g., `nbin gulp build`.
## Build commands
To build Angular and prepare tests, run:
## Windows only
In order to create the right symlinks, run **as administrator**:
```shell
$(npm bin)/gulp build
./scripts/windows/create-symlinks.sh
```
Notes:
* Results are put in the `dist` folder.
* This will also run `pub get` for the subfolders in `modules` and run `dartanalyzer` for
every file that matches `<module>/src/<module>.dart`, e.g. `di/src/di.dart`.
Before submitting a PR, do not forget to remove them:
```shell
./scripts/windows/remove-symlinks.sh
```
You can selectively build either the JS or Dart versions as follows:
## Building
* `$(npm bin)/gulp build.js`
* `$(npm bin)/gulp build.dart`
To clean out the `dist` folder, run:
To build Angular run:
```shell
$(npm bin)/gulp clean
./build.sh
```
* Results are put in the dist folder.
## Running Tests Locally
### Full test suite
* `npm test`: full test suite for both JS and Dart versions of Angular. These are the same tests
that run on Travis.
You can selectively run either the JS or Dart versions as follows:
* `$(npm bin)/gulp test.all.js`
* `$(npm bin)/gulp test.all.dart`
### Unit tests
You can run just the unit tests as follows:
* `$(npm bin)/gulp test.unit.js`: JS tests in a browser; runs in **watch mode** (i.e.
watches the test files for changes and re-runs tests when files are updated).
* `$(npm bin)/gulp test.unit.cjs`: JS tests in NodeJS; runs in **watch mode**.
* `$(npm bin)/gulp test.unit.dart`: Dart tests in Dartium; runs in **watch mode**.
If you prefer running tests in "single-run" mode rather than watch mode use:
* `$(npm bin)/gulp test.unit.js/ci`
* `$(npm bin)/gulp test.unit.cjs/ci`
* `$(npm bin)/gulp test.unit.dart/ci`
The task updates the dist folder with transpiled code whenever a source or test file changes, and
Karma is run against the new output.
**Note**: If you want to only run a single test you can alter the test you wish to run by changing
`it` to `iit` or `describe` to `ddescribe`. This will only run that individual test and make it
much easier to debug. `xit` and `xdescribe` can also be useful to exclude a test and a group of
tests respectively.
**Note**: **watch mode** needs symlinks to work, so if you're using windows, ensure you have the
rights to built them in your operating system.
### Unit tests with Sauce Labs or Browser Stack
First, in a terminal, create a tunnel with [Sauce Connect](https://docs.saucelabs.com/reference/sauce-connect/) or [Browser Stack Local](https://www.browserstack.com/local-testing#command-line), and valid credentials.
Then, in another terminal:
- Define the credentials as environment variables, e.g.:
```
export SAUCE_USERNAME='my_user'; export SAUCE_ACCESS_KEY='my_key';
export BROWSER_STACK_USERNAME='my_user'; export BROWSER_STACK_ACCESS_KEY='my_key';
```
- Then run `gulp test.unit.js.(saucelabs|browserstack) --browsers=option1,option2,..,optionN`
The options are any mix of browsers and aliases which are defined in the [browser-providers.conf.js](https://github.com/angular/angular/blob/master/browser-providers.conf.js) file.
They are case insensitive, and the `SL_` or `BS_` prefix must not be added for browsers.
Some examples of commands:
```
gulp test.unit.js.saucelabs --browsers=Safari8,ie11 //run in Sauce Labs with Safari 8 and IE11
gulp test.unit.js.browserstack --browsers=Safari,IE //run in Browser Stack with Safari 7, Safari 8, Safari 9, IE 9, IE 10 and IE 11
gulp test.unit.js.saucelabs --browsers=IOS,safari8,android5.1 //run in Sauce Labs with iOS 7, iOS 8, iOs 9, Safari 8 and Android 5.1
```
### E2E tests
1. `$(npm bin)/gulp build.js.cjs` (builds benchpress and tests into `dist/js/cjs` folder).
2. `$(npm bin)/gulp serve.js.prod serve.dart` (runs a local webserver).
3. `$(npm bin)/protractor protractor-js.conf.js`: JS e2e tests.
4. `$(npm bin)/protractor protractor-dart2js.conf.js`: dart2js e2e tests.
Angular specific command line options when running protractor:
- `$(npm bin)/protractor protractor-{js|dart2js}-conf.js --ng-help`
### Performance tests
1. `$(npm bin)/gulp build.js.cjs` (builds benchpress and tests into `dist/js/cjs` folder)
2. `$(npm bin)/gulp serve.js.prod serve.dart` (runs a local webserver)
3. `$(npm bin)/protractor protractor-js.conf.js --benchmark`: JS performance tests
4. `$(npm bin)/protractor protractor-dart2js.conf.js --benchmark`: dart2js performance tests
Angular specific command line options when running protractor (e.g. force gc, ...):
`$(npm bin)/protractor protractor-{js|dart2js}-conf.js --ng-help`
## Formatting with <a name="clang-format">clang-format</a>
We use [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to automatically enforce code
style for our TypeScript code. This allows us to focus our code reviews more on the content, and
less on style nit-picking. It also lets us encode our style guide in the `.clang-format` file in the
repository, allowing many tools and editors to share our settings.
To check the formatting of your code, run
gulp check-format
Note that the continuous build on Travis runs `gulp enforce-format`. Unlike the `check-format` task,
this will actually fail the build if files aren't formatted according to the style guide.
Your life will be easier if you include the formatter in your standard workflow. Otherwise, you'll
likely forget to check the formatting, and waste time waiting for a build on Travis that fails due
to some whitespace difference.
* Use `$(npm bin)/clang-format -i [file name]` to format a file (or multiple).
* Use `gulp enforce-format` to check if your code is `clang-format` clean. This also gives
you a command line to format your code.
* `clang-format` also includes a git hook, run `git clang-format` to format all files you
touched.
* You can run this as a **git pre-commit hook** to automatically format your delta regions when you
commit a change. In the angular repo, run
```
$ echo -e '#!/bin/sh\nexec git clang-format' > .git/hooks/pre-commit
$ chmod u+x !$
```
* **WebStorm** can run clang-format on the current file.
1. Under Preferences, open Tools > External Tools.
1. Plus icon to Create Tool
1. Fill in the form:
- Name: clang-format
- Description: Format
- Synchronize files after execution: checked
- Open console: not checked
- Show in: Editor menu
- Program: `$ProjectFileDir$/node_modules/.bin/clang-format`
- Parameters: `-i -style=file $FilePath$`
- Working directory: `$ProjectFileDir$`
* `clang-format` integrations are also available for many popular editors (`vim`, `emacs`,
`Sublime Text`, etc.).
## Generating the API documentation
The following gulp task will generate the API docs in the `dist/angular.io/partials/api/angular2`:
To run tests:
```shell
$(npm bin)/gulp docs/angular.io
$ ./test.sh node # Run all angular tests on node
$ ./test.sh browser # Run all angular tests in browser
$ ./test.sh browserNoRouter # Optionally run all angular tests without router in browser
$ ./test.sh tools # Run angular tooling (not framework) tests
```
You can serve the generated documentation to check how it would render on [angular.io](https://angular.io/):
- check out the [angular.io repo](https://github.com/angular/angular.io) locally,
- install dependencies as described in the [angular.io README](https://github.com/angular/angular.io/blob/master/README.md),
- copy the generated documentation from your local angular repo at `angular/dist/angular.io/partials/api/angular2` to your local angular.io repo at `angular.io/public/docs/js/latest/api`,
- run `harp compile` at the root of the angular.io repo to check the generated documentation for errors,
- run `harp server` and open a browser at `http://localhost:9000/docs/js/latest/api/` to check the rendered documentation.
You should execute the 3 test suites before submitting a PR to github.
## Project Information
All the tests are executed on our Continuous Integration infrastructure and a PR could only be merged once the tests pass.
### Folder structure
- CircleCI fails if your code is not formatted properly,
- Travis CI fails if any of the test suite describe above fails.
* `modules/*`: modules that will be loaded in the browser
* `tools/*`: tools that are needed to build Angular
* `dist/*`: build files are placed here.
## Update the public API tests
### File suffixes
If you happen to modify the public API of Angular, API golden files must be updated using:
* `*.ts`: TypeScript files that get transpiled to Dart and EcmaScript 5/6
* `*.dart`: Dart files that don't get transpiled
``` shell
$ gulp public-api:update
```
## CI using Travis
Note: The command `./test.sh tools` fails when the API doesn't match the golden files.
For instructions on setting up Continuous Integration using Travis, see the instructions given
[here](https://github.com/angular/angular.dart/blob/master/travis.md).
## Formatting your source code
## Transforming Dart code
Angular uses [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to format the source code. If the source code
is not properly formatted, the CI will fail and the PR can not be merged.
See the [wiki](//github.com/angular/angular/wiki/Angular-2-Dart-Transformer).
You can automatically format your code by running:
## Debugging
``` shell
$ gulp format
```
### Debug the transpiler
If you need to debug the transpiler:
- add a `debugger;` statement in the transpiler code,
- from the root folder, execute `node debug $(npm bin)/gulp build` to enter the node
debugger
- press "c" to execute the program until you reach the `debugger;` statement,
- you can then type "repl" to enter the REPL and inspect variables in the context.
See the [Node.js manual](http://nodejs.org/api/debugger.html) for more information.
Notes:
- You can also execute `node $(npm bin)/karma start karma-dart.conf.js` depending on which
code you want to debug (the former will process the "modules" folder while the later processes
the transpiler specs).
- You can also add `debugger;` statements in the specs (JavaScript). The execution will halt when
the developer tools are opened in the browser running Karma.
### Debug the tests
If you need to debug the tests:
- add a `debugger;` statement to the test you want to debug (or the source code),
- execute karma `$(npm bin)/gulp test.js`,
- press the top right "DEBUG" button,
- open the DevTools and press F5,
- the execution halts at the `debugger;` statement
**Note (WebStorm users)**:
1. Create a Karma run config from WebStorm.
2. Then in the "Run" menu, press "Debug 'karma-js.conf.js'", and WebStorm will stop in the generated
code on the `debugger;` statement.
3. You can then step into the code and add watches.
The `debugger;` statement is needed because WebStorm will stop in a transpiled file. Breakpoints in
the original source files are not supported at the moment.

215
LICENSE
View File

@ -1,202 +1,21 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
The MIT License
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Copyright (c) 2014-2016 Google, Inc. http://angular.io
1. Definitions.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,18 +1,22 @@
[![Build Status](https://travis-ci.org/angular/angular.svg?branch=master)](https://travis-ci.org/angular/angular)
[![Build Status](https://travis-ci.org/angular/angular.svg?branch=master)](https://travis-ci.org/angular/angular)
[![CircleCI](https://circleci.com/gh/angular/angular/tree/master.svg?style=shield)](https://circleci.com/gh/angular/angular/tree/master)
[![Join the chat at https://gitter.im/angular/angular](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Issue Stats](http://issuestats.com/github/angular/angular/badge/pr)](http://issuestats.com/github/angular/angular)
[![Issue Stats](http://issuestats.com/github/angular/angular/badge/issue)](http://issuestats.com/github/angular/angular)
[![npm version](https://badge.fury.io/js/angular2.svg)](http://badge.fury.io/js/angular2)
[![Issue Stats](http://issuestats.com/github/angular/angular/badge/pr?style=flat)](http://issuestats.com/github/angular/angular)
[![Issue Stats](http://issuestats.com/github/angular/angular/badge/issue?style=flat)](http://issuestats.com/github/angular/angular)
[![npm version](https://badge.fury.io/js/%40angular%2Fcore.svg)](https://badge.fury.io/js/%40angular%2Fcore)
[![Downloads](http://img.shields.io/npm/dm/angular2.svg)](https://npmjs.org/package/angular2)
[![Sauce Test Status](https://saucelabs.com/browser-matrix/angular2-ci.svg)](https://saucelabs.com/u/angular2-ci)
Angular
Angular
=========
Angular is a development platform for building mobile and desktop web applications. This is the
repository for [Angular 2][ng2], both the JavaScript (JS) and [Dart][dart] versions.
repository for [Angular 2][ng2] Typescript/JavaScript (JS).
Angular2 for [Dart][dart] can be found at [dart-lang/angular2][ng2dart].
Angular 2 is currently in **Beta**.
Angular 2 is currently in **Release Candidate**.
## Quickstart
@ -32,3 +36,4 @@ guidelines for [contributing][contributing] and then check out one of our issues
[ng2]: http://angular.io
[ngDart]: http://angulardart.org
[ngJS]: http://angularjs.org
[ng2dart]: https://github.com/dart-lang/angular2

View File

@ -1,376 +0,0 @@
# Developer Tools for Dart
Use these tools and techniques to increase your app's performance
and reliability.
* [Angular debugging tools](#angular-debugging-tools)
* [Code size](#code-size)
* [Performance](#performance)
## Angular debugging tools
Starting with alpha.38, Angular provides a set of debugging tools
that are accessible from any browser's developer console.
In Chrome, you can get to the dev console by pressing
Ctrl + Shift + J (on Mac: Cmd + Opt + J).
### Enabling the debugging tools
By default the debugging tools are disabled.
Enable the debugging tools as follows:
```dart
import 'package:angular2/platform/browser.dart';
main() async {
var appRef = await bootstrap(Application);
enableDebugTools(appRef);
}
```
<!-- Change function name to enableDebuggingTools? -->
### Using the debugging tools
In the browser, open the dev console. The top-level object is called `ng` and
contains more specific tools inside it.
For example, to run the change detection profiler on your app:
```javascript
// In the dev console:
ng.profiler.timeChangeDetection();
```
The [Change detection profiler](#change-detection-profiler) section
has more details.
<!-- Point to API docs when they're published, if they're useful.
They should be under
http://www.dartdocs.org/documentation/angular2/latest
and/or
https://angular.io/docs/js/latest/api/. -->
## Code size
Code must be downloaded, parsed, and executed. Too much code can lead to
slow application start-up time, especially on slow networks and low-end devices.
The tools and techniques in this section can help you to identify
unnecessarily large code and to reduce code size.
### Finding contributors to code size
Options for investigating code size include the `--dump-info` dart2js option,
ng2soyc, `reflector.trackUsage()`, and code coverage information
from the Dart VM.
#### Use --dump-info
The `--dump-info` option of `dart2js` outputs information about what happened
during compilation. You can specify `--dump-info` in `pubspec.yaml`:
```yaml
transformers:
...
- $dart2js:
commandLineOptions:
- --dump-info
```
The [Dump Info Visualizer](https://github.com/dart-lang/dump-info-visualizer)
can help you analyze the output.
For more information, see the
[dart2js_info API reference](http://dart-lang.github.io/dart2js_info/doc/api/).
#### Use ng2soyc.dart
[ng2soyc](https://github.com/angular/ng2soyc.dart) is a utility for analyzing
code size contributors in Angular 2 applications. It groups code size by
library and, assuming your library names follow
[standard naming conventions](https://www.dartlang.org/articles/style-guide/#do-prefix-library-names-with-the-package-name-and-a-dot-separated-path)
(package.library.sublibrary...), gives the code size breakdown at
each level. To reduce noise in the output of very large apps, ng2soyc provides
an option to hide libraries that are too small, so you can focus on the biggest
contributors.
#### Find unused reflection data
Your app might have types that are annotated with `@Component` or `@Injectable`
but never used.
To find these unused types, use `reflector.trackUsage()` and then,
after exercising your app, `reflector.listUnusedKeys()`.
For example:
```
import 'package:angular2/src/core/reflection/reflection.dart';
...
main() async {
reflector.trackUsage();
await bootstrap(AppComponent);
print('Unused keys: ${reflector.listUnusedKeys()}');
}
```
When you run that code (in Dartium or another browser),
you'll see a list of types that Angular _can_ inject but hasn't needed to.
Consider removing those types or their `@Component`/`@Injectable` annotation
to decrease your app's code size.
Three conditions must be true for `listUnusedKeys()` to return helpful data:
1. The angular2 transformer must run on the app.
2. If you're running a JavaScript version of the app,
the app must not be minified, so that the names are readable.
3. You must exercise your app in as many ways as possible
before calling `listUnusedKeys()`.
Otherwise, you might get false positives:
keys that haven't been used only because you didn't exercise
the relevant feature of the app.
To run the angular2 transformer, first specify it in `pubspec.yaml`:
```
name: hello_world
...
transformers:
- angular2:
entry_points: web/main.dart
```
Then use pub to run the transformer. If you use `pub serve`,
it provides both Dart and unminified (by default) JavaScript versions.
If you want to serve actual files, then use `pub build` in debug mode
to generate Dart and unminified JavaScript files:
`pub build --mode=debug`.
The `reflector.trackUsage()` method makes Angular track the reflection
information used by the app. Reflection information (`ReflectionInfo`) is a data
structure that stores information that Angular uses for locating DI factories
and for generating change detectors and other code related to a
given type.
#### Use code coverage to find dead code
When running in Dartium (or in the Dart VM, in general) you can request code
coverage information from the VM. You can either use
[observatory](https://www.dartlang.org/tools/observatory/) or download
the coverage file and use your own tools to inspect it. Lines of code that are
not covered are top candidates for dead code.
Keep in mind, however, that uncovered code is not sufficient evidence of dead
code, only necessary evidence. It is perfectly possible that you simply didn't
exercise your application in a way that triggers the execution of uncovered
code. A common example is error handling code. Just because your testing never
encountered an error does not mean the error won't happen in production. You
therefore don't have to rush and remove all the `catch` blocks.
### Reducing code size
To reduce code size, you can disable reflection,
enable minification, and manually remove dead code.
You can also try less safe options such as
telling dart2js to trust type annotations.
#### Disable reflection
`dart:mirrors` allows discovering program metadata at runtime. However, this
means that `dart2js` needs to retain that metadata and thus increase the size
of resulting JS output. In practice, however, it is possible to extract most
metadata necessary for your metaprogramming tasks statically using a
transformer and `package:analyzer`, and act on it before compiling to JS.
#### Enable minification
Minification shortens all your `longMethodNames` into 2- or 3-letter long
symbols. `dart2js` ensures that this kind of renaming is done safely, without
breaking the functionality of your programs. You can enable it in `pubspec.yaml`
under `$dart2js` transformer:
```yaml
transformers:
...
- $dart2js:
minify: true
```
#### Manually remove dead code
`dart2js` comes with dead code elimination out-of-the-box. However, it may not
always be able to tell if a piece of code could be used. Consider the following
example:
```dart
/// This function decides which serialization format to use
void setupSerializers() {
if (server.doYouSupportProtocolBuffers()) {
useProtobufSerializers();
} else {
useJsonSerializers();
}
}
```
In this example the application asks the server what kind of serialization
format it uses and dynamically chooses one or the other. `dart2js` can't
tell whether the server responds with yes or no, so it must retain both
kinds of serializers. However, if you know that your server supports
protocol buffers, you can remove that `if` block entirely and default to
protocol buffers.
Code coverage (see above) is a good way to find dead code in your app.
#### Unsafe options
Dart also provides more aggressive optimization options. However, you have to
be careful when using them and as of today the benefits aren't that clear. If
your type annotations are inaccurate you may end up with non-Darty runtime
behavior, including the classic "undefined is not a function" tautology, as
well as the "keep on truckin'" behavior, e.g. `null + 1 == 1` and
`{} + [] == 0`.
`--trust-type-annotations` tells `dart2js` to trust that your type annotations
are correct. So if you have a function `foo(Bar bar)` the compiler can omit the
check that `bar` is truly `Bar` when calling methods on it.
`--trust-primitives` tells `dart2js` that primitive types, such as numbers and
booleans are never `null` when performing arithmetic, and that your program
does not run into range error when operating on lists, letting the compiler
remove some of the error checking code.
Specify these options in `pubspec.yaml`.
Example:
```yaml
transformers:
...
- $dart2js:
commandLineOptions:
- --trust-type-annotations
- --trust-primitives
```
## Performance
### Change detection profiler
If your application is janky (it misses frames) or is slow according to other
metrics, you need to find out why. This tool helps by measuring the average
speed of _change detection_, a phase in Angular's
lifecycle that detects changes in values that are bound to the UI.
Janky UI updates can result from slowness either in _computing_ the changes or
in _applying_ those changes to the UI.
For your app to be performant, the process of _computing_ changes must be very
fast—preferably **under 3 milliseconds**.
Fast change computation leaves room for
the application logic, UI updates, and browser rendering pipeline
to fit within a 16 ms frame (assuming a target frame rate of 60 FPS).
The change detection profiler repeatedly performs change detection
without invoking any user actions, such as clicking buttons or entering
text in input fields. It then computes the average amount of time
(in milliseconds) to perform a single cycle of change detection and
prints that to the console. This number depends on the current state of the UI. You are likely to see different numbers
as you go from one screen in your application to another.
#### Running the profiler
Before running the profiler, enable the debugging tools
and put the app into the state you want to measure:
1. If you haven't already done so,
[enable the debugging tools](#enabling-the-debugging-tools).
2. Navigate the app to a screen whose performance you want to profile.
3. Make sure the screen is in a state that you want to measure.
For example, you might want to profile the screen several times,
with different amounts and kinds of data.
To run the profiler, enter the following in the dev console:
```javascript
ng.profiler.timeChangeDetection();
```
The results are visible in the console.
#### Recording CPU profiles
To record a profile, pass `{record: true}` to `timeChangeDetection()`:
```javascript
ng.profiler.timeChangeDetection({record: true});
```
Then open the **Profiles** tab. The recorded profile has the title
**Change Detection**. In Chrome, if you record the profile repeatedly, all the
profiles are nested under Change Detection.
#### Interpreting the numbers
In a properly designed application, repeated attempts to detect changes without
any user actions result in no changes to the UI. It is
also desirable to have the cost of a user action be proportional to the amount
of UI changes required. For example, popping up a menu with 5 items should be
vastly faster than rendering a table of 500 rows and 10 columns. Therefore,
change detection with no UI updates should be as fast as possible.
#### Investigating slow change detection
So you found a screen in your application on which the profiler reports a very
high number (i.e. >3ms). This is where a recorded CPU profile can help. Enable
recording while profiling:
```javascript
ng.profiler.timeChangeDetection({record: true});
```
Then look for hot spots using
[Chrome CPU profiler](https://developer.chrome.com/devtools/docs/cpu-profiling).
#### Reducing change detection cost
There are many reasons for slow change detection. To gain intuition about
possible causes it helps to understand how change detection works. Such a
discussion is outside the scope of this document,
but here are some key concepts.
<!-- TODO: link to change detection docs -->
By default, Angular uses a _dirty checking_ mechanism to find model changes.
This mechanism involves evaluating every bound expression that's active on the
UI. These usually include text interpolation via `{{expression}}` and property
bindings via `[prop]="expression"`. If any of the evaluated expressions are
costly to compute, they might contribute to slow change detection. A good way to
speed things up is to use plain class fields in your expressions and avoid any
kind of computation. For example:
```dart
@View(
template: '<button [enabled]="isEnabled">{{title}}</button>'
)
class FancyButton {
// GOOD: no computation, just returns the value
bool isEnabled;
// BAD: computes the final value upon request
String _title;
String get title => _title.trim().toUpperCase();
}
```
Most cases like these can be solved by precomputing the value and storing the
final value in a field.
Angular also supports a second type of change detection: the _push_ model. In
this model, Angular does not poll your component for changes. Instead, the
component tells Angular when it changes, and only then does Angular perform
the update. This model is suitable in situations when your data model uses
observable or immutable objects.
<!-- TODO: link to discussion of push model -->

View File

@ -14,7 +14,7 @@ Ctrl + Shift + j.
By default the debug tools are disabled. You can enable debug tools as follows:
```typescript
import {enableDebugTools} from 'angular2/platform/browser';
import {enableDebugTools} from '@angular/platform-browser';
bootstrap(Application).then((appRef) => {
enableDebugTools(appRef);
@ -117,7 +117,7 @@ speed things up is to use plain class fields in your expressions and avoid any
kinds of computation. Example:
```typescript
@View({
@Component({
template: '<button [enabled]="isEnabled">{{title}}</button>'
})
class FancyButton {

View File

@ -4,14 +4,16 @@
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}},
// FirefoxBeta and ChromeBeta should be target:'BS' or target:'SL', and required:true
// Currently deactivated due to https://github.com/angular/angular/issues/7560
'ChromeBeta': { unitTest: {target: null, required: true}, e2e: {target: null, required: false}},
'FirefoxBeta': { unitTest: {target: null, required: false}, e2e: {target: null, required: false}},
'ChromeDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
'FirefoxDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
'IE9': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
'IE10': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
'IE11': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
'Edge': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
'Edge': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'Android4.1': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
'Android4.2': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
'Android4.3': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
@ -22,8 +24,7 @@ var CIconfiguration = {
'Safari9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'iOS7': { unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}},
'iOS8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
// TODO(mlaval): iOS9 deactivated as not reliable, reactivate after https://github.com/angular/angular/issues/5408
'iOS9': { unitTest: {target: null, required: false}, e2e: {target: null, required: true}},
'iOS9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'WindowsPhone': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
};
@ -37,7 +38,7 @@ var customLaunchers = {
'SL_CHROME': {
base: 'SauceLabs',
browserName: 'chrome',
version: '46'
version: '52'
},
'SL_CHROMEBETA': {
base: 'SauceLabs',
@ -52,7 +53,7 @@ var customLaunchers = {
'SL_FIREFOX': {
base: 'SauceLabs',
browserName: 'firefox',
version: '42'
version: '46'
},
'SL_FIREFOXBETA': {
base: 'SauceLabs',
@ -68,13 +69,13 @@ var customLaunchers = {
base: 'SauceLabs',
browserName: 'safari',
platform: 'OS X 10.9',
version: '7'
version: '7.0'
},
'SL_SAFARI8': {
base: 'SauceLabs',
browserName: 'safari',
platform: 'OS X 10.10',
version: '8'
version: '8.0'
},
'SL_SAFARI9': {
base: 'SauceLabs',
@ -98,7 +99,7 @@ var customLaunchers = {
base: 'SauceLabs',
browserName: 'iphone',
platform: 'OS X 10.10',
version: '9.1'
version: '9.3'
},
'SL_IE9': {
base: 'SauceLabs',
@ -120,9 +121,9 @@ var customLaunchers = {
},
'SL_EDGE': {
base: 'SauceLabs',
browserName: 'microsoftedge',
browserName: 'MicrosoftEdge',
platform: 'Windows 10',
version: '20.10240'
version: '13.10586'
},
'SL_ANDROID4.1': {
base: 'SauceLabs',
@ -201,7 +202,7 @@ var customLaunchers = {
base: 'BrowserStack',
device: 'iPhone 6S',
os: 'ios',
os_version: '9.0'
os_version: '9.1'
},
'BS_IE9': {
base: 'BrowserStack',
@ -298,12 +299,7 @@ module.exports = {
customLaunchers: customLaunchers,
sauceAliases: sauceAliases,
browserstackAliases: browserstackAliases
}
if (process.env.TRAVIS) {
process.env.SAUCE_ACCESS_KEY = process.env.SAUCE_ACCESS_KEY.split('').reverse().join('');
process.env.BROWSER_STACK_ACCESS_KEY = process.env.BROWSER_STACK_ACCESS_KEY.split('').reverse().join('');
}
};
function buildConfiguration(type, target, required) {
return Object.keys(CIconfiguration)

121
build.sh Executable file
View File

@ -0,0 +1,121 @@
#!/usr/bin/env bash
set -e -o pipefail
cd `dirname $0`
export NODE_PATH=${NODE_PATH}:$(pwd)/dist/all:$(pwd)/dist/tools
rm -rf ./dist/all/
mkdir -p ./dist/all/
TSCONFIG=./tools/tsconfig.json
echo "====== (all)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
$(npm bin)/tsc -p ${TSCONFIG}
cp ./tools/@angular/tsc-wrapped/package.json ./dist/tools/@angular/tsc-wrapped
echo "====== Copying files needed for e2e tests ====="
cp -r ./modules/playground ./dist/all/
cp -r ./modules/playground/favicon.ico ./dist/
#rsync -aP ./modules/playground/* ./dist/all/playground/
mkdir ./dist/all/playground/vendor
cd ./dist/all/playground/vendor
ln -s ../../../../node_modules/es6-shim/es6-shim.js .
ln -s ../../../../node_modules/zone.js/dist/zone.js .
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
ln -s ../../../../node_modules/base64-js/lib/b64.js .
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
ln -s ../../../../node_modules/rxjs .
ln -s ../../../../node_modules/angular/angular.js .
cd -
TSCONFIG=./modules/tsconfig.json
echo "====== (all)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
# compile ts code
TSC="node --max-old-space-size=3000 dist/tools/@angular/tsc-wrapped/src/main"
$TSC -p modules/tsconfig.json
rm -rf ./dist/packages-dist
for PACKAGE in \
core \
compiler \
common \
forms \
platform-browser \
platform-browser-dynamic \
platform-server \
http \
router \
router-deprecated \
upgrade \
compiler-cli
do
SRCDIR=./modules/@angular/${PACKAGE}
DESTDIR=./dist/packages-dist/${PACKAGE}
UMD_ES6_PATH=${DESTDIR}/esm/${PACKAGE}.umd.js
UMD_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.js
UMD_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.min.js
if [[ ${PACKAGE} == "router-deprecated" ]]; then
echo "====== COMPILING: \$(npm bin)/tsc -p ${SRCDIR}/tsconfig-es5.json ====="
$(npm bin)/tsc -p ${SRCDIR}/tsconfig-es5.json
else
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-es5.json ====="
$TSC -p ${SRCDIR}/tsconfig-es5.json
fi
cp ${SRCDIR}/package.json ${DESTDIR}/
echo "====== TSC 1.8 d.ts compat for ${DESTDIR} ====="
# safely strips 'readonly' specifier from d.ts files to make them compatible with tsc 1.8
if [ "$(uname)" == "Darwin" ]; then
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i '' -e 's/\(^ *(static |private )*\)*readonly */\1/g'
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i '' -E 's/^( +)abstract ([[:alnum:]]+\:)/\1\2/g'
else
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i -e 's/\(^ *(static |private )*\)*readonly */\1/g'
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i -E 's/^( +)abstract ([[:alnum:]]+\:)/\1\2/g'
fi
if [[ ${PACKAGE} != compiler-cli ]]; then
if [[ ${PACKAGE} == "router-deprecated" ]]; then
echo "====== (esm)COMPILING: \$(npm bin)/tsc -p ${SRCDIR}/tsconfig-es2015.json ====="
$(npm bin)/tsc --emitDecoratorMetadata -p ${SRCDIR}/tsconfig-es2015.json
else
echo "====== (esm)COMPILING: $TSC -p ${SRCDIR}/tsconfig-es2015.json ====="
$TSC -p ${SRCDIR}/tsconfig-es2015.json
fi
echo "====== BUNDLING: ${SRCDIR} ====="
mkdir ${DESTDIR}/bundles
(
cd ${SRCDIR}
echo "..." # here just to have grep match something and not exit with 1
../../../node_modules/.bin/rollup -c rollup.config.js
) 2>&1 | grep -v "as external dependency"
$(npm bin)/tsc \
--out ${UMD_ES5_PATH} \
--target es5 \
--lib "es6,dom" \
--allowJs \
${UMD_ES6_PATH}
rm ${UMD_ES6_PATH}
cat ./modules/@angular/license-banner.txt > ${UMD_ES5_PATH}.tmp
cat ${UMD_ES5_PATH} >> ${UMD_ES5_PATH}.tmp
mv ${UMD_ES5_PATH}.tmp ${UMD_ES5_PATH}
$(npm bin)/uglifyjs -c --screw-ie8 -o ${UMD_ES5_MIN_PATH} ${UMD_ES5_PATH}
fi
done
echo "====== COMPILING: \$(npm bin)/tsc -p benchpress/tsconfig.json ====="
$(npm bin)/tsc -p ./modules/benchpress/tsconfig.json

11
circle.yml Normal file
View File

@ -0,0 +1,11 @@
machine:
node:
version: 5.4.1
dependencies:
pre:
- npm install -g npm
test:
override:
- gulp lint

File diff suppressed because it is too large Load Diff

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

@ -11,28 +11,38 @@ module.exports = function(config) {
files: [
// Sources and specs.
// Loaded through the System loader, in `test-main.js`.
{pattern: 'dist/js/dev/es5/**', included: false, watched: false},
{pattern: 'dist/all/@angular/**/*.js', included: false, watched: true},
'node_modules/es6-shim/es6-shim.js',
// include Angular v1 for upgrade module testing
'node_modules/angular/angular.min.js',
// zone-microtask must be included first as it contains a Promise monkey patch
'node_modules/zone.js/dist/zone-microtask.js',
'node_modules/zone.js/dist/zone.js',
'node_modules/zone.js/dist/long-stack-trace-zone.js',
'node_modules/zone.js/dist/jasmine-patch.js',
'node_modules/zone.js/dist/async-test.js',
'node_modules/zone.js/dist/fake-async-test.js',
// Including systemjs because it defines `__eval`, which produces correct stack traces.
'modules/angular2/src/testing/shims_for_IE.js',
'shims_for_IE.js',
'node_modules/systemjs/dist/system.src.js',
{pattern: 'node_modules/rxjs/**', included: false, watched: false, served: true},
'node_modules/reflect-metadata/Reflect.js',
'tools/build/file2modulename.js',
'test-main.js',
{pattern: 'modules/**/test/**/static_assets/**', included: false, watched: false}
{pattern: 'dist/all/empty.*', included: false, watched: false},
{pattern: 'modules/@angular/platform-browser/test/static_assets/**', included: false, watched: false},
{pattern: 'modules/@angular/platform-browser/test/browser/static_assets/**', included: false, watched: false}
],
exclude: ['dist/js/dev/es5/**/e2e_test/**', 'dist/js/dev/es5/angular2/examples/**', 'dist/angular1_router.js'],
exclude: [
'dist/all/@angular/**/e2e_test/**',
'dist/all/@angular/examples/**',
'dist/all/@angular/router/**',
'dist/all/@angular/compiler-cli/**',
'dist/all/angular1_router.js',
'dist/all/@angular/platform-browser/testing/e2e_util.js'
],
customLaunchers: browserProvidersConf.customLaunchers,
@ -42,7 +52,6 @@ module.exports = function(config) {
'karma-sauce-launcher',
'karma-chrome-launcher',
'karma-sourcemap-loader',
'karma-dart',
internalAngularReporter
],
@ -53,11 +62,12 @@ module.exports = function(config) {
reporters: ['internal-angular'],
sauceLabs: {
testName: 'Angular2',
retryLimit: 3,
startConnect: false,
recordVideo: false,
recordScreenshots: false,
options: {
'selenium-version': '2.48.2',
'selenium-version': '2.53.0',
'command-timeout': 600,
'idle-timeout': 600,
'max-duration': 5400
@ -67,19 +77,23 @@ module.exports = function(config) {
browserStack: {
project: 'Angular2',
startTunnel: false,
retryLimit: 1,
retryLimit: 3,
timeout: 600,
pollingTimeout: 10000
},
browsers: ['Chrome'],
port: 9876
port: 9876,
captureTimeout: 60000,
browserDisconnectTimeout : 60000,
browserDisconnectTolerance : 3,
browserNoActivityTimeout : 60000,
});
if (process.env.TRAVIS) {
var buildId = 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';
if (process.env.MODE.startsWith('saucelabs')) {
if (process.env.CI_MODE.startsWith('saucelabs')) {
config.sauceLabs.build = buildId;
config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
@ -89,7 +103,7 @@ module.exports = function(config) {
config.transports = ['polling'];
}
if (process.env.MODE.startsWith('browserstack')) {
if (process.env.CI_MODE.startsWith('browserstack')) {
config.browserStack.build = buildId;
config.browserStack.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
}

View File

@ -0,0 +1,30 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgModule} from '@angular/core';
import {COMMON_DIRECTIVES} from './src/common_directives';
import {COMMON_PIPES} from './src/pipes';
export * from './src/pipes';
export * from './src/directives';
export * from './src/forms-deprecated';
export * from './src/common_directives';
export * from './src/location';
export {NgLocalization} from './src/localization';
// Note: This does not contain the location providers,
// as they need some platform specific implementations to work.
/**
* The module that includes all the basic Angular directives like {@link NgIf}, ${link NgFor}, ...
*
* @experimental
*/
@NgModule(
{declarations: [COMMON_DIRECTIVES, COMMON_PIPES], exports: [COMMON_DIRECTIVES, COMMON_PIPES]})
export class CommonModule {
}

View File

@ -0,0 +1,17 @@
{
"name": "@angular/common",
"version": "0.0.0-PLACEHOLDER",
"description": "",
"main": "index.js",
"jsnext:main": "esm/index.js",
"typings": "index.d.ts",
"author": "angular",
"license": "MIT",
"peerDependencies": {
"@angular/core": "0.0.0-PLACEHOLDER"
},
"repository": {
"type": "git",
"url": "https://github.com/angular/angular.git"
}
}

View File

@ -0,0 +1,17 @@
export default {
entry: '../../../dist/packages-dist/common/esm/index.js',
dest: '../../../dist/packages-dist/common/esm/common.umd.js',
format: 'umd',
moduleName: 'ng.common',
globals: {
'@angular/core': 'ng.core',
'rxjs/Subject': 'Rx',
'rxjs/observable/PromiseObservable': 'Rx', // this is wrong, but this stuff has changed in rxjs b.6 so we need to fix it when we update.
'rxjs/operator/toPromise': 'Rx.Observable.prototype',
'rxjs/Observable': 'Rx'
},
plugins: [
// nodeResolve({ jsnext: true, main: true }),
]
}

View File

@ -0,0 +1,58 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Type} from '@angular/core';
import {CORE_DIRECTIVES} from './directives';
/**
* A collection of Angular core directives that are likely to be used in each and every Angular
* application. This includes core directives (e.g., NgIf and NgFor), and forms directives (e.g.,
* NgModel).
*
* This collection can be used to quickly enumerate all the built-in directives in the `directives`
* property of the `@Component` decorator.
*
* ### Example
*
* Instead of writing:
*
* ```typescript
* import {NgClass, NgIf, NgFor, NgSwitch, NgSwitchWhen, NgSwitchDefault, NgModel, NgForm} from
* '@angular/common';
* import {OtherDirective} from './myDirectives';
*
* @Component({
* selector: 'my-component',
* templateUrl: 'myComponent.html',
* directives: [NgClass, NgIf, NgFor, NgSwitch, NgSwitchWhen, NgSwitchDefault, NgModel, NgForm,
* OtherDirective]
* })
* export class MyComponent {
* ...
* }
* ```
* one could import all the common directives at once:
*
* ```typescript
* import {COMMON_DIRECTIVES} from '@angular/common';
* import {OtherDirective} from './myDirectives';
*
* @Component({
* selector: 'my-component',
* templateUrl: 'myComponent.html',
* directives: [COMMON_DIRECTIVES, OtherDirective]
* })
* export class MyComponent {
* ...
* }
* ```
*
* @experimental Contains forms which are experimental.
*/
export const COMMON_DIRECTIVES: Type[][] = [CORE_DIRECTIVES];

View File

@ -0,0 +1,21 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* @module
* @description
* Common directives shipped with Angular.
*/
export {CORE_DIRECTIVES} from './directives/core_directives';
export {NgClass} from './directives/ng_class';
export {NgFor} from './directives/ng_for';
export {NgIf} from './directives/ng_if';
export {NgPlural, NgPluralCase} from './directives/ng_plural';
export {NgStyle} from './directives/ng_style';
export {NgSwitch, NgSwitchCase, NgSwitchDefault} from './directives/ng_switch';
export {NgTemplateOutlet} from './directives/ng_template_outlet';

View File

@ -0,0 +1,72 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Type} from '../facade/lang';
import {NgClass} from './ng_class';
import {NgFor} from './ng_for';
import {NgIf} from './ng_if';
import {NgPlural, NgPluralCase} from './ng_plural';
import {NgStyle} from './ng_style';
import {NgSwitch, NgSwitchCase, NgSwitchDefault} from './ng_switch';
import {NgTemplateOutlet} from './ng_template_outlet';
/**
* A collection of Angular core directives that are likely to be used in each and every Angular
* application.
*
* This collection can be used to quickly enumerate all the built-in directives in the `directives`
* property of the `@Component` annotation.
*
* ### Example ([live demo](http://plnkr.co/edit/yakGwpCdUkg0qfzX5m8g?p=preview))
*
* Instead of writing:
*
* ```typescript
* import {NgClass, NgIf, NgFor, NgSwitch, NgSwitchWhen, NgSwitchDefault} from '@angular/common';
* import {OtherDirective} from './myDirectives';
*
* @Component({
* selector: 'my-component',
* templateUrl: 'myComponent.html',
* directives: [NgClass, NgIf, NgFor, NgSwitch, NgSwitchWhen, NgSwitchDefault, OtherDirective]
* })
* export class MyComponent {
* ...
* }
* ```
* one could import all the core directives at once:
*
* ```typescript
* import {CORE_DIRECTIVES} from '@angular/common';
* import {OtherDirective} from './myDirectives';
*
* @Component({
* selector: 'my-component',
* templateUrl: 'myComponent.html',
* directives: [CORE_DIRECTIVES, OtherDirective]
* })
* export class MyComponent {
* ...
* }
* ```
*
* @stable
*/
export const CORE_DIRECTIVES: Type[] = [
NgClass,
NgFor,
NgIf,
NgTemplateOutlet,
NgStyle,
NgSwitch,
NgSwitchCase,
NgSwitchDefault,
NgPlural,
NgPluralCase,
];

View File

@ -0,0 +1,189 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableDiffer, IterableDiffers, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
import {StringMapWrapper, isListLikeIterable} from '../facade/collection';
import {isArray, isPresent, isString} from '../facade/lang';
/**
* The `NgClass` directive conditionally adds and removes CSS classes on an HTML element based on
* an expression's evaluation result.
*
* The result of an expression evaluation is interpreted differently depending on type of
* the expression evaluation result:
* - `string` - all the CSS classes listed in a string (space delimited) are added
* - `Array` - all the CSS classes (Array elements) are added
* - `Object` - each key corresponds to a CSS class name while values are interpreted as expressions
* evaluating to `Boolean`. If a given expression evaluates to `true` a corresponding CSS class
* is added - otherwise it is removed.
*
* While the `NgClass` directive can interpret expressions evaluating to `string`, `Array`
* or `Object`, the `Object`-based version is the most often used and has an advantage of keeping
* all the CSS class names in a template.
*
* ### Example ([live demo](http://plnkr.co/edit/a4YdtmWywhJ33uqfpPPn?p=preview)):
*
* ```
* import {Component} from '@angular/core';
* import {NgClass} from '@angular/common';
*
* @Component({
* selector: 'toggle-button',
* inputs: ['isDisabled'],
* template: `
* <div class="button" [ngClass]="{active: isOn, disabled: isDisabled}"
* (click)="toggle(!isOn)">
* Click me!
* </div>`,
* styles: [`
* .button {
* width: 120px;
* border: medium solid black;
* }
*
* .active {
* background-color: red;
* }
*
* .disabled {
* color: gray;
* border: medium solid gray;
* }
* `],
* directives: [NgClass]
* })
* class ToggleButton {
* isOn = false;
* isDisabled = false;
*
* toggle(newState) {
* if (!this.isDisabled) {
* this.isOn = newState;
* }
* }
* }
* ```
*
* @stable
*/
@Directive({selector: '[ngClass]'})
export class NgClass implements DoCheck {
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) {}
@Input('class')
set initialClasses(v: string) {
this._applyInitialClasses(true);
this._initialClasses = isPresent(v) && isString(v) ? v.split(' ') : [];
this._applyInitialClasses(false);
this._applyClasses(this._rawClass, false);
}
@Input()
set ngClass(v: string|string[]|Set<string>|{[key: string]: any}) {
this._cleanupClasses(this._rawClass);
if (isString(v)) {
v = (<string>v).split(' ');
}
this._rawClass = <string[]|Set<string>>v;
this._iterableDiffer = null;
this._keyValueDiffer = null;
if (isPresent(v)) {
if (isListLikeIterable(v)) {
this._iterableDiffer = this._iterableDiffers.find(v).create(null);
} else {
this._keyValueDiffer = this._keyValueDiffers.find(v).create(null);
}
}
}
ngDoCheck(): void {
if (isPresent(this._iterableDiffer)) {
var changes = this._iterableDiffer.diff(this._rawClass);
if (isPresent(changes)) {
this._applyIterableChanges(changes);
}
}
if (isPresent(this._keyValueDiffer)) {
var changes = this._keyValueDiffer.diff(this._rawClass);
if (isPresent(changes)) {
this._applyKeyValueChanges(changes);
}
}
}
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: 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);
}
});
}
private _applyIterableChanges(changes: any): void {
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]: any}, isCleanup: boolean) {
if (isPresent(rawClassVal)) {
if (isArray(rawClassVal)) {
(<string[]>rawClassVal).forEach(className => this._toggleClass(className, !isCleanup));
} else if (rawClassVal instanceof Set) {
(<Set<string>>rawClassVal).forEach(className => this._toggleClass(className, !isCleanup));
} else {
StringMapWrapper.forEach(
<{[k: string]: any}>rawClassVal, (expVal: any, className: string) => {
if (isPresent(expVal)) this._toggleClass(className, !isCleanup);
});
}
}
}
private _toggleClass(className: string, enabled: boolean): void {
className = className.trim();
if (className.length > 0) {
if (className.indexOf(' ') > -1) {
var classes = className.split(/\s+/g);
for (var i = 0, len = classes.length; i < len; i++) {
this._renderer.setElementClass(this._ngEl.nativeElement, classes[i], enabled);
}
} else {
this._renderer.setElementClass(this._ngEl.nativeElement, className, enabled);
}
}
}
}

View File

@ -0,0 +1,161 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ChangeDetectorRef, CollectionChangeRecord, DefaultIterableDiffer, Directive, DoCheck, EmbeddedViewRef, Input, IterableDiffer, IterableDiffers, OnChanges, SimpleChanges, TemplateRef, TrackByFn, ViewContainerRef} from '@angular/core';
import {BaseException} from '../facade/exceptions';
import {getTypeNameForDebugging, isBlank, isPresent} from '../facade/lang';
export class NgForRow {
constructor(public $implicit: any, public index: number, public count: number) {}
get first(): boolean { return this.index === 0; }
get last(): boolean { return this.index === this.count - 1; }
get even(): boolean { return this.index % 2 === 0; }
get odd(): boolean { return !this.even; }
}
/**
* The `NgFor` directive instantiates a template once per item from an iterable. The context for
* each instantiated template inherits from the outer context with the given loop variable set
* to the current item from the iterable.
*
* ### Local Variables
*
* `NgFor` provides several exported values that can be aliased to local variables:
*
* * `index` will be set to the current loop iteration for each template context.
* * `first` will be set to a boolean value indicating whether the item is the first one in the
* iteration.
* * `last` will be set to a boolean value indicating whether the item is the last one in the
* iteration.
* * `even` will be set to a boolean value indicating whether this item has an even index.
* * `odd` will be set to a boolean value indicating whether this item has an odd index.
*
* ### Change Propagation
*
* When the contents of the iterator changes, `NgFor` makes the corresponding changes to the DOM:
*
* * When an item is added, a new instance of the template is added to the DOM.
* * When an item is removed, its template instance is removed from the DOM.
* * When items are reordered, their respective templates are reordered in the DOM.
* * Otherwise, the DOM element for that item will remain the same.
*
* Angular uses object identity to track insertions and deletions within the iterator and reproduce
* those changes in the DOM. This has important implications for animations and any stateful
* controls
* (such as `<input>` elements which accept user input) that are present. Inserted rows can be
* animated in, deleted rows can be animated out, and unchanged rows retain any unsaved state such
* as user input.
*
* It is possible for the identities of elements in the iterator to change while the data does not.
* This can happen, for example, if the iterator produced from an RPC to the server, and that
* RPC is re-run. Even if the data hasn't changed, the second response will produce objects with
* different identities, and Angular will tear down the entire DOM and rebuild it (as if all old
* elements were deleted and all new elements inserted). This is an expensive operation and should
* be avoided if possible.
*
* ### Syntax
*
* - `<li *ngFor="let item of items; let i = index">...</li>`
* - `<li template="ngFor let item of items; let i = index">...</li>`
* - `<template ngFor let-item [ngForOf]="items" let-i="index"><li>...</li></template>`
*
* ### Example
*
* See a [live demo](http://plnkr.co/edit/KVuXxDp0qinGDyo307QW?p=preview) for a more detailed
* example.
*
* @stable
*/
@Directive({selector: '[ngFor][ngForOf]'})
export class NgFor implements DoCheck, OnChanges {
@Input() ngForOf: any;
@Input() ngForTrackBy: TrackByFn;
private _differ: IterableDiffer;
constructor(
private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef<NgForRow>,
private _iterableDiffers: IterableDiffers, private _cdr: ChangeDetectorRef) {}
@Input()
set ngForTemplate(value: TemplateRef<NgForRow>) {
if (isPresent(value)) {
this._templateRef = value;
}
}
ngOnChanges(changes: SimpleChanges): void {
if ('ngForOf' in changes) {
// React on ngForOf changes only once all inputs have been initialized
const value = changes['ngForOf'].currentValue;
if (isBlank(this._differ) && isPresent(value)) {
try {
this._differ = this._iterableDiffers.find(value).create(this._cdr, this.ngForTrackBy);
} catch (e) {
throw new BaseException(
`Cannot find a differ supporting object '${value}' of type '${getTypeNameForDebugging(value)}'. NgFor only supports binding to Iterables such as Arrays.`);
}
}
}
}
ngDoCheck() {
if (isPresent(this._differ)) {
const changes = this._differ.diff(this.ngForOf);
if (isPresent(changes)) this._applyChanges(changes);
}
}
private _applyChanges(changes: DefaultIterableDiffer) {
const insertTuples: RecordViewTuple[] = [];
changes.forEachOperation(
(item: CollectionChangeRecord, adjustedPreviousIndex: number, currentIndex: number) => {
if (item.previousIndex == null) {
let view = this._viewContainer.createEmbeddedView(
this._templateRef, new NgForRow(null, null, null), currentIndex);
let tuple = new RecordViewTuple(item, view);
insertTuples.push(tuple);
} else if (currentIndex == null) {
this._viewContainer.remove(adjustedPreviousIndex);
} else {
let view = this._viewContainer.get(adjustedPreviousIndex);
this._viewContainer.move(view, currentIndex);
let tuple = new RecordViewTuple(item, <EmbeddedViewRef<NgForRow>>view);
insertTuples.push(tuple);
}
});
for (let i = 0; i < insertTuples.length; i++) {
this._perViewChange(insertTuples[i].view, insertTuples[i].record);
}
for (let i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
var viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(i);
viewRef.context.index = i;
viewRef.context.count = ilen;
}
changes.forEachIdentityChange((record: any) => {
var viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(record.currentIndex);
viewRef.context.$implicit = record.item;
});
}
private _perViewChange(view: EmbeddedViewRef<NgForRow>, record: CollectionChangeRecord) {
view.context.$implicit = record.item;
}
}
class RecordViewTuple {
constructor(public record: any, public view: EmbeddedViewRef<NgForRow>) {}
}

View File

@ -0,0 +1,56 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, Input, TemplateRef, ViewContainerRef} from '@angular/core';
import {isBlank} from '../facade/lang';
/**
* Removes or recreates a portion of the DOM tree based on an {expression}.
*
* If the expression assigned to `ngIf` evaluates to a false value then the element
* is removed from the DOM, otherwise a clone of the element is reinserted into the DOM.
*
* ### Example ([live demo](http://plnkr.co/edit/fe0kgemFBtmQOY31b4tw?p=preview)):
*
* ```
* <div *ngIf="errorCount > 0" class="error">
* <!-- Error message displayed when the errorCount property on the current context is greater
* than 0. -->
* {{errorCount}} errors detected
* </div>
* ```
*
* ### Syntax
*
* - `<div *ngIf="condition">...</div>`
* - `<div template="ngIf condition">...</div>`
* - `<template [ngIf]="condition"><div>...</div></template>`
*
* @stable
*/
@Directive({selector: '[ngIf]'})
export class NgIf {
private _prevCondition: boolean = null;
constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef<Object>) {
}
@Input()
set ngIf(newCondition: any) {
if (newCondition && (isBlank(this._prevCondition) || !this._prevCondition)) {
this._prevCondition = true;
this._viewContainer.createEmbeddedView(this._templateRef);
} else if (!newCondition && (isBlank(this._prevCondition) || this._prevCondition)) {
this._prevCondition = false;
this._viewContainer.clear();
}
}
}

View File

@ -0,0 +1,118 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Attribute, Directive, Host, Input, OnInit, TemplateRef, ViewContainerRef} from '@angular/core';
import {isPresent} from '../facade/lang';
import {NgLocalization, getPluralCategory} from '../localization';
import {SwitchView} from './ng_switch';
/**
* `ngPlural` is an i18n directive that displays DOM sub-trees that match the switch expression
* value, or failing that, DOM sub-trees that match the switch expression's pluralization category.
*
* To use this directive, you must provide an extension of `NgLocalization` that maps values to
* category names. You then define a container element that sets the `[ngPlural]` attribute to a
* switch expression.
* - Inner elements defined with an `[ngPluralCase]` attribute will display based on their
* expression.
* - If `[ngPluralCase]` is set to a value starting with `=`, it will only display if the value
* matches the switch expression exactly.
* - Otherwise, the view will be treated as a "category match", and will only display if exact
* value matches aren't found and the value maps to its category using the `getPluralCategory`
* function provided.
*
* ```typescript
* class MyLocalization extends NgLocalization {
* getPluralCategory(value: any) {
* if(value < 5) {
* return 'few';
* }
* }
* }
*
* @Component({
* selector: 'app',
* providers: [{provide: NgLocalization, useClass: MyLocalization}]
* })
* @View({
* template: `
* <p>Value = {{value}}</p>
* <button (click)="inc()">Increment</button>
*
* <div [ngPlural]="value">
* <template ngPluralCase="=0">there is nothing</template>
* <template ngPluralCase="=1">there is one</template>
* <template ngPluralCase="few">there are a few</template>
* <template ngPluralCase="other">there is some number</template>
* </div>
* `,
* directives: [NgPlural, NgPluralCase]
* })
* export class App {
* value = 'init';
*
* inc() {
* this.value = this.value === 'init' ? 0 : this.value + 1;
* }
* }
*
* ```
* @experimental
*/
@Directive({selector: '[ngPlural]'})
export class NgPlural {
private _switchValue: number;
private _activeView: SwitchView;
private _caseViews: {[k: string]: SwitchView} = {};
constructor(private _localization: NgLocalization) {}
@Input()
set ngPlural(value: number) {
this._switchValue = value;
this._updateView();
}
addCase(value: string, switchView: SwitchView): void { this._caseViews[value] = switchView; }
/** @internal */
_updateView(): void {
this._clearViews();
var key =
getPluralCategory(this._switchValue, Object.keys(this._caseViews), this._localization);
this._activateView(this._caseViews[key]);
}
/** @internal */
_clearViews() {
if (isPresent(this._activeView)) this._activeView.destroy();
}
/** @internal */
_activateView(view: SwitchView) {
if (!isPresent(view)) return;
this._activeView = view;
this._activeView.create();
}
}
/**
* @experimental
*/
@Directive({selector: '[ngPluralCase]'})
export class NgPluralCase {
constructor(
@Attribute('ngPluralCase') public value: string, template: TemplateRef<Object>,
viewContainer: ViewContainerRef, @Host() ngPlural: NgPlural) {
ngPlural.addCase(value, new SwitchView(viewContainer, template));
}
}

View File

@ -0,0 +1,112 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
import {isBlank, isPresent} from '../facade/lang';
/**
* The `NgStyle` directive changes styles based on a result of expression evaluation.
*
* An expression assigned to the `ngStyle` property must evaluate to an object and the
* corresponding element styles are updated based on changes to this object. Style names to update
* are taken from the object's keys, and values - from the corresponding object's values.
*
* ### Syntax
*
* - `<div [ngStyle]="{'font-style': styleExp}"></div>`
* - `<div [ngStyle]="{'max-width.px': widthExp}"></div>`
* - `<div [ngStyle]="styleExp"></div>` - here the `styleExp` must evaluate to an object
*
* ### Example ([live demo](http://plnkr.co/edit/YamGS6GkUh9GqWNQhCyM?p=preview)):
*
* ```
* import {Component} from '@angular/core';
* import {NgStyle} from '@angular/common';
*
* @Component({
* selector: 'ngStyle-example',
* template: `
* <h1 [ngStyle]="{'font-style': style, 'font-size': size, 'font-weight': weight}">
* Change style of this text!
* </h1>
*
* <hr>
*
* <label>Italic: <input type="checkbox" (change)="changeStyle($event)"></label>
* <label>Bold: <input type="checkbox" (change)="changeWeight($event)"></label>
* <label>Size: <input type="text" [value]="size" (change)="size = $event.target.value"></label>
* `,
* directives: [NgStyle]
* })
* export class NgStyleExample {
* style = 'normal';
* weight = 'normal';
* size = '20px';
*
* changeStyle($event: any) {
* this.style = $event.target.checked ? 'italic' : 'normal';
* }
*
* changeWeight($event: any) {
* this.weight = $event.target.checked ? 'bold' : 'normal';
* }
* }
* ```
*
* In this example the `font-style`, `font-size` and `font-weight` styles will be updated
* based on the `style` property's value changes.
*
* @stable
*/
@Directive({selector: '[ngStyle]'})
export class NgStyle implements DoCheck {
/** @internal */
_ngStyle: {[key: string]: string};
/** @internal */
_differ: KeyValueDiffer;
constructor(
private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer) {}
@Input()
set ngStyle(v: {[key: string]: string}) {
this._ngStyle = v;
if (isBlank(this._differ) && isPresent(v)) {
this._differ = this._differs.find(this._ngStyle).create(null);
}
}
ngDoCheck() {
if (isPresent(this._differ)) {
var changes = this._differ.diff(this._ngStyle);
if (isPresent(changes)) {
this._applyChanges(changes);
}
}
}
private _applyChanges(changes: any): void {
changes.forEachRemovedItem(
(record: KeyValueChangeRecord) => { 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); });
}
private _setStyle(name: string, val: string): void {
const nameParts = name.split('.');
const nameToSet = nameParts[0];
const valToSet = isPresent(val) && nameParts.length === 2 ? `${val}${nameParts[1]}` : val;
this._renderer.setElementStyle(this._ngEl.nativeElement, nameToSet, valToSet);
}
}

View File

@ -0,0 +1,235 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, Host, Input, TemplateRef, ViewContainerRef} from '@angular/core';
import {ListWrapper} from '../facade/collection';
import {isBlank, isPresent, normalizeBlank} from '../facade/lang';
const _CASE_DEFAULT = new Object();
// TODO: remove when fully deprecated
let _warned: boolean = false;
export class SwitchView {
constructor(
private _viewContainerRef: ViewContainerRef, private _templateRef: TemplateRef<Object>) {}
create(): void { this._viewContainerRef.createEmbeddedView(this._templateRef); }
destroy(): void { this._viewContainerRef.clear(); }
}
/**
* Adds or removes DOM sub-trees when their match expressions match the switch expression.
*
* Elements within `NgSwitch` but without `NgSwitchCase` or `NgSwitchDefault` directives will be
* preserved at the location as specified in the template.
*
* `NgSwitch` simply inserts nested elements based on which match expression matches the value
* obtained from the evaluated switch expression. In other words, you define a container element
* (where you place the directive with a switch expression on the
* `[ngSwitch]="..."` attribute), define any inner elements inside of the directive and
* place a `[ngSwitchCase]` attribute per element.
*
* The `ngSwitchCase` property is used to inform `NgSwitch` which element to display when the
* expression is evaluated. If a matching expression is not found via a `ngSwitchCase` property
* then an element with the `ngSwitchDefault` attribute is displayed.
*
* ### Example ([live demo](http://plnkr.co/edit/DQMTII95CbuqWrl3lYAs?p=preview))
*
* ```typescript
* @Component({
* selector: 'app',
* template: `
* <p>Value = {{value}}</p>
* <button (click)="inc()">Increment</button>
*
* <div [ngSwitch]="value">
* <p *ngSwitchCase="'init'">increment to start</p>
* <p *ngSwitchCase="0">0, increment again</p>
* <p *ngSwitchCase="1">1, increment again</p>
* <p *ngSwitchCase="2">2, stop incrementing</p>
* <p *ngSwitchDefault>&gt; 2, STOP!</p>
* </div>
*
* <!-- alternate syntax -->
*
* <p [ngSwitch]="value">
* <template ngSwitchCase="init">increment to start</template>
* <template [ngSwitchCase]="0">0, increment again</template>
* <template [ngSwitchCase]="1">1, increment again</template>
* <template [ngSwitchCase]="2">2, stop incrementing</template>
* <template ngSwitchDefault>&gt; 2, STOP!</template>
* </p>
* `,
* directives: [NgSwitch, NgSwitchCase, NgSwitchDefault]
* })
* export class App {
* value = 'init';
*
* inc() {
* this.value = this.value === 'init' ? 0 : this.value + 1;
* }
* }
*
* bootstrap(App).catch(err => console.error(err));
* ```
*
* @experimental
*/
@Directive({selector: '[ngSwitch]'})
export class NgSwitch {
private _switchValue: any;
private _useDefault: boolean = false;
private _valueViews = new Map<any, SwitchView[]>();
private _activeViews: SwitchView[] = [];
@Input()
set ngSwitch(value: any) {
// Empty the currently active ViewContainers
this._emptyAllActiveViews();
// Add the ViewContainers matching the value (with a fallback to default)
this._useDefault = false;
var views = this._valueViews.get(value);
if (isBlank(views)) {
this._useDefault = true;
views = normalizeBlank(this._valueViews.get(_CASE_DEFAULT));
}
this._activateViews(views);
this._switchValue = value;
}
/** @internal */
_onCaseValueChanged(oldCase: any, newCase: any, view: SwitchView): void {
this._deregisterView(oldCase, view);
this._registerView(newCase, view);
if (oldCase === this._switchValue) {
view.destroy();
ListWrapper.remove(this._activeViews, view);
} else if (newCase === this._switchValue) {
if (this._useDefault) {
this._useDefault = false;
this._emptyAllActiveViews();
}
view.create();
this._activeViews.push(view);
}
// Switch to default when there is no more active ViewContainers
if (this._activeViews.length === 0 && !this._useDefault) {
this._useDefault = true;
this._activateViews(this._valueViews.get(_CASE_DEFAULT));
}
}
/** @internal */
_emptyAllActiveViews(): void {
var activeContainers = this._activeViews;
for (var i = 0; i < activeContainers.length; i++) {
activeContainers[i].destroy();
}
this._activeViews = [];
}
/** @internal */
_activateViews(views: SwitchView[]): void {
// TODO(vicb): assert(this._activeViews.length === 0);
if (isPresent(views)) {
for (var i = 0; i < views.length; i++) {
views[i].create();
}
this._activeViews = views;
}
}
/** @internal */
_registerView(value: any, view: SwitchView): void {
var views = this._valueViews.get(value);
if (isBlank(views)) {
views = [];
this._valueViews.set(value, views);
}
views.push(view);
}
/** @internal */
_deregisterView(value: any, view: SwitchView): void {
// `_CASE_DEFAULT` is used a marker for non-registered cases
if (value === _CASE_DEFAULT) return;
var views = this._valueViews.get(value);
if (views.length == 1) {
this._valueViews.delete(value);
} else {
ListWrapper.remove(views, view);
}
}
}
/**
* Insert the sub-tree when the `ngSwitchCase` expression evaluates to the same value as the
* enclosing switch expression.
*
* If multiple match expression match the switch expression value, all of them are displayed.
*
* See {@link NgSwitch} for more details and example.
*
* @experimental
*/
@Directive({selector: '[ngSwitchCase],[ngSwitchWhen]'})
export class NgSwitchCase {
// `_CASE_DEFAULT` is used as a marker for a not yet initialized value
/** @internal */
_value: any = _CASE_DEFAULT;
/** @internal */
_view: SwitchView;
private _switch: NgSwitch;
constructor(
viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
@Host() ngSwitch: NgSwitch) {
this._switch = ngSwitch;
this._view = new SwitchView(viewContainer, templateRef);
}
@Input()
set ngSwitchCase(value: any) {
this._switch._onCaseValueChanged(this._value, value, this._view);
this._value = value;
}
@Input()
set ngSwitchWhen(value: any) {
if (!_warned) {
_warned = true;
console.warn('*ngSwitchWhen is deprecated and will be removed. Use *ngSwitchCase instead');
}
this._switch._onCaseValueChanged(this._value, value, this._view);
this._value = value;
}
}
/**
* Default case statements are displayed when no match expression matches the switch expression
* value.
*
* See {@link NgSwitch} for more details and example.
*
* @experimental
*/
@Directive({selector: '[ngSwitchDefault]'})
export class NgSwitchDefault {
constructor(
viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
@Host() sswitch: NgSwitch) {
sswitch._registerView(_CASE_DEFAULT, new SwitchView(viewContainer, templateRef));
}
}

View File

@ -0,0 +1,52 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, EmbeddedViewRef, Input, OnChanges, TemplateRef, ViewContainerRef} from '@angular/core';
/**
* Creates and inserts an embedded view based on a prepared `TemplateRef`.
* You can attach a context object to the `EmbeddedViewRef` by setting `[ngOutletContext]`.
* `[ngOutletContext]` should be an object, the object's keys will be the local template variables
* available within the `TemplateRef`.
*
* Note: using the key `$implicit` in the context object will set it's value as default.
*
* ### Syntax
*
* ```
* <template [ngTemplateOutlet]="templateRefExpression"
* [ngOutletContext]="objectExpression">
* </template>
* ```
*
* @experimental
*/
@Directive({selector: '[ngTemplateOutlet]'})
export class NgTemplateOutlet implements OnChanges {
private _viewRef: EmbeddedViewRef<any>;
private _context: Object;
private _templateRef: TemplateRef<any>;
constructor(private _viewContainerRef: ViewContainerRef) {}
@Input()
set ngOutletContext(context: Object) { this._context = context; }
@Input()
set ngTemplateOutlet(templateRef: TemplateRef<Object>) { this._templateRef = templateRef; }
ngOnChanges() {
if (this._viewRef) {
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._viewRef));
}
if (this._templateRef) {
this._viewRef = this._viewContainerRef.createEmbeddedView(this._templateRef, this._context);
}
}
}

View File

@ -0,0 +1 @@
../../facade/src

View File

@ -0,0 +1,75 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* @module
* @description
* This module is used for handling user input, by defining and building a {@link ControlGroup} that
* consists of
* {@link Control} objects, and mapping them onto the DOM. {@link Control} objects can then be used
* to read information
* from the form DOM elements.
*
* Forms providers are not included in default providers; you must import these providers
* explicitly.
*/
import {NgModule, Type} from '@angular/core';
import {FORM_DIRECTIVES} from './forms-deprecated/directives';
import {RadioControlRegistry} from './forms-deprecated/directives/radio_control_value_accessor';
import {FormBuilder} from './forms-deprecated/form_builder';
export {FORM_DIRECTIVES, RadioButtonState} from './forms-deprecated/directives';
export {AbstractControlDirective} from './forms-deprecated/directives/abstract_control_directive';
export {CheckboxControlValueAccessor} from './forms-deprecated/directives/checkbox_value_accessor';
export {ControlContainer} from './forms-deprecated/directives/control_container';
export {ControlValueAccessor, NG_VALUE_ACCESSOR} from './forms-deprecated/directives/control_value_accessor';
export {DefaultValueAccessor} from './forms-deprecated/directives/default_value_accessor';
export {Form} from './forms-deprecated/directives/form_interface';
export {NgControl} from './forms-deprecated/directives/ng_control';
export {NgControlGroup} from './forms-deprecated/directives/ng_control_group';
export {NgControlName} from './forms-deprecated/directives/ng_control_name';
export {NgControlStatus} from './forms-deprecated/directives/ng_control_status';
export {NgForm} from './forms-deprecated/directives/ng_form';
export {NgFormControl} from './forms-deprecated/directives/ng_form_control';
export {NgFormModel} from './forms-deprecated/directives/ng_form_model';
export {NgModel} from './forms-deprecated/directives/ng_model';
export {NgSelectOption, SelectControlValueAccessor} from './forms-deprecated/directives/select_control_value_accessor';
export {MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator, Validator} from './forms-deprecated/directives/validators';
export {FormBuilder} from './forms-deprecated/form_builder';
export {AbstractControl, Control, ControlArray, ControlGroup} from './forms-deprecated/model';
export {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from './forms-deprecated/validators';
/**
* Shorthand set of providers used for building Angular forms.
*
* ### Example
*
* ```typescript
* bootstrap(MyApp, [FORM_PROVIDERS]);
* ```
*
* @experimental
*/
export const FORM_PROVIDERS: Type[] = [FormBuilder, RadioControlRegistry];
/**
* The ng module for the deprecated forms API.
* @deprecated
*/
@NgModule({
providers: [
FORM_PROVIDERS,
],
declarations: FORM_DIRECTIVES,
exports: FORM_DIRECTIVES
})
export class DeprecatedFormsModule {
}

View File

@ -0,0 +1,84 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Type} from '@angular/core';
import {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
import {DefaultValueAccessor} from './directives/default_value_accessor';
import {NgControlGroup} from './directives/ng_control_group';
import {NgControlName} from './directives/ng_control_name';
import {NgControlStatus} from './directives/ng_control_status';
import {NgForm} from './directives/ng_form';
import {NgFormControl} from './directives/ng_form_control';
import {NgFormModel} from './directives/ng_form_model';
import {NgModel} from './directives/ng_model';
import {NumberValueAccessor} from './directives/number_value_accessor';
import {RadioControlValueAccessor} from './directives/radio_control_value_accessor';
import {NgSelectOption, SelectControlValueAccessor} from './directives/select_control_value_accessor';
import {NgSelectMultipleOption, SelectMultipleControlValueAccessor} from './directives/select_multiple_control_value_accessor';
import {MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator} from './directives/validators';
export {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
export {ControlValueAccessor} from './directives/control_value_accessor';
export {DefaultValueAccessor} from './directives/default_value_accessor';
export {NgControl} from './directives/ng_control';
export {NgControlGroup} from './directives/ng_control_group';
export {NgControlName} from './directives/ng_control_name';
export {NgControlStatus} from './directives/ng_control_status';
export {NgForm} from './directives/ng_form';
export {NgFormControl} from './directives/ng_form_control';
export {NgFormModel} from './directives/ng_form_model';
export {NgModel} from './directives/ng_model';
export {NumberValueAccessor} from './directives/number_value_accessor';
export {RadioButtonState, RadioControlValueAccessor} from './directives/radio_control_value_accessor';
export {NgSelectOption, SelectControlValueAccessor} from './directives/select_control_value_accessor';
export {NgSelectMultipleOption, SelectMultipleControlValueAccessor} from './directives/select_multiple_control_value_accessor';
export {MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator} from './directives/validators';
/**
*
* A list of all the form directives used as part of a `@Component` annotation.
*
* This is a shorthand for importing them each individually.
*
* ### Example
*
* ```typescript
* @Component({
* selector: 'my-app',
* directives: [FORM_DIRECTIVES]
* })
* class MyApp {}
* ```
* @experimental
*/
export const FORM_DIRECTIVES: Type[] = [
NgControlName,
NgControlGroup,
NgFormControl,
NgModel,
NgFormModel,
NgForm,
NgSelectOption,
NgSelectMultipleOption,
DefaultValueAccessor,
NumberValueAccessor,
CheckboxControlValueAccessor,
SelectControlValueAccessor,
SelectMultipleControlValueAccessor,
RadioControlValueAccessor,
NgControlStatus,
RequiredValidator,
MinLengthValidator,
MaxLengthValidator,
PatternValidator,
];

View File

@ -0,0 +1,41 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {unimplemented} from '../../facade/exceptions';
import {isPresent} from '../../facade/lang';
import {AbstractControl} from '../model';
/**
* Base class for control directives.
*
* Only used internally in the forms module.
*
* @experimental
*/
export abstract class AbstractControlDirective {
get control(): AbstractControl { return unimplemented(); }
get value(): any { return isPresent(this.control) ? this.control.value : null; }
get valid(): boolean { return isPresent(this.control) ? this.control.valid : null; }
get errors(): {[key: string]: any} {
return isPresent(this.control) ? this.control.errors : null;
}
get pristine(): boolean { return isPresent(this.control) ? this.control.pristine : null; }
get dirty(): boolean { return isPresent(this.control) ? this.control.dirty : null; }
get touched(): boolean { return isPresent(this.control) ? this.control.touched : null; }
get untouched(): boolean { return isPresent(this.control) ? this.control.untouched : null; }
get path(): string[] { return null; }
}

View File

@ -0,0 +1,46 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, ElementRef, Renderer, forwardRef} from '@angular/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
export const CHECKBOX_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CheckboxControlValueAccessor),
multi: true
};
/**
* The accessor for writing a value and listening to changes on a checkbox input element.
*
* ### Example
* ```
* <input type="checkbox" ngControl="rememberLogin">
* ```
*
* @experimental
*/
@Directive({
selector:
'input[type=checkbox][ngControl],input[type=checkbox][ngFormControl],input[type=checkbox][ngModel]',
host: {'(change)': 'onChange($event.target.checked)', '(blur)': 'onTouched()'},
providers: [CHECKBOX_VALUE_ACCESSOR]
})
export class CheckboxControlValueAccessor implements ControlValueAccessor {
onChange = (_: any) => {};
onTouched = () => {};
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
writeValue(value: any): void {
this._renderer.setElementProperty(this._elementRef.nativeElement, 'checked', value);
}
registerOnChange(fn: (_: any) => {}): void { this.onChange = fn; }
registerOnTouched(fn: () => {}): void { this.onTouched = fn; }
}

View File

@ -0,0 +1,32 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {AbstractControlDirective} from './abstract_control_directive';
import {Form} from './form_interface';
/**
* A directive that contains multiple {@link NgControl}s.
*
* Only used by the forms module.
*
* @experimental
*/
export class ControlContainer extends AbstractControlDirective {
name: string;
/**
* Get the form to which this container belongs.
*/
get formDirective(): Form { return null; }
/**
* Get the path to this container.
*/
get path(): string[] { return null; }
}

View File

@ -0,0 +1,44 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {OpaqueToken} from '@angular/core';
/**
* A bridge between a control and a native element.
*
* A `ControlValueAccessor` abstracts the operations of writing a new value to a
* DOM element representing an input control.
*
* Please see {@link DefaultValueAccessor} for more information.
*
* @experimental
*/
export interface ControlValueAccessor {
/**
* Write a new value to the element.
*/
writeValue(obj: any): void;
/**
* Set the function to be called when the control receives a change event.
*/
registerOnChange(fn: any): void;
/**
* Set the function to be called when the control receives a touch event.
*/
registerOnTouched(fn: any): void;
}
/**
* Used to provide a {@link ControlValueAccessor} for form controls.
*
* See {@link DefaultValueAccessor} for how to implement one.
* @experimental
*/
export const NG_VALUE_ACCESSOR: OpaqueToken = new OpaqueToken('NgValueAccessor');

View File

@ -0,0 +1,54 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, ElementRef, Renderer, forwardRef} from '@angular/core';
import {isBlank} from '../../facade/lang';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
export const DEFAULT_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DefaultValueAccessor),
multi: true
};
/**
* The default accessor for writing a value and listening to changes that is used by the
* {@link NgModel}, {@link NgFormControl}, and {@link NgControlName} directives.
*
* ### Example
* ```
* <input type="text" ngControl="searchQuery">
* ```
*
* @experimental
*/
@Directive({
selector:
'input:not([type=checkbox])[ngControl],textarea[ngControl],input:not([type=checkbox])[ngFormControl],textarea[ngFormControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]',
// TODO: vsavkin replace the above selector with the one below it once
// https://github.com/angular/angular/issues/3011 is implemented
// selector: '[ngControl],[ngModel],[ngFormControl]',
host: {'(input)': 'onChange($event.target.value)', '(blur)': 'onTouched()'},
providers: [DEFAULT_VALUE_ACCESSOR]
})
export class DefaultValueAccessor implements ControlValueAccessor {
onChange = (_: any) => {};
onTouched = () => {};
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
writeValue(value: any): void {
var normalizedValue = isBlank(value) ? '' : value;
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', normalizedValue);
}
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
}

View File

@ -0,0 +1,57 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Control, ControlGroup} from '../model';
import {NgControl} from './ng_control';
import {NgControlGroup} from './ng_control_group';
/**
* An interface that {@link NgFormModel} and {@link NgForm} implement.
*
* Only used by the forms module.
*
* @experimental
*/
export interface Form {
/**
* Add a control to this form.
*/
addControl(dir: NgControl): void;
/**
* Remove a control from this form.
*/
removeControl(dir: NgControl): void;
/**
* Look up the {@link Control} associated with a particular {@link NgControl}.
*/
getControl(dir: NgControl): Control;
/**
* Add a group of controls to this form.
*/
addControlGroup(dir: NgControlGroup): void;
/**
* Remove a group of controls from this form.
*/
removeControlGroup(dir: NgControlGroup): void;
/**
* Look up the {@link ControlGroup} associated with a particular {@link NgControlGroup}.
*/
getControlGroup(dir: NgControlGroup): ControlGroup;
/**
* Update the model for a particular control with a new value.
*/
updateModel(dir: NgControl, value: any): void;
}

View File

@ -0,0 +1,32 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {unimplemented} from '../../facade/exceptions';
import {AbstractControlDirective} from './abstract_control_directive';
import {ControlValueAccessor} from './control_value_accessor';
import {AsyncValidatorFn, ValidatorFn} from './validators';
/**
* A base class that all control directive extend.
* It binds a {@link Control} object to a DOM element.
*
* Used internally by Angular forms.
*
* @experimental
*/
export abstract class NgControl extends AbstractControlDirective {
name: string = null;
valueAccessor: ControlValueAccessor = null;
get validator(): ValidatorFn { return <ValidatorFn>unimplemented(); }
get asyncValidator(): AsyncValidatorFn { return <AsyncValidatorFn>unimplemented(); }
abstract viewToModelUpdate(newValue: any): void;
}

View File

@ -0,0 +1,111 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, Host, Inject, OnDestroy, OnInit, Optional, Self, SkipSelf, forwardRef} from '@angular/core';
import {ControlGroup} from '../model';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators';
import {ControlContainer} from './control_container';
import {Form} from './form_interface';
import {composeAsyncValidators, composeValidators, controlPath} from './shared';
import {AsyncValidatorFn, ValidatorFn} from './validators';
export const controlGroupProvider: any = {
provide: ControlContainer,
useExisting: forwardRef(() => NgControlGroup)
};
/**
* Creates and binds a control group to a DOM element.
*
* This directive can only be used as a child of {@link NgForm} or {@link NgFormModel}.
*
* ### Example ([live demo](http://plnkr.co/edit/7EJ11uGeaggViYM6T5nq?p=preview))
*
* ```typescript
* @Component({
* selector: 'my-app',
* template: `
* <div>
* <h2>Angular Control &amp; ControlGroup Example</h2>
* <form #f="ngForm">
* <div ngControlGroup="name" #cgName="ngForm">
* <h3>Enter your name:</h3>
* <p>First: <input ngControl="first" required></p>
* <p>Middle: <input ngControl="middle"></p>
* <p>Last: <input ngControl="last" required></p>
* </div>
* <h3>Name value:</h3>
* <pre>{{valueOf(cgName)}}</pre>
* <p>Name is {{cgName?.control?.valid ? "valid" : "invalid"}}</p>
* <h3>What's your favorite food?</h3>
* <p><input ngControl="food"></p>
* <h3>Form value</h3>
* <pre>{{valueOf(f)}}</pre>
* </form>
* </div>
* `
* })
* export class App {
* valueOf(cg: NgControlGroup): string {
* if (cg.control == null) {
* return null;
* }
* return JSON.stringify(cg.control.value, null, 2);
* }
* }
* ```
*
* This example declares a control group for a user's name. The value and validation state of
* this group can be accessed separately from the overall form.
*
* @experimental
*/
@Directive({
selector: '[ngControlGroup]',
providers: [controlGroupProvider],
inputs: ['name: ngControlGroup'],
exportAs: 'ngForm'
})
export class NgControlGroup extends ControlContainer implements OnInit,
OnDestroy {
/** @internal */
_parent: ControlContainer;
constructor(
@Host() @SkipSelf() parent: ControlContainer,
@Optional() @Self() @Inject(NG_VALIDATORS) private _validators: any[],
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) private _asyncValidators: any[]) {
super();
this._parent = parent;
}
ngOnInit(): void { this.formDirective.addControlGroup(this); }
ngOnDestroy(): void { this.formDirective.removeControlGroup(this); }
/**
* Get the {@link ControlGroup} backing this binding.
*/
get control(): ControlGroup { return this.formDirective.getControlGroup(this); }
/**
* Get the path to this control group.
*/
get path(): string[] { return controlPath(this.name, this._parent); }
/**
* Get the {@link Form} to which this group belongs.
*/
get formDirective(): Form { return this._parent.formDirective; }
get validator(): ValidatorFn { return composeValidators(this._validators); }
get asyncValidator(): AsyncValidatorFn { return composeAsyncValidators(this._asyncValidators); }
}

View File

@ -0,0 +1,139 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, Host, Inject, OnChanges, OnDestroy, Optional, Self, SimpleChanges, SkipSelf, forwardRef} from '@angular/core';
import {EventEmitter} from '../../facade/async';
import {Control} from '../model';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators';
import {ControlContainer} from './control_container';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
import {NgControl} from './ng_control';
import {composeAsyncValidators, composeValidators, controlPath, isPropertyUpdated, selectValueAccessor} from './shared';
import {AsyncValidatorFn, ValidatorFn} from './validators';
export const controlNameBinding: any = {
provide: NgControl,
useExisting: forwardRef(() => NgControlName)
};
/**
* Creates and binds a control with a specified name to a DOM element.
*
* This directive can only be used as a child of {@link NgForm} or {@link NgFormModel}.
* ### Example
*
* In this example, we create the login and password controls.
* We can work with each control separately: check its validity, get its value, listen to its
* changes.
*
* ```
* @Component({
* selector: "login-comp",
* directives: [FORM_DIRECTIVES],
* template: `
* <form #f="ngForm" (submit)='onLogIn(f.value)'>
* Login <input type='text' ngControl='login' #l="ngForm">
* <div *ngIf="!l.valid">Login is invalid</div>
*
* Password <input type='password' ngControl='password'>
* <button type='submit'>Log in!</button>
* </form>
* `})
* class LoginComp {
* onLogIn(value): void {
* // value === {login: 'some login', password: 'some password'}
* }
* }
* ```
*
* We can also use ngModel to bind a domain model to the form.
*
* ```
* @Component({
* selector: "login-comp",
* directives: [FORM_DIRECTIVES],
* template: `
* <form (submit)='onLogIn()'>
* Login <input type='text' ngControl='login' [(ngModel)]="credentials.login">
* Password <input type='password' ngControl='password'
* [(ngModel)]="credentials.password">
* <button type='submit'>Log in!</button>
* </form>
* `})
* class LoginComp {
* credentials: {login:string, password:string};
*
* onLogIn(): void {
* // this.credentials.login === "some login"
* // this.credentials.password === "some password"
* }
* }
* ```
*
* @experimental
*/
@Directive({
selector: '[ngControl]',
providers: [controlNameBinding],
inputs: ['name: ngControl', 'model: ngModel'],
outputs: ['update: ngModelChange'],
exportAs: 'ngForm'
})
export class NgControlName extends NgControl implements OnChanges,
OnDestroy {
/** @internal */
update = new EventEmitter();
model: any;
viewModel: any;
private _added = false;
constructor(@Host() @SkipSelf() private _parent: ControlContainer,
@Optional() @Self() @Inject(NG_VALIDATORS) private _validators:
/* Array<Validator|Function> */ any[],
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) private _asyncValidators:
/* Array<Validator|Function> */ any[],
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR)
valueAccessors: ControlValueAccessor[]) {
super();
this.valueAccessor = selectValueAccessor(this, valueAccessors);
}
ngOnChanges(changes: SimpleChanges) {
if (!this._added) {
this.formDirective.addControl(this);
this._added = true;
}
if (isPropertyUpdated(changes, this.viewModel)) {
this.viewModel = this.model;
this.formDirective.updateModel(this, this.model);
}
}
ngOnDestroy(): void { this.formDirective.removeControl(this); }
viewToModelUpdate(newValue: any): void {
this.viewModel = newValue;
this.update.emit(newValue);
}
get path(): string[] { return controlPath(this.name, this._parent); }
get formDirective(): any { return this._parent.formDirective; }
get validator(): ValidatorFn { return composeValidators(this._validators); }
get asyncValidator(): AsyncValidatorFn {
return composeAsyncValidators(this._asyncValidators);
}
get control(): Control { return this.formDirective.getControl(this); }
}

View File

@ -0,0 +1,56 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, Self} from '@angular/core';
import {isPresent} from '../../facade/lang';
import {NgControl} from './ng_control';
/**
* Directive automatically applied to Angular forms that sets CSS classes
* based on control status (valid/invalid/dirty/etc).
*
* @experimental
*/
@Directive({
selector: '[ngControl],[ngModel],[ngFormControl]',
host: {
'[class.ng-untouched]': 'ngClassUntouched',
'[class.ng-touched]': 'ngClassTouched',
'[class.ng-pristine]': 'ngClassPristine',
'[class.ng-dirty]': 'ngClassDirty',
'[class.ng-valid]': 'ngClassValid',
'[class.ng-invalid]': 'ngClassInvalid'
}
})
export class NgControlStatus {
private _cd: NgControl;
constructor(@Self() cd: NgControl) { this._cd = cd; }
get ngClassUntouched(): boolean {
return isPresent(this._cd.control) ? this._cd.control.untouched : false;
}
get ngClassTouched(): boolean {
return isPresent(this._cd.control) ? this._cd.control.touched : false;
}
get ngClassPristine(): boolean {
return isPresent(this._cd.control) ? this._cd.control.pristine : false;
}
get ngClassDirty(): boolean {
return isPresent(this._cd.control) ? this._cd.control.dirty : false;
}
get ngClassValid(): boolean {
return isPresent(this._cd.control) ? this._cd.control.valid : false;
}
get ngClassInvalid(): boolean {
return isPresent(this._cd.control) ? !this._cd.control.valid : false;
}
}

View File

@ -0,0 +1,202 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, Inject, Optional, Self, forwardRef} from '@angular/core';
import {EventEmitter} from '../../facade/async';
import {ListWrapper} from '../../facade/collection';
import {isPresent} from '../../facade/lang';
import {AbstractControl, Control, ControlGroup} from '../model';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators';
import {ControlContainer} from './control_container';
import {Form} from './form_interface';
import {NgControl} from './ng_control';
import {NgControlGroup} from './ng_control_group';
import {composeAsyncValidators, composeValidators, setUpControl, setUpControlGroup} from './shared';
export const formDirectiveProvider: any = {
provide: ControlContainer,
useExisting: forwardRef(() => NgForm)
};
let _formWarningDisplayed: boolean = false;
const resolvedPromise = Promise.resolve(null);
/**
* If `NgForm` is bound in a component, `<form>` elements in that component will be
* upgraded to use the Angular form system.
*
* ### Typical Use
*
* Include `FORM_DIRECTIVES` in the `directives` section of a {@link Component} annotation
* to use `NgForm` and its associated controls.
*
* ### Structure
*
* An Angular form is a collection of `Control`s in some hierarchy.
* `Control`s can be at the top level or can be organized in `ControlGroup`s
* or `ControlArray`s. This hierarchy is reflected in the form's `value`, a
* JSON object that mirrors the form structure.
*
* ### Submission
*
* The `ngSubmit` event signals when the user triggers a form submission.
*
* ### Example ([live demo](http://plnkr.co/edit/ltdgYj4P0iY64AR71EpL?p=preview))
*
* ```typescript
* @Component({
* selector: 'my-app',
* template: `
* <div>
* <p>Submit the form to see the data object Angular builds</p>
* <h2>NgForm demo</h2>
* <form #f="ngForm" (ngSubmit)="onSubmit(f.value)">
* <h3>Control group: credentials</h3>
* <div ngControlGroup="credentials">
* <p>Login: <input type="text" ngControl="login"></p>
* <p>Password: <input type="password" ngControl="password"></p>
* </div>
* <h3>Control group: person</h3>
* <div ngControlGroup="person">
* <p>First name: <input type="text" ngControl="firstName"></p>
* <p>Last name: <input type="text" ngControl="lastName"></p>
* </div>
* <button type="submit">Submit Form</button>
* <p>Form data submitted:</p>
* </form>
* <pre>{{data}}</pre>
* </div>
* `,
* directives: [CORE_DIRECTIVES, FORM_DIRECTIVES]
* })
* export class App {
* constructor() {}
*
* data: string;
*
* onSubmit(data) {
* this.data = JSON.stringify(data, null, 2);
* }
* }
* ```
*
* @experimental
*/
@Directive({
selector: 'form:not([ngNoForm]):not([ngFormModel]),ngForm,[ngForm]',
providers: [formDirectiveProvider],
host: {
'(submit)': 'onSubmit()',
},
outputs: ['ngSubmit'],
exportAs: 'ngForm'
})
export class NgForm extends ControlContainer implements Form {
private _submitted: boolean = false;
form: ControlGroup;
ngSubmit = new EventEmitter();
constructor(
@Optional() @Self() @Inject(NG_VALIDATORS) validators: any[],
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: any[]) {
super();
this._displayWarning();
this.form = new ControlGroup(
{}, null, composeValidators(validators), composeAsyncValidators(asyncValidators));
}
private _displayWarning() {
// TODO(kara): Update this when the new forms module becomes the default
if (!_formWarningDisplayed) {
_formWarningDisplayed = true;
console.warn(`
*It looks like you're using the old forms module. This will be opt-in in the next RC, and
will eventually be removed in favor of the new forms module. For more information, see:
https://docs.google.com/document/d/1RIezQqE4aEhBRmArIAS1mRIZtWFf6JxN_7B4meyWK0Y/preview
`);
}
}
get submitted(): boolean { return this._submitted; }
get formDirective(): Form { return this; }
get control(): ControlGroup { return this.form; }
get path(): string[] { return []; }
get controls(): {[key: string]: AbstractControl} { return this.form.controls; }
addControl(dir: NgControl): void {
resolvedPromise.then(() => {
var container = this._findContainer(dir.path);
var ctrl = new Control();
setUpControl(ctrl, dir);
container.registerControl(dir.name, ctrl);
ctrl.updateValueAndValidity({emitEvent: false});
});
}
getControl(dir: NgControl): Control { return <Control>this.form.find(dir.path); }
removeControl(dir: NgControl): void {
resolvedPromise.then(() => {
var container = this._findContainer(dir.path);
if (isPresent(container)) {
container.removeControl(dir.name);
}
});
}
addControlGroup(dir: NgControlGroup): void {
resolvedPromise.then(() => {
var container = this._findContainer(dir.path);
var group = new ControlGroup({});
setUpControlGroup(group, dir);
container.registerControl(dir.name, group);
group.updateValueAndValidity({emitEvent: false});
});
}
removeControlGroup(dir: NgControlGroup): void {
resolvedPromise.then(() => {
var container = this._findContainer(dir.path);
if (isPresent(container)) {
container.removeControl(dir.name);
}
});
}
getControlGroup(dir: NgControlGroup): ControlGroup {
return <ControlGroup>this.form.find(dir.path);
}
updateModel(dir: NgControl, value: any): void {
resolvedPromise.then(() => {
var ctrl = <Control>this.form.find(dir.path);
ctrl.updateValue(value);
});
}
onSubmit(): boolean {
this._submitted = true;
this.ngSubmit.emit(null);
return false;
}
/** @internal */
_findContainer(path: string[]): ControlGroup {
path.pop();
return ListWrapper.isEmpty(path) ? this.form : <ControlGroup>this.form.find(path);
}
}

View File

@ -0,0 +1,127 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, Inject, OnChanges, Optional, Self, SimpleChanges, forwardRef} from '@angular/core';
import {EventEmitter} from '../../facade/async';
import {StringMapWrapper} from '../../facade/collection';
import {Control} from '../model';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
import {NgControl} from './ng_control';
import {composeAsyncValidators, composeValidators, isPropertyUpdated, selectValueAccessor, setUpControl} from './shared';
import {AsyncValidatorFn, ValidatorFn} from './validators';
export const formControlBinding: any = {
provide: NgControl,
useExisting: forwardRef(() => NgFormControl)
};
/**
* Binds an existing {@link Control} to a DOM element.
*
* ### Example ([live demo](http://plnkr.co/edit/jcQlZ2tTh22BZZ2ucNAT?p=preview))
*
* In this example, we bind the control to an input element. When the value of the input element
* changes, the value of the control will reflect that change. Likewise, if the value of the
* control changes, the input element reflects that change.
*
* ```typescript
* @Component({
* selector: 'my-app',
* template: `
* <div>
* <h2>NgFormControl Example</h2>
* <form>
* <p>Element with existing control: <input type="text"
* [ngFormControl]="loginControl"></p>
* <p>Value of existing control: {{loginControl.value}}</p>
* </form>
* </div>
* `,
* directives: [CORE_DIRECTIVES, FORM_DIRECTIVES]
* })
* export class App {
* loginControl: Control = new Control('');
* }
* ```
*
* ### ngModel
*
* We can also use `ngModel` to bind a domain model to the form.
*
* ### Example ([live demo](http://plnkr.co/edit/yHMLuHO7DNgT8XvtjTDH?p=preview))
*
* ```typescript
* @Component({
* selector: "login-comp",
* directives: [FORM_DIRECTIVES],
* template: "<input type='text' [ngFormControl]='loginControl' [(ngModel)]='login'>"
* })
* class LoginComp {
* loginControl: Control = new Control('');
* login:string;
* }
* ```
*
* @experimental
*/
@Directive({
selector: '[ngFormControl]',
providers: [formControlBinding],
inputs: ['form: ngFormControl', 'model: ngModel'],
outputs: ['update: ngModelChange'],
exportAs: 'ngForm'
})
export class NgFormControl extends NgControl implements OnChanges {
form: Control;
update = new EventEmitter();
model: any;
viewModel: any;
constructor(@Optional() @Self() @Inject(NG_VALIDATORS) private _validators:
/* Array<Validator|Function> */ any[],
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) private _asyncValidators:
/* Array<Validator|Function> */ any[],
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR)
valueAccessors: ControlValueAccessor[]) {
super();
this.valueAccessor = selectValueAccessor(this, valueAccessors);
}
ngOnChanges(changes: SimpleChanges): void {
if (this._isControlChanged(changes)) {
setUpControl(this.form, this);
this.form.updateValueAndValidity({emitEvent: false});
}
if (isPropertyUpdated(changes, this.viewModel)) {
this.form.updateValue(this.model);
this.viewModel = this.model;
}
}
get path(): string[] { return []; }
get validator(): ValidatorFn { return composeValidators(this._validators); }
get asyncValidator(): AsyncValidatorFn {
return composeAsyncValidators(this._asyncValidators);
}
get control(): Control { return this.form; }
viewToModelUpdate(newValue: any): void {
this.viewModel = newValue;
this.update.emit(newValue);
}
private _isControlChanged(changes: {[key: string]: any}): boolean {
return StringMapWrapper.contains(changes, 'form');
}
}

View File

@ -0,0 +1,212 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, Inject, OnChanges, Optional, Self, SimpleChanges, forwardRef} from '@angular/core';
import {EventEmitter} from '../../facade/async';
import {ListWrapper, StringMapWrapper} from '../../facade/collection';
import {BaseException} from '../../facade/exceptions';
import {isBlank} from '../../facade/lang';
import {Control, ControlGroup} from '../model';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from '../validators';
import {ControlContainer} from './control_container';
import {Form} from './form_interface';
import {NgControl} from './ng_control';
import {NgControlGroup} from './ng_control_group';
import {composeAsyncValidators, composeValidators, setUpControl, setUpControlGroup} from './shared';
export const formDirectiveProvider: any = {
provide: ControlContainer,
useExisting: forwardRef(() => NgFormModel)
};
let _formModelWarningDisplayed: boolean = false;
/**
* Binds an existing control group to a DOM element.
*
* ### Example ([live demo](http://plnkr.co/edit/jqrVirudY8anJxTMUjTP?p=preview))
*
* In this example, we bind the control group to the form element, and we bind the login and
* password controls to the login and password elements.
*
* ```typescript
* @Component({
* selector: 'my-app',
* template: `
* <div>
* <h2>NgFormModel Example</h2>
* <form [ngFormModel]="loginForm">
* <p>Login: <input type="text" ngControl="login"></p>
* <p>Password: <input type="password" ngControl="password"></p>
* </form>
* <p>Value:</p>
* <pre>{{value}}</pre>
* </div>
* `,
* directives: [FORM_DIRECTIVES]
* })
* export class App {
* loginForm: ControlGroup;
*
* constructor() {
* this.loginForm = new ControlGroup({
* login: new Control(""),
* password: new Control("")
* });
* }
*
* get value(): string {
* return JSON.stringify(this.loginForm.value, null, 2);
* }
* }
* ```
*
* We can also use ngModel to bind a domain model to the form.
*
* ```typescript
* @Component({
* selector: "login-comp",
* directives: [FORM_DIRECTIVES],
* template: `
* <form [ngFormModel]='loginForm'>
* Login <input type='text' ngControl='login' [(ngModel)]='credentials.login'>
* Password <input type='password' ngControl='password'
* [(ngModel)]='credentials.password'>
* <button (click)="onLogin()">Login</button>
* </form>`
* })
* class LoginComp {
* credentials: {login: string, password: string};
* loginForm: ControlGroup;
*
* constructor() {
* this.loginForm = new ControlGroup({
* login: new Control(""),
* password: new Control("")
* });
* }
*
* onLogin(): void {
* // this.credentials.login === 'some login'
* // this.credentials.password === 'some password'
* }
* }
* ```
*
* @experimental
*/
@Directive({
selector: '[ngFormModel]',
providers: [formDirectiveProvider],
inputs: ['form: ngFormModel'],
host: {'(submit)': 'onSubmit()'},
outputs: ['ngSubmit'],
exportAs: 'ngForm'
})
export class NgFormModel extends ControlContainer implements Form,
OnChanges {
private _submitted: boolean = false;
form: ControlGroup = null;
directives: NgControl[] = [];
ngSubmit = new EventEmitter();
constructor(
@Optional() @Self() @Inject(NG_VALIDATORS) private _validators: any[],
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) private _asyncValidators: any[]) {
super();
this._displayWarning();
}
private _displayWarning() {
// TODO(kara): Update this when the new forms module becomes the default
if (!_formModelWarningDisplayed) {
_formModelWarningDisplayed = true;
console.warn(`
*It looks like you're using the old forms module. This will be opt-in in the next RC, and
will eventually be removed in favor of the new forms module. For more information, see:
https://docs.google.com/document/d/1RIezQqE4aEhBRmArIAS1mRIZtWFf6JxN_7B4meyWK0Y/preview
`);
}
}
ngOnChanges(changes: SimpleChanges): void {
this._checkFormPresent();
if (StringMapWrapper.contains(changes, 'form')) {
var sync = composeValidators(this._validators);
this.form.validator = Validators.compose([this.form.validator, sync]);
var async = composeAsyncValidators(this._asyncValidators);
this.form.asyncValidator = Validators.composeAsync([this.form.asyncValidator, async]);
this.form.updateValueAndValidity({onlySelf: true, emitEvent: false});
}
this._updateDomValue();
}
get submitted(): boolean { return this._submitted; }
get formDirective(): Form { return this; }
get control(): ControlGroup { return this.form; }
get path(): string[] { return []; }
addControl(dir: NgControl): void {
var ctrl: any = this.form.find(dir.path);
setUpControl(ctrl, dir);
ctrl.updateValueAndValidity({emitEvent: false});
this.directives.push(dir);
}
getControl(dir: NgControl): Control { return <Control>this.form.find(dir.path); }
removeControl(dir: NgControl): void { ListWrapper.remove(this.directives, dir); }
addControlGroup(dir: NgControlGroup) {
var ctrl: any = this.form.find(dir.path);
setUpControlGroup(ctrl, dir);
ctrl.updateValueAndValidity({emitEvent: false});
}
removeControlGroup(dir: NgControlGroup) {}
getControlGroup(dir: NgControlGroup): ControlGroup {
return <ControlGroup>this.form.find(dir.path);
}
updateModel(dir: NgControl, value: any): void {
var ctrl  = <Control>this.form.find(dir.path);
ctrl.updateValue(value);
}
onSubmit(): boolean {
this._submitted = true;
this.ngSubmit.emit(null);
return false;
}
/** @internal */
_updateDomValue() {
this.directives.forEach(dir => {
var ctrl: any = this.form.find(dir.path);
dir.valueAccessor.writeValue(ctrl.value);
});
}
private _checkFormPresent() {
if (isBlank(this.form)) {
throw new BaseException(
`ngFormModel expects a form. Please pass one in. Example: <form [ngFormModel]="myCoolForm">`);
}
}
}

View File

@ -0,0 +1,99 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, Inject, OnChanges, Optional, Self, SimpleChanges, forwardRef} from '@angular/core';
import {EventEmitter} from '../../facade/async';
import {Control} from '../model';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
import {NgControl} from './ng_control';
import {composeAsyncValidators, composeValidators, isPropertyUpdated, selectValueAccessor, setUpControl} from './shared';
import {AsyncValidatorFn, ValidatorFn} from './validators';
export const formControlBinding: any = {
provide: NgControl,
useExisting: forwardRef(() => NgModel)
};
/**
* Binds a domain model to a form control.
*
* ### Usage
*
* `ngModel` binds an existing domain model to a form control. For a
* two-way binding, use `[(ngModel)]` to ensure the model updates in
* both directions.
*
* ### Example ([live demo](http://plnkr.co/edit/R3UX5qDaUqFO2VYR0UzH?p=preview))
* ```typescript
* @Component({
* selector: "search-comp",
* directives: [FORM_DIRECTIVES],
* template: `<input type='text' [(ngModel)]="searchQuery">`
* })
* class SearchComp {
* searchQuery: string;
* }
* ```
*
* @experimental
*/
@Directive({
selector: '[ngModel]:not([ngControl]):not([ngFormControl])',
providers: [formControlBinding],
inputs: ['model: ngModel'],
outputs: ['update: ngModelChange'],
exportAs: 'ngForm'
})
export class NgModel extends NgControl implements OnChanges {
/** @internal */
_control = new Control();
/** @internal */
_added = false;
update = new EventEmitter();
model: any;
viewModel: any;
constructor(@Optional() @Self() @Inject(NG_VALIDATORS) private _validators: any[],
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) private _asyncValidators: any[],
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR)
valueAccessors: ControlValueAccessor[]) {
super();
this.valueAccessor = selectValueAccessor(this, valueAccessors);
}
ngOnChanges(changes: SimpleChanges) {
if (!this._added) {
setUpControl(this._control, this);
this._control.updateValueAndValidity({emitEvent: false});
this._added = true;
}
if (isPropertyUpdated(changes, this.viewModel)) {
this._control.updateValue(this.model);
this.viewModel = this.model;
}
}
get control(): Control { return this._control; }
get path(): string[] { return []; }
get validator(): ValidatorFn { return composeValidators(this._validators); }
get asyncValidator(): AsyncValidatorFn {
return composeAsyncValidators(this._asyncValidators);
}
viewToModelUpdate(newValue: any): void {
this.viewModel = newValue;
this.update.emit(newValue);
}
}

View File

@ -0,0 +1,27 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {AbstractControl} from '../model';
import {AsyncValidatorFn, Validator, ValidatorFn} from './validators';
export function normalizeValidator(validator: ValidatorFn | Validator): ValidatorFn {
if ((<Validator>validator).validate !== undefined) {
return (c: AbstractControl) => (<Validator>validator).validate(c);
} else {
return <ValidatorFn>validator;
}
}
export function normalizeAsyncValidator(validator: AsyncValidatorFn | Validator): AsyncValidatorFn {
if ((<Validator>validator).validate !== undefined) {
return (c: AbstractControl) => (<Validator>validator).validate(c);
} else {
return <AsyncValidatorFn>validator;
}
}

View File

@ -0,0 +1,56 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, ElementRef, Renderer, forwardRef} from '@angular/core';
import {NumberWrapper, isBlank} from '../../facade/lang';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
export const NUMBER_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => NumberValueAccessor),
multi: true
};
/**
* The accessor for writing a number value and listening to changes that is used by the
* {@link NgModel}, {@link NgFormControl}, and {@link NgControlName} directives.
*
* ### Example
* ```
* <input type="number" [(ngModel)]="age">
* ```
*/
@Directive({
selector:
'input[type=number][ngControl],input[type=number][ngFormControl],input[type=number][ngModel]',
host: {
'(change)': 'onChange($event.target.value)',
'(input)': 'onChange($event.target.value)',
'(blur)': 'onTouched()'
},
providers: [NUMBER_VALUE_ACCESSOR]
})
export class NumberValueAccessor implements ControlValueAccessor {
onChange = (_: any) => {};
onTouched = () => {};
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
writeValue(value: number): void {
// The value needs to be normalized for IE9, otherwise it is set to 'null' when null
const normalizedValue = isBlank(value) ? '' : value;
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', normalizedValue);
}
registerOnChange(fn: (_: number) => void): void {
this.onChange = (value) => { fn(value == '' ? null : NumberWrapper.parseFloat(value)); };
}
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
}

View File

@ -0,0 +1,134 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, ElementRef, Injectable, Injector, Input, OnDestroy, OnInit, Renderer, forwardRef} from '@angular/core';
import {ListWrapper} from '../../facade/collection';
import {isPresent} from '../../facade/lang';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
import {NgControl} from './ng_control';
export const RADIO_VALUE_ACCESSOR: any = {
provide: 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 (this._isSameGroup(c, accessor) && c[1] !== accessor) {
c[1].fireUncheck();
}
});
}
private _isSameGroup(
controlPair: [NgControl, RadioControlValueAccessor], accessor: RadioControlValueAccessor) {
return controlPair[0].control.root === accessor._control.control.root &&
controlPair[1].name === accessor.name;
}
}
/**
* The value provided by the forms API for radio buttons.
*
* @experimental
*/
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 {
/** @internal */
_state: RadioButtonState;
/** @internal */
_control: NgControl;
@Input() name: string;
/** @internal */
_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

@ -0,0 +1,140 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, ElementRef, Host, Input, OnDestroy, Optional, Renderer, forwardRef} from '@angular/core';
import {MapWrapper} from '../../facade/collection';
import {StringWrapper, isBlank, isPresent, isPrimitive, looseIdentical} from '../../facade/lang';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
export const SELECT_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SelectControlValueAccessor),
multi: true
};
function _buildValueString(id: string, value: any): string {
if (isBlank(id)) return `${value}`;
if (!isPrimitive(value)) value = 'Object';
return StringWrapper.slice(`${id}: ${value}`, 0, 50);
}
function _extractId(valueString: string): string {
return valueString.split(':')[0];
}
/**
* The accessor for writing a value and listening to changes on a select element.
*
* Note: We have to listen to the 'change' event because 'input' events aren't fired
* for selects in Firefox and IE:
* https://bugzilla.mozilla.org/show_bug.cgi?id=1024350
* https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/4660045/
*
* @experimental
*/
@Directive({
selector:
'select:not([multiple])[ngControl],select:not([multiple])[ngFormControl],select:not([multiple])[ngModel]',
host: {'(change)': 'onChange($event.target.value)', '(blur)': 'onTouched()'},
providers: [SELECT_VALUE_ACCESSOR]
})
export class SelectControlValueAccessor implements ControlValueAccessor {
value: any;
/** @internal */
_optionMap: Map<string, any> = new Map<string, any>();
/** @internal */
_idCounter: number = 0;
onChange = (_: any) => {};
onTouched = () => {};
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
writeValue(value: any): void {
this.value = value;
var valueString = _buildValueString(this._getOptionId(value), value);
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', valueString);
}
registerOnChange(fn: (value: any) => any): void {
this.onChange = (valueString: string) => {
this.value = valueString;
fn(this._getOptionValue(valueString));
};
}
registerOnTouched(fn: () => any): void { this.onTouched = fn; }
/** @internal */
_registerOption(): string { return (this._idCounter++).toString(); }
/** @internal */
_getOptionId(value: any): string {
for (let id of MapWrapper.keys(this._optionMap)) {
if (looseIdentical(this._optionMap.get(id), value)) return id;
}
return null;
}
/** @internal */
_getOptionValue(valueString: string): any {
let value = this._optionMap.get(_extractId(valueString));
return isPresent(value) ? value : valueString;
}
}
/**
* Marks `<option>` as dynamic, so Angular can be notified when options change.
*
* ### Example
*
* ```
* <select ngControl="city">
* <option *ngFor="let c of cities" [value]="c"></option>
* </select>
* ```
*
* @experimental
*/
@Directive({selector: 'option'})
export class NgSelectOption implements OnDestroy {
id: string;
constructor(
private _element: ElementRef, private _renderer: Renderer,
@Optional() @Host() private _select: SelectControlValueAccessor) {
if (isPresent(this._select)) this.id = this._select._registerOption();
}
@Input('ngValue')
set ngValue(value: any) {
if (this._select == null) return;
this._select._optionMap.set(this.id, value);
this._setElementValue(_buildValueString(this.id, value));
this._select.writeValue(this._select.value);
}
@Input('value')
set value(value: any) {
this._setElementValue(value);
if (isPresent(this._select)) this._select.writeValue(this._select.value);
}
/** @internal */
_setElementValue(value: string): void {
this._renderer.setElementProperty(this._element.nativeElement, 'value', value);
}
ngOnDestroy() {
if (isPresent(this._select)) {
this._select._optionMap.delete(this.id);
this._select.writeValue(this._select.value);
}
}
}

View File

@ -0,0 +1,185 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, ElementRef, Host, Input, OnDestroy, OpaqueToken, Optional, Renderer, Type, forwardRef} from '@angular/core';
import {MapWrapper} from '../../facade/collection';
import {StringWrapper, isBlank, isPresent, isPrimitive, isString, looseIdentical} from '../../facade/lang';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
export const SELECT_MULTIPLE_VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SelectMultipleControlValueAccessor),
multi: true
};
function _buildValueString(id: string, value: any): string {
if (isBlank(id)) return `${value}`;
if (isString(value)) value = `'${value}'`;
if (!isPrimitive(value)) value = 'Object';
return StringWrapper.slice(`${id}: ${value}`, 0, 50);
}
function _extractId(valueString: string): string {
return valueString.split(':')[0];
}
/** Mock interface for HTML Options */
interface HTMLOption {
value: string;
selected: boolean;
}
/** Mock interface for HTMLCollection */
abstract class HTMLCollection {
length: number;
abstract item(_: number): HTMLOption;
}
/**
* The accessor for writing a value and listening to changes on a select element.
*/
@Directive({
selector: 'select[multiple][ngControl],select[multiple][ngFormControl],select[multiple][ngModel]',
host: {'(change)': 'onChange($event.target)', '(blur)': 'onTouched()'},
providers: [SELECT_MULTIPLE_VALUE_ACCESSOR]
})
export class SelectMultipleControlValueAccessor implements ControlValueAccessor {
value: any;
/** @internal */
_optionMap: Map<string, NgSelectMultipleOption> = new Map<string, NgSelectMultipleOption>();
/** @internal */
_idCounter: number = 0;
onChange = (_: any) => {};
onTouched = () => {};
constructor() {}
writeValue(value: any): void {
this.value = value;
if (value == null) return;
let values: Array<any> = <Array<any>>value;
// convert values to ids
let ids = values.map((v) => this._getOptionId(v));
this._optionMap.forEach((opt, o) => { opt._setSelected(ids.indexOf(o.toString()) > -1); });
}
registerOnChange(fn: (value: any) => any): void {
this.onChange = (_: any) => {
let selected: Array<any> = [];
if (_.hasOwnProperty('selectedOptions')) {
let options: HTMLCollection = _.selectedOptions;
for (var i = 0; i < options.length; i++) {
let opt: any = options.item(i);
let val: any = this._getOptionValue(opt.value);
selected.push(val);
}
}
// Degrade on IE
else {
let options: HTMLCollection = <HTMLCollection>_.options;
for (var i = 0; i < options.length; i++) {
let opt: HTMLOption = options.item(i);
if (opt.selected) {
let val: any = this._getOptionValue(opt.value);
selected.push(val);
}
}
}
fn(selected);
};
}
registerOnTouched(fn: () => any): void { this.onTouched = fn; }
/** @internal */
_registerOption(value: NgSelectMultipleOption): string {
let id: string = (this._idCounter++).toString();
this._optionMap.set(id, value);
return id;
}
/** @internal */
_getOptionId(value: any): string {
for (let id of MapWrapper.keys(this._optionMap)) {
if (looseIdentical(this._optionMap.get(id)._value, value)) return id;
}
return null;
}
/** @internal */
_getOptionValue(valueString: string): any {
let opt = this._optionMap.get(_extractId(valueString));
return isPresent(opt) ? opt._value : valueString;
}
}
/**
* Marks `<option>` as dynamic, so Angular can be notified when options change.
*
* ### Example
*
* ```
* <select multiple ngControl="city">
* <option *ngFor="let c of cities" [value]="c"></option>
* </select>
* ```
*/
@Directive({selector: 'option'})
export class NgSelectMultipleOption implements OnDestroy {
id: string;
/** @internal */
_value: any;
constructor(
private _element: ElementRef, private _renderer: Renderer,
@Optional() @Host() private _select: SelectMultipleControlValueAccessor) {
if (isPresent(this._select)) {
this.id = this._select._registerOption(this);
}
}
@Input('ngValue')
set ngValue(value: any) {
if (this._select == null) return;
this._value = value;
this._setElementValue(_buildValueString(this.id, value));
this._select.writeValue(this._select.value);
}
@Input('value')
set value(value: any) {
if (isPresent(this._select)) {
this._value = value;
this._setElementValue(_buildValueString(this.id, value));
this._select.writeValue(this._select.value);
} else {
this._setElementValue(value);
}
}
/** @internal */
_setElementValue(value: string): void {
this._renderer.setElementProperty(this._element.nativeElement, 'value', value);
}
/** @internal */
_setSelected(selected: boolean) {
this._renderer.setElementProperty(this._element.nativeElement, 'selected', selected);
}
ngOnDestroy() {
if (isPresent(this._select)) {
this._select._optionMap.delete(this.id);
this._select.writeValue(this._select.value);
}
}
}
export const SELECT_DIRECTIVES = [SelectMultipleControlValueAccessor, NgSelectMultipleOption];

View File

@ -0,0 +1,128 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ListWrapper, StringMapWrapper} from '../../facade/collection';
import {BaseException} from '../../facade/exceptions';
import {hasConstructor, isBlank, isPresent, looseIdentical} from '../../facade/lang';
import {Control, ControlGroup} from '../model';
import {Validators} from '../validators';
import {AbstractControlDirective} from './abstract_control_directive';
import {CheckboxControlValueAccessor} from './checkbox_value_accessor';
import {ControlContainer} from './control_container';
import {ControlValueAccessor} from './control_value_accessor';
import {DefaultValueAccessor} from './default_value_accessor';
import {NgControl} from './ng_control';
import {NgControlGroup} from './ng_control_group';
import {normalizeAsyncValidator, normalizeValidator} from './normalize_validator';
import {NumberValueAccessor} from './number_value_accessor';
import {RadioControlValueAccessor} from './radio_control_value_accessor';
import {SelectControlValueAccessor} from './select_control_value_accessor';
import {SelectMultipleControlValueAccessor} from './select_multiple_control_value_accessor';
import {AsyncValidatorFn, ValidatorFn} from './validators';
export function controlPath(name: string, parent: ControlContainer): string[] {
var p = ListWrapper.clone(parent.path);
p.push(name);
return p;
}
export function setUpControl(control: Control, dir: NgControl): void {
if (isBlank(control)) _throwError(dir, 'Cannot find control with');
if (isBlank(dir.valueAccessor)) _throwError(dir, 'No value accessor for form control with');
control.validator = Validators.compose([control.validator, dir.validator]);
control.asyncValidator = Validators.composeAsync([control.asyncValidator, dir.asyncValidator]);
dir.valueAccessor.writeValue(control.value);
// view -> model
dir.valueAccessor.registerOnChange((newValue: any) => {
dir.viewToModelUpdate(newValue);
control.updateValue(newValue, {emitModelToViewChange: false});
control.markAsDirty();
});
// model -> view
control.registerOnChange((newValue: any) => dir.valueAccessor.writeValue(newValue));
// touched
dir.valueAccessor.registerOnTouched(() => control.markAsTouched());
}
export function setUpControlGroup(control: ControlGroup, dir: NgControlGroup) {
if (isBlank(control)) _throwError(dir, 'Cannot find control with');
control.validator = Validators.compose([control.validator, dir.validator]);
control.asyncValidator = Validators.composeAsync([control.asyncValidator, dir.asyncValidator]);
}
function _throwError(dir: AbstractControlDirective, message: string): void {
let messageEnd: string;
if (dir.path.length > 1) {
messageEnd = `path: '${dir.path.join(' -> ')}'`;
} else if (dir.path[0]) {
messageEnd = `name: '${dir.path}'`;
} else {
messageEnd = 'unspecified name';
}
throw new BaseException(`${message} ${messageEnd}`);
}
export function composeValidators(validators: /* Array<Validator|Function> */ any[]): ValidatorFn {
return isPresent(validators) ? Validators.compose(validators.map(normalizeValidator)) : null;
}
export function composeAsyncValidators(validators: /* Array<Validator|Function> */ any[]):
AsyncValidatorFn {
return isPresent(validators) ? Validators.composeAsync(validators.map(normalizeAsyncValidator)) :
null;
}
export function isPropertyUpdated(changes: {[key: string]: any}, viewModel: any): boolean {
if (!StringMapWrapper.contains(changes, 'model')) return false;
var change = changes['model'];
if (change.isFirstChange()) return true;
return !looseIdentical(viewModel, change.currentValue);
}
// TODO: vsavkin remove it once https://github.com/angular/angular/issues/3011 is implemented
export function selectValueAccessor(
dir: NgControl, valueAccessors: ControlValueAccessor[]): ControlValueAccessor {
if (isBlank(valueAccessors)) return null;
var defaultAccessor: ControlValueAccessor;
var builtinAccessor: ControlValueAccessor;
var customAccessor: ControlValueAccessor;
valueAccessors.forEach((v: ControlValueAccessor) => {
if (hasConstructor(v, DefaultValueAccessor)) {
defaultAccessor = v;
} else if (
hasConstructor(v, CheckboxControlValueAccessor) || hasConstructor(v, NumberValueAccessor) ||
hasConstructor(v, SelectControlValueAccessor) ||
hasConstructor(v, SelectMultipleControlValueAccessor) ||
hasConstructor(v, RadioControlValueAccessor)) {
if (isPresent(builtinAccessor))
_throwError(dir, 'More than one built-in value accessor matches form control with');
builtinAccessor = v;
} else {
if (isPresent(customAccessor))
_throwError(dir, 'More than one custom value accessor matches form control with');
customAccessor = v;
}
});
if (isPresent(customAccessor)) return customAccessor;
if (isPresent(builtinAccessor)) return builtinAccessor;
if (isPresent(defaultAccessor)) return defaultAccessor;
_throwError(dir, 'No valid value accessor for form control with');
return null;
}

View File

@ -0,0 +1,169 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Attribute, Directive, forwardRef} from '@angular/core';
import {NumberWrapper} from '../../facade/lang';
import {AbstractControl} from '../model';
import {NG_VALIDATORS, Validators} from '../validators';
/**
* An interface that can be implemented by classes that can act as validators.
*
* ## Usage
*
* ```typescript
* @Directive({
* selector: '[custom-validator]',
* providers: [{provide: NG_VALIDATORS, useExisting: CustomValidatorDirective, multi: true}]
* })
* class CustomValidatorDirective implements Validator {
* validate(c: Control): {[key: string]: any} {
* return {"custom": true};
* }
* }
* ```
*
* @experimental
*/
export interface Validator { validate(c: AbstractControl): {[key: string]: any}; }
export const REQUIRED = Validators.required;
export const REQUIRED_VALIDATOR: any = {
provide: NG_VALIDATORS,
useValue: REQUIRED,
multi: true
};
/**
* A Directive that adds the `required` validator to any controls marked with the
* `required` attribute, via the {@link NG_VALIDATORS} binding.
*
* ### Example
*
* ```
* <input ngControl="fullName" required>
* ```
*
* @experimental
*/
@Directive({
selector: '[required][ngControl],[required][ngFormControl],[required][ngModel]',
providers: [REQUIRED_VALIDATOR]
})
export class RequiredValidator {
}
export interface ValidatorFn { (c: AbstractControl): {[key: string]: any}; }
export interface AsyncValidatorFn {
(c: AbstractControl): any /*Promise<{[key: string]: any}>|Observable<{[key: string]: any}>*/;
}
/**
* Provivder which adds {@link MinLengthValidator} to {@link NG_VALIDATORS}.
*
* ## Example:
*
* {@example common/forms/ts/validators/validators.ts region='min'}
*/
export const MIN_LENGTH_VALIDATOR: any = {
provide: NG_VALIDATORS,
useExisting: forwardRef(() => MinLengthValidator),
multi: true
};
/**
* A directive which installs the {@link MinLengthValidator} for any `ngControl`,
* `ngFormControl`, or control with `ngModel` that also has a `minlength` attribute.
*
* @experimental
*/
@Directive({
selector: '[minlength][ngControl],[minlength][ngFormControl],[minlength][ngModel]',
providers: [MIN_LENGTH_VALIDATOR]
})
export class MinLengthValidator implements Validator {
private _validator: ValidatorFn;
constructor(@Attribute('minlength') minLength: string) {
this._validator = Validators.minLength(NumberWrapper.parseInt(minLength, 10));
}
validate(c: AbstractControl): {[key: string]: any} { return this._validator(c); }
}
/**
* Provider which adds {@link MaxLengthValidator} to {@link NG_VALIDATORS}.
*
* ## Example:
*
* {@example common/forms/ts/validators/validators.ts region='max'}
*/
export const MAX_LENGTH_VALIDATOR: any = {
provide: NG_VALIDATORS,
useExisting: forwardRef(() => MaxLengthValidator),
multi: true
};
/**
* A directive which installs the {@link MaxLengthValidator} for any `ngControl, `ngFormControl`,
* or control with `ngModel` that also has a `maxlength` attribute.
*
* @experimental
*/
@Directive({
selector: '[maxlength][ngControl],[maxlength][ngFormControl],[maxlength][ngModel]',
providers: [MAX_LENGTH_VALIDATOR]
})
export class MaxLengthValidator implements Validator {
private _validator: ValidatorFn;
constructor(@Attribute('maxlength') maxLength: string) {
this._validator = Validators.maxLength(NumberWrapper.parseInt(maxLength, 10));
}
validate(c: AbstractControl): {[key: string]: any} { return this._validator(c); }
}
export const PATTERN_VALIDATOR: any = {
provide: NG_VALIDATORS,
useExisting: forwardRef(() => PatternValidator),
multi: true
};
/**
* 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 ]*">
* ```
* @experimental
*/
@Directive({
selector: '[pattern][ngControl],[pattern][ngFormControl],[pattern][ngModel]',
providers: [PATTERN_VALIDATOR]
})
export class PatternValidator implements Validator {
private _validator: ValidatorFn;
constructor(@Attribute('pattern') pattern: string) {
this._validator = Validators.pattern(pattern);
}
validate(c: AbstractControl): {[key: string]: any} { return this._validator(c); }
}

View File

@ -0,0 +1,123 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Injectable} from '@angular/core';
import {StringMapWrapper} from '../facade/collection';
import {isArray, isPresent} from '../facade/lang';
import {AsyncValidatorFn, ValidatorFn} from './directives/validators';
import {AbstractControl, Control, ControlArray, ControlGroup} from './model';
/**
* Creates a form object from a user-specified configuration.
*
* ### Example ([live demo](http://plnkr.co/edit/ENgZo8EuIECZNensZCVr?p=preview))
*
* ```typescript
* @Component({
* selector: 'my-app',
* viewProviders: [FORM_BINDINGS]
* template: `
* <form [ngFormModel]="loginForm">
* <p>Login <input ngControl="login"></p>
* <div ngControlGroup="passwordRetry">
* <p>Password <input type="password" ngControl="password"></p>
* <p>Confirm password <input type="password" ngControl="passwordConfirmation"></p>
* </div>
* </form>
* <h3>Form value:</h3>
* <pre>{{value}}</pre>
* `,
* directives: [FORM_DIRECTIVES]
* })
* export class App {
* loginForm: ControlGroup;
*
* constructor(builder: FormBuilder) {
* this.loginForm = builder.group({
* login: ["", Validators.required],
* passwordRetry: builder.group({
* password: ["", Validators.required],
* passwordConfirmation: ["", Validators.required, asyncValidator]
* })
* });
* }
*
* get value(): string {
* return JSON.stringify(this.loginForm.value, null, 2);
* }
* }
* ```
*
* @experimental
*/
@Injectable()
export class FormBuilder {
/**
* Construct a new {@link ControlGroup} with the given map of configuration.
* Valid keys for the `extra` parameter map are `optionals` and `validator`.
*
* See the {@link ControlGroup} constructor for more details.
*/
group(controlsConfig: {[key: string]: any}, extra: {[key: string]: any} = null): ControlGroup {
var controls = this._reduceControls(controlsConfig);
var optionals = <{[key: string]: boolean}>(
isPresent(extra) ? StringMapWrapper.get(extra, 'optionals') : null);
var validator: ValidatorFn = isPresent(extra) ? StringMapWrapper.get(extra, 'validator') : null;
var asyncValidator: AsyncValidatorFn =
isPresent(extra) ? StringMapWrapper.get(extra, 'asyncValidator') : null;
return new ControlGroup(controls, optionals, validator, asyncValidator);
}
/**
* Construct a new {@link Control} with the given `value`,`validator`, and `asyncValidator`.
*/
control(value: Object, validator: ValidatorFn = null, asyncValidator: AsyncValidatorFn = null):
Control {
return new Control(value, validator, asyncValidator);
}
/**
* Construct an array of {@link Control}s from the given `controlsConfig` array of
* configuration, with the given optional `validator` and `asyncValidator`.
*/
array(
controlsConfig: any[], validator: ValidatorFn = null,
asyncValidator: AsyncValidatorFn = null): ControlArray {
var controls = controlsConfig.map(c => this._createControl(c));
return new ControlArray(controls, validator, asyncValidator);
}
/** @internal */
_reduceControls(controlsConfig: {[k: string]: any}): {[key: string]: AbstractControl} {
var controls: {[key: string]: AbstractControl} = {};
StringMapWrapper.forEach(controlsConfig, (controlConfig: any, controlName: string) => {
controls[controlName] = this._createControl(controlConfig);
});
return controls;
}
/** @internal */
_createControl(controlConfig: any): AbstractControl {
if (controlConfig instanceof Control || controlConfig instanceof ControlGroup ||
controlConfig instanceof ControlArray) {
return controlConfig;
} else if (isArray(controlConfig)) {
var value = controlConfig[0];
var validator: ValidatorFn = controlConfig.length > 1 ? controlConfig[1] : null;
var asyncValidator: AsyncValidatorFn = controlConfig.length > 2 ? controlConfig[2] : null;
return this.control(value, validator, asyncValidator);
} else {
return this.control(controlConfig);
}
}
}

View File

@ -0,0 +1,538 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {PromiseObservable} from 'rxjs/observable/PromiseObservable';
import {EventEmitter, Observable} from '../facade/async';
import {ListWrapper, StringMapWrapper} from '../facade/collection';
import {isBlank, isPresent, isPromise, normalizeBool} from '../facade/lang';
import {AsyncValidatorFn, ValidatorFn} from './directives/validators';
/**
* Indicates that a Control is valid, i.e. that no errors exist in the input value.
*/
export const VALID = 'VALID';
/**
* Indicates that a Control is invalid, i.e. that an error exists in the input value.
*/
export const INVALID = 'INVALID';
/**
* Indicates that a Control is pending, i.e. that async validation is occurring and
* errors are not yet available for the input value.
*/
export const PENDING = 'PENDING';
export function isControl(control: Object): boolean {
return control instanceof AbstractControl;
}
function _find(control: AbstractControl, path: Array<string|number>| string) {
if (isBlank(path)) return null;
if (!(path instanceof Array)) {
path = (<string>path).split('/');
}
if (path instanceof Array && ListWrapper.isEmpty(path)) return null;
return (<Array<string|number>>path).reduce((v, name) => {
if (v instanceof ControlGroup) {
return isPresent(v.controls[name]) ? v.controls[name] : null;
} else if (v instanceof ControlArray) {
var index = <number>name;
return isPresent(v.at(index)) ? v.at(index) : null;
} else {
return null;
}
}, control);
}
function toObservable(r: any): Observable<any> {
return isPromise(r) ? PromiseObservable.create(r) : r;
}
/**
* @experimental
*/
export abstract class AbstractControl {
/** @internal */
_value: any;
private _valueChanges: EventEmitter<any>;
private _statusChanges: EventEmitter<any>;
private _status: string;
private _errors: {[key: string]: any};
private _pristine: boolean = true;
private _touched: boolean = false;
private _parent: ControlGroup|ControlArray;
private _asyncValidationSubscription: any;
constructor(public validator: ValidatorFn, public asyncValidator: AsyncValidatorFn) {}
get value(): any { return this._value; }
get status(): string { return this._status; }
get valid(): boolean { return this._status === VALID; }
/**
* Returns the errors of this control.
*/
get errors(): {[key: string]: any} { return this._errors; }
get pristine(): boolean { return this._pristine; }
get dirty(): boolean { return !this.pristine; }
get touched(): boolean { return this._touched; }
get untouched(): boolean { return !this._touched; }
get valueChanges(): Observable<any> { return this._valueChanges; }
get statusChanges(): Observable<any> { return this._statusChanges; }
get pending(): boolean { return this._status == PENDING; }
markAsTouched(): void { this._touched = true; }
markAsDirty({onlySelf}: {onlySelf?: boolean} = {}): void {
onlySelf = normalizeBool(onlySelf);
this._pristine = false;
if (isPresent(this._parent) && !onlySelf) {
this._parent.markAsDirty({onlySelf: onlySelf});
}
}
markAsPending({onlySelf}: {onlySelf?: boolean} = {}): void {
onlySelf = normalizeBool(onlySelf);
this._status = PENDING;
if (isPresent(this._parent) && !onlySelf) {
this._parent.markAsPending({onlySelf: onlySelf});
}
}
setParent(parent: ControlGroup|ControlArray): void { this._parent = parent; }
updateValueAndValidity({onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}):
void {
onlySelf = normalizeBool(onlySelf);
emitEvent = isPresent(emitEvent) ? emitEvent : true;
this._updateValue();
this._errors = this._runValidator();
this._status = this._calculateStatus();
if (this._status == VALID || this._status == PENDING) {
this._runAsyncValidator(emitEvent);
}
if (emitEvent) {
this._valueChanges.emit(this._value);
this._statusChanges.emit(this._status);
}
if (isPresent(this._parent) && !onlySelf) {
this._parent.updateValueAndValidity({onlySelf: onlySelf, emitEvent: emitEvent});
}
}
private _runValidator(): {[key: string]: any} {
return isPresent(this.validator) ? this.validator(this) : null;
}
private _runAsyncValidator(emitEvent: boolean): void {
if (isPresent(this.asyncValidator)) {
this._status = PENDING;
this._cancelExistingSubscription();
var obs = toObservable(this.asyncValidator(this));
this._asyncValidationSubscription = obs.subscribe(
{next: (res: {[key: string]: any}) => this.setErrors(res, {emitEvent: emitEvent})});
}
}
private _cancelExistingSubscription(): void {
if (isPresent(this._asyncValidationSubscription)) {
this._asyncValidationSubscription.unsubscribe();
}
}
/**
* Sets errors on a control.
*
* This is used when validations are run not automatically, but manually by the user.
*
* Calling `setErrors` will also update the validity of the parent control.
*
* ## Usage
*
* ```
* var login = new Control("someLogin");
* login.setErrors({
* "notUnique": true
* });
*
* expect(login.valid).toEqual(false);
* expect(login.errors).toEqual({"notUnique": true});
*
* login.updateValue("someOtherLogin");
*
* expect(login.valid).toEqual(true);
* ```
*/
setErrors(errors: {[key: string]: any}, {emitEvent}: {emitEvent?: boolean} = {}): void {
emitEvent = isPresent(emitEvent) ? emitEvent : true;
this._errors = errors;
this._status = this._calculateStatus();
if (emitEvent) {
this._statusChanges.emit(this._status);
}
if (isPresent(this._parent)) {
this._parent._updateControlsErrors();
}
}
find(path: Array<string|number>|string): AbstractControl { return _find(this, path); }
getError(errorCode: string, path: string[] = null): any {
var control = isPresent(path) && !ListWrapper.isEmpty(path) ? this.find(path) : this;
if (isPresent(control) && isPresent(control._errors)) {
return StringMapWrapper.get(control._errors, errorCode);
} else {
return null;
}
}
hasError(errorCode: string, path: string[] = null): boolean {
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();
if (isPresent(this._parent)) {
this._parent._updateControlsErrors();
}
}
/** @internal */
_initObservables() {
this._valueChanges = new EventEmitter();
this._statusChanges = new EventEmitter();
}
private _calculateStatus(): string {
if (isPresent(this._errors)) return INVALID;
if (this._anyControlsHaveStatus(PENDING)) return PENDING;
if (this._anyControlsHaveStatus(INVALID)) return INVALID;
return VALID;
}
/** @internal */
abstract _updateValue(): void;
/** @internal */
abstract _anyControlsHaveStatus(status: string): boolean;
}
/**
* Defines a part of a form that cannot be divided into other controls. `Control`s have values and
* validation state, which is determined by an optional validation function.
*
* `Control` is one of the three fundamental building blocks used to define forms in Angular, along
* with {@link ControlGroup} and {@link ControlArray}.
*
* ## Usage
*
* By default, a `Control` is created for every `<input>` or other form component.
* With {@link NgFormControl} or {@link NgFormModel} an existing {@link Control} can be
* bound to a DOM element instead. This `Control` can be configured with a custom
* validation function.
*
* ### Example ([live demo](http://plnkr.co/edit/23DESOpbNnBpBHZt1BR4?p=preview))
*
* @experimental
*/
export class Control extends AbstractControl {
/** @internal */
_onChange: Function;
constructor(
value: any = null, validator: ValidatorFn = null, asyncValidator: AsyncValidatorFn = null) {
super(validator, asyncValidator);
this._value = value;
this.updateValueAndValidity({onlySelf: true, emitEvent: false});
this._initObservables();
}
/**
* Set the value of the control to `value`.
*
* If `onlySelf` is `true`, this change will only affect the validation of this `Control`
* and not its parent component. If `emitEvent` is `true`, this change will cause a
* `valueChanges` event on the `Control` to be emitted. Both of these options default to
* `false`.
*
* If `emitModelToViewChange` is `true`, the view will be notified about the new value
* via an `onChange` event. This is the default behavior if `emitModelToViewChange` is not
* specified.
*/
updateValue(value: any, {onlySelf, emitEvent, emitModelToViewChange}: {
onlySelf?: boolean,
emitEvent?: boolean,
emitModelToViewChange?: boolean
} = {}): void {
emitModelToViewChange = isPresent(emitModelToViewChange) ? emitModelToViewChange : true;
this._value = value;
if (isPresent(this._onChange) && emitModelToViewChange) this._onChange(this._value);
this.updateValueAndValidity({onlySelf: onlySelf, emitEvent: emitEvent});
}
/**
* @internal
*/
_updateValue() {}
/**
* @internal
*/
_anyControlsHaveStatus(status: string): boolean { return false; }
/**
* Register a listener for change events.
*/
registerOnChange(fn: Function): void { this._onChange = fn; }
}
/**
* Defines a part of a form, of fixed length, that can contain other controls.
*
* A `ControlGroup` aggregates the values of each {@link Control} in the group.
* The status of a `ControlGroup` depends on the status of its children.
* If one of the controls in a group is invalid, the entire group is invalid.
* Similarly, if a control changes its value, the entire group changes as well.
*
* `ControlGroup` is one of the three fundamental building blocks used to define forms in Angular,
* along with {@link Control} and {@link ControlArray}. {@link ControlArray} can also contain other
* controls, but is of variable length.
*
* ### Example ([live demo](http://plnkr.co/edit/23DESOpbNnBpBHZt1BR4?p=preview))
*
* @experimental
*/
export class ControlGroup extends AbstractControl {
private _optionals: {[key: string]: boolean};
constructor(
public controls: {[key: string]: AbstractControl}, optionals: {[key: string]: boolean} = null,
validator: ValidatorFn = null, asyncValidator: AsyncValidatorFn = null) {
super(validator, asyncValidator);
this._optionals = isPresent(optionals) ? optionals : {};
this._initObservables();
this._setParentForControls();
this.updateValueAndValidity({onlySelf: true, emitEvent: false});
}
/**
* Register a control with the group's list of controls.
*/
registerControl(name: string, control: AbstractControl): void {
this.controls[name] = control;
control.setParent(this);
}
/**
* Add a control to this group.
*/
addControl(name: string, control: AbstractControl): void {
this.registerControl(name, control);
this.updateValueAndValidity();
}
/**
* Remove a control from this group.
*/
removeControl(name: string): void {
StringMapWrapper.delete(this.controls, name);
this.updateValueAndValidity();
}
/**
* Mark the named control as non-optional.
*/
include(controlName: string): void {
StringMapWrapper.set(this._optionals, controlName, true);
this.updateValueAndValidity();
}
/**
* Mark the named control as optional.
*/
exclude(controlName: string): void {
StringMapWrapper.set(this._optionals, controlName, false);
this.updateValueAndValidity();
}
/**
* Check whether there is a control with the given name in the group.
*/
contains(controlName: string): boolean {
var c = StringMapWrapper.contains(this.controls, controlName);
return c && this._included(controlName);
}
/** @internal */
_setParentForControls() {
StringMapWrapper.forEach(
this.controls, (control: AbstractControl, name: string) => { control.setParent(this); });
}
/** @internal */
_updateValue() { this._value = this._reduceValue(); }
/** @internal */
_anyControlsHaveStatus(status: string): boolean {
var res = false;
StringMapWrapper.forEach(this.controls, (control: AbstractControl, name: string) => {
res = res || (this.contains(name) && control.status == status);
});
return res;
}
/** @internal */
_reduceValue() {
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: AbstractControl, name: string) => {
if (this._included(name)) {
res = fn(res, control, name);
}
});
return res;
}
/** @internal */
_included(controlName: string): boolean {
var isOptional = StringMapWrapper.contains(this._optionals, controlName);
return !isOptional || StringMapWrapper.get(this._optionals, controlName);
}
}
/**
* Defines a part of a form, of variable length, that can contain other controls.
*
* A `ControlArray` aggregates the values of each {@link Control} in the group.
* The status of a `ControlArray` depends on the status of its children.
* If one of the controls in a group is invalid, the entire array is invalid.
* Similarly, if a control changes its value, the entire array changes as well.
*
* `ControlArray` is one of the three fundamental building blocks used to define forms in Angular,
* along with {@link Control} and {@link ControlGroup}. {@link ControlGroup} can also contain
* other controls, but is of fixed length.
*
* ## Adding or removing controls
*
* To change the controls in the array, use the `push`, `insert`, or `removeAt` methods
* in `ControlArray` itself. These methods ensure the controls are properly tracked in the
* form's hierarchy. Do not modify the array of `AbstractControl`s used to instantiate
* the `ControlArray` directly, as that will result in strange and unexpected behavior such
* as broken change detection.
*
* ### Example ([live demo](http://plnkr.co/edit/23DESOpbNnBpBHZt1BR4?p=preview))
*
* @experimental
*/
export class ControlArray extends AbstractControl {
constructor(
public controls: AbstractControl[], validator: ValidatorFn = null,
asyncValidator: AsyncValidatorFn = null) {
super(validator, asyncValidator);
this._initObservables();
this._setParentForControls();
this.updateValueAndValidity({onlySelf: true, emitEvent: false});
}
/**
* Get the {@link AbstractControl} at the given `index` in the array.
*/
at(index: number): AbstractControl { return this.controls[index]; }
/**
* Insert a new {@link AbstractControl} at the end of the array.
*/
push(control: AbstractControl): void {
this.controls.push(control);
control.setParent(this);
this.updateValueAndValidity();
}
/**
* Insert a new {@link AbstractControl} at the given `index` in the array.
*/
insert(index: number, control: AbstractControl): void {
ListWrapper.insert(this.controls, index, control);
control.setParent(this);
this.updateValueAndValidity();
}
/**
* Remove the control at the given `index` in the array.
*/
removeAt(index: number): void {
ListWrapper.removeAt(this.controls, index);
this.updateValueAndValidity();
}
/**
* Length of the control array.
*/
get length(): number { return this.controls.length; }
/** @internal */
_updateValue(): void { this._value = this.controls.map((control) => control.value); }
/** @internal */
_anyControlsHaveStatus(status: string): boolean {
return this.controls.some(c => c.status == status);
}
/** @internal */
_setParentForControls(): void {
this.controls.forEach((control) => { control.setParent(this); });
}
}

View File

@ -0,0 +1,155 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {OpaqueToken} from '@angular/core';
import {toPromise} from 'rxjs/operator/toPromise';
import {StringMapWrapper} from '../facade/collection';
import {isBlank, isPresent, isPromise, isString} from '../facade/lang';
import {AsyncValidatorFn, ValidatorFn} from './directives/validators';
import {AbstractControl} from './model';
/**
* Providers for validators to be used for {@link Control}s in a form.
*
* Provide this using `multi: true` to add validators.
*
* ### Example
*
* {@example core/forms/ts/ng_validators/ng_validators.ts region='ng_validators'}
* @experimental
*/
export const NG_VALIDATORS: OpaqueToken = new OpaqueToken('NgValidators');
/**
* Providers for asynchronous validators to be used for {@link Control}s
* in a form.
*
* Provide this using `multi: true` to add validators.
*
* See {@link NG_VALIDATORS} for more details.
*
* @experimental
*/
export const NG_ASYNC_VALIDATORS: OpaqueToken = new OpaqueToken('NgAsyncValidators');
/**
* Provides a set of validators used by form controls.
*
* A validator is a function that processes a {@link Control} or collection of
* controls and returns a map of errors. A null map means that validation has passed.
*
* ### Example
*
* ```typescript
* var loginControl = new Control("", Validators.required)
* ```
*
* @experimental
*/
export class Validators {
/**
* Validator that requires controls to have a non-empty value.
*/
static required(control: AbstractControl): {[key: string]: boolean} {
return isBlank(control.value) || (isString(control.value) && control.value == '') ?
{'required': true} :
null;
}
/**
* Validator that requires controls to have a value of a minimum length.
*/
static minLength(minLength: number): ValidatorFn {
return (control: AbstractControl): {[key: string]: any} => {
if (isPresent(Validators.required(control))) return null;
var v: string = control.value;
return v.length < minLength ?
{'minlength': {'requiredLength': minLength, 'actualLength': v.length}} :
null;
};
}
/**
* Validator that requires controls to have a value of a maximum length.
*/
static maxLength(maxLength: number): ValidatorFn {
return (control: AbstractControl): {[key: string]: any} => {
if (isPresent(Validators.required(control))) return null;
var v: string = control.value;
return v.length > maxLength ?
{'maxlength': {'requiredLength': maxLength, 'actualLength': v.length}} :
null;
};
}
/**
* Validator that requires a control to match a regex to its value.
*/
static pattern(pattern: string): ValidatorFn {
return (control: AbstractControl): {[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.
*/
static nullValidator(c: AbstractControl): {[key: string]: boolean} { return null; }
/**
* Compose multiple validators into a single function that returns the union
* of the individual error maps.
*/
static compose(validators: ValidatorFn[]): ValidatorFn {
if (isBlank(validators)) return null;
var presentValidators = validators.filter(isPresent);
if (presentValidators.length == 0) return null;
return function(control: AbstractControl) {
return _mergeErrors(_executeValidators(control, presentValidators));
};
}
static composeAsync(validators: AsyncValidatorFn[]): AsyncValidatorFn {
if (isBlank(validators)) return null;
var presentValidators = validators.filter(isPresent);
if (presentValidators.length == 0) return null;
return function(control: AbstractControl) {
let promises = _executeAsyncValidators(control, presentValidators).map(_convertToPromise);
return Promise.all(promises).then(_mergeErrors);
};
}
}
function _convertToPromise(obj: any): Promise<any> {
return isPromise(obj) ? obj : toPromise.call(obj);
}
function _executeValidators(control: AbstractControl, validators: ValidatorFn[]): any[] {
return validators.map(v => v(control));
}
function _executeAsyncValidators(control: AbstractControl, validators: AsyncValidatorFn[]): any[] {
return validators.map(v => v(control));
}
function _mergeErrors(arrayOfErrors: any[]): {[key: string]: any} {
var res: {[key: string]: any} =
arrayOfErrors.reduce((res: {[key: string]: any}, errors: {[key: string]: any}) => {
return isPresent(errors) ? StringMapWrapper.merge(res, errors) : res;
}, {});
return StringMapWrapper.isEmpty(res) ? null : res;
}

View File

@ -0,0 +1,26 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* @experimental
*/
export abstract class NgLocalization { abstract getPluralCategory(value: any): string; }
/**
* Returns the plural category for a given value.
* - "=value" when the case exists,
* - the plural category otherwise
*
* @internal
*/
export function getPluralCategory(
value: number, cases: string[], ngLocalization: NgLocalization): string {
const nbCase = `=${value}`;
return cases.indexOf(nbCase) > -1 ? nbCase : ngLocalization.getPluralCategory(value);
}

View File

@ -0,0 +1,13 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export * from './location/platform_location';
export * from './location/location_strategy';
export * from './location/hash_location_strategy';
export * from './location/path_location_strategy';
export * from './location/location';

View File

@ -0,0 +1,113 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Inject, Injectable, Optional} from '@angular/core';
import {isPresent} from '../facade/lang';
import {Location} from './location';
import {APP_BASE_HREF, LocationStrategy} from './location_strategy';
import {PlatformLocation, UrlChangeListener} from './platform_location';
/**
* `HashLocationStrategy` is a {@link LocationStrategy} used to configure the
* {@link Location} service to represent its state in the
* [hash fragment](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax)
* of the browser's URL.
*
* For instance, if you call `location.go('/foo')`, the browser's URL will become
* `example.com#/foo`.
*
* ### Example
*
* ```
* import {Component, provide} from '@angular/core';
* import {
* Location,
* LocationStrategy,
* HashLocationStrategy
* } from '@angular/common';
* import {
* ROUTER_DIRECTIVES,
* ROUTER_PROVIDERS,
* RouteConfig
* } from '@angular/router';
*
* @Component({directives: [ROUTER_DIRECTIVES]})
* @RouteConfig([
* {...},
* ])
* class AppCmp {
* constructor(location: Location) {
* location.go('/foo');
* }
* }
*
* bootstrap(AppCmp, [
* ROUTER_PROVIDERS,
* {provide: LocationStrategy, useClass: HashLocationStrategy}
* ]);
* ```
*
* @stable
*/
@Injectable()
export class HashLocationStrategy extends LocationStrategy {
private _baseHref: string = '';
constructor(
private _platformLocation: PlatformLocation,
@Optional() @Inject(APP_BASE_HREF) _baseHref?: string) {
super();
if (isPresent(_baseHref)) {
this._baseHref = _baseHref;
}
}
onPopState(fn: UrlChangeListener): void {
this._platformLocation.onPopState(fn);
this._platformLocation.onHashChange(fn);
}
getBaseHref(): string { return this._baseHref; }
path(includeHash: boolean = false): string {
// the hash value is always prefixed with a `#`
// and if it is empty then it will stay empty
var path = this._platformLocation.hash;
if (!isPresent(path)) path = '#';
return path.length > 0 ? path.substring(1) : path;
}
prepareExternalUrl(internal: string): string {
var url = Location.joinWithSlash(this._baseHref, internal);
return url.length > 0 ? ('#' + url) : url;
}
pushState(state: any, title: string, path: string, queryParams: string) {
var url = this.prepareExternalUrl(path + Location.normalizeQueryParams(queryParams));
if (url.length == 0) {
url = this._platformLocation.pathname;
}
this._platformLocation.pushState(state, title, url);
}
replaceState(state: any, title: string, path: string, queryParams: string) {
var url = this.prepareExternalUrl(path + Location.normalizeQueryParams(queryParams));
if (url.length == 0) {
url = this._platformLocation.pathname;
}
this._platformLocation.replaceState(state, title, url);
}
forward(): void { this._platformLocation.forward(); }
back(): void { this._platformLocation.back(); }
}

View File

@ -0,0 +1,205 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {EventEmitter, Injectable} from '@angular/core';
import {LocationStrategy} from './location_strategy';
/**
* `Location` is a service that applications can use to interact with a browser's URL.
* Depending on which {@link LocationStrategy} is used, `Location` will either persist
* to the URL's path or the URL's hash segment.
*
* Note: it's better to use {@link Router#navigate} service to trigger route changes. Use
* `Location` only if you need to interact with or create normalized URLs outside of
* routing.
*
* `Location` is responsible for normalizing the URL against the application's base href.
* A normalized URL is absolute from the URL host, includes the application's base href, and has no
* trailing slash:
* - `/my/app/user/123` is normalized
* - `my/app/user/123` **is not** normalized
* - `/my/app/user/123/` **is not** normalized
*
* ### Example
*
* ```
* import {Component} from '@angular/core';
* import {Location} from '@angular/common';
* import {
* ROUTER_DIRECTIVES,
* ROUTER_PROVIDERS,
* RouteConfig
* } from '@angular/router';
*
* @Component({directives: [ROUTER_DIRECTIVES]})
* @RouteConfig([
* {...},
* ])
* class AppCmp {
* constructor(location: Location) {
* location.go('/foo');
* }
* }
*
* bootstrap(AppCmp, [ROUTER_PROVIDERS]);
* ```
*
* @stable
*/
@Injectable()
export class Location {
/** @internal */
_subject: EventEmitter<any> = new EventEmitter();
/** @internal */
_baseHref: string;
/** @internal */
_platformStrategy: LocationStrategy;
constructor(platformStrategy: LocationStrategy) {
this._platformStrategy = platformStrategy;
var browserBaseHref = this._platformStrategy.getBaseHref();
this._baseHref = Location.stripTrailingSlash(_stripIndexHtml(browserBaseHref));
this._platformStrategy.onPopState(
(ev) => { this._subject.emit({'url': this.path(true), 'pop': true, 'type': ev.type}); });
}
/**
* Returns the normalized URL path.
*/
// TODO: vsavkin. Remove the boolean flag and always include hash once the deprecated router is
// removed.
path(includeHash: boolean = false): string {
return this.normalize(this._platformStrategy.path(includeHash));
}
/**
* Normalizes the given path and compares to the current normalized path.
*/
isCurrentPathEqualTo(path: string, query: string = ''): boolean {
return this.path() == this.normalize(path + Location.normalizeQueryParams(query));
}
/**
* Given a string representing a URL, returns the normalized URL path without leading or
* trailing slashes
*/
normalize(url: string): string {
return Location.stripTrailingSlash(_stripBaseHref(this._baseHref, _stripIndexHtml(url)));
}
/**
* Given a string representing a URL, returns the platform-specific external URL path.
* If the given URL doesn't begin with a leading slash (`'/'`), this method adds one
* before normalizing. This method will also add a hash if `HashLocationStrategy` is
* used, or the `APP_BASE_HREF` if the `PathLocationStrategy` is in use.
*/
prepareExternalUrl(url: string): string {
if (url.length > 0 && !url.startsWith('/')) {
url = '/' + url;
}
return this._platformStrategy.prepareExternalUrl(url);
}
// TODO: rename this method to pushState
/**
* Changes the browsers URL to the normalized version of the given URL, and pushes a
* new item onto the platform's history.
*/
go(path: string, query: string = ''): void {
this._platformStrategy.pushState(null, '', path, query);
}
/**
* Changes the browsers URL to the normalized version of the given URL, and replaces
* the top item on the platform's history stack.
*/
replaceState(path: string, query: string = ''): void {
this._platformStrategy.replaceState(null, '', path, query);
}
/**
* Navigates forward in the platform's history.
*/
forward(): void { this._platformStrategy.forward(); }
/**
* Navigates back in the platform's history.
*/
back(): void { this._platformStrategy.back(); }
/**
* Subscribe to the platform's `popState` events.
*/
subscribe(
onNext: (value: any) => void, onThrow: (exception: any) => void = null,
onReturn: () => void = null): Object {
return this._subject.subscribe({next: onNext, error: onThrow, complete: onReturn});
}
/**
* Given a string of url parameters, prepend with '?' if needed, otherwise return parameters as
* is.
*/
public static normalizeQueryParams(params: string): string {
return (params.length > 0 && params.substring(0, 1) != '?') ? ('?' + params) : params;
}
/**
* Given 2 parts of a url, join them with a slash if needed.
*/
public static joinWithSlash(start: string, end: string): string {
if (start.length == 0) {
return end;
}
if (end.length == 0) {
return start;
}
var slashes = 0;
if (start.endsWith('/')) {
slashes++;
}
if (end.startsWith('/')) {
slashes++;
}
if (slashes == 2) {
return start + end.substring(1);
}
if (slashes == 1) {
return start + end;
}
return start + '/' + end;
}
/**
* If url has a trailing slash, remove it, otherwise return url as is.
*/
public static stripTrailingSlash(url: string): string {
if (/\/$/g.test(url)) {
url = url.substring(0, url.length - 1);
}
return url;
}
}
function _stripBaseHref(baseHref: string, url: string): string {
if (baseHref.length > 0 && url.startsWith(baseHref)) {
return url.substring(baseHref.length);
}
return url;
}
function _stripIndexHtml(url: string): string {
if (/\/index.html$/g.test(url)) {
// '/index.html'.length == 11
return url.substring(0, url.length - 11);
}
return url;
}

View File

@ -0,0 +1,72 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {OpaqueToken} from '@angular/core';
import {UrlChangeListener} from './platform_location';
/**
* `LocationStrategy` is responsible for representing and reading route state
* from the browser's URL. Angular provides two strategies:
* {@link HashLocationStrategy} and {@link PathLocationStrategy} (default).
*
* This is used under the hood of the {@link Location} service.
*
* Applications should use the {@link Router} or {@link Location} services to
* interact with application route state.
*
* For instance, {@link HashLocationStrategy} produces URLs like
* `http://example.com#/foo`, and {@link PathLocationStrategy} produces
* `http://example.com/foo` as an equivalent URL.
*
* See these two classes for more.
*
* @stable
*/
export abstract class LocationStrategy {
abstract path(includeHash?: boolean): string;
abstract prepareExternalUrl(internal: string): string;
abstract pushState(state: any, title: string, url: string, queryParams: string): void;
abstract replaceState(state: any, title: string, url: string, queryParams: string): void;
abstract forward(): void;
abstract back(): void;
abstract onPopState(fn: UrlChangeListener): void;
abstract getBaseHref(): string;
}
/**
* The `APP_BASE_HREF` token represents the base href to be used with the
* {@link PathLocationStrategy}.
*
* If you're using {@link PathLocationStrategy}, you must provide a provider to a string
* representing the URL prefix that should be preserved when generating and recognizing
* URLs.
*
* ### Example
*
* ```
* import {Component} from '@angular/core';
* import {ROUTER_DIRECTIVES, ROUTER_PROVIDERS, RouteConfig} from '@angular/router';
* import {APP_BASE_HREF} from '@angular/common';
*
* @Component({directives: [ROUTER_DIRECTIVES]})
* @RouteConfig([
* {...},
* ])
* class AppCmp {
* // ...
* }
*
* bootstrap(AppCmp, [
* ROUTER_PROVIDERS,
* {provide: APP_BASE_HREF, useValue: '/my/app'}
* ]);
* ```
* @stable
*/
export const APP_BASE_HREF: OpaqueToken = new OpaqueToken('appBaseHref');

View File

@ -0,0 +1,94 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Inject, Injectable, Optional} from '@angular/core';
import {BaseException} from '../facade/exceptions';
import {isBlank} from '../facade/lang';
import {Location} from './location';
import {APP_BASE_HREF, LocationStrategy} from './location_strategy';
import {PlatformLocation, UrlChangeListener} from './platform_location';
/**
* `PathLocationStrategy` is a {@link LocationStrategy} used to configure the
* {@link Location} service to represent its state in the
* [path](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) of the
* browser's URL.
*
* `PathLocationStrategy` is the default binding for {@link LocationStrategy}
* provided in {@link ROUTER_PROVIDERS}.
*
* If you're using `PathLocationStrategy`, you must provide a {@link APP_BASE_HREF}
* or add a base element to the document. This URL prefix that will be preserved
* when generating and recognizing URLs.
*
* For instance, if you provide an `APP_BASE_HREF` of `'/my/app'` and call
* `location.go('/foo')`, the browser's URL will become
* `example.com/my/app/foo`.
*
* Similarly, if you add `<base href='/my/app'/>` to the document and call
* `location.go('/foo')`, the browser's URL will become
* `example.com/my/app/foo`.
*
* @stable
*/
@Injectable()
export class PathLocationStrategy extends LocationStrategy {
private _baseHref: string;
constructor(
private _platformLocation: PlatformLocation,
@Optional() @Inject(APP_BASE_HREF) href?: string) {
super();
if (isBlank(href)) {
href = this._platformLocation.getBaseHrefFromDOM();
}
if (isBlank(href)) {
throw new BaseException(
`No base href set. Please provide a value for the APP_BASE_HREF token or add a base element to the document.`);
}
this._baseHref = href;
}
onPopState(fn: UrlChangeListener): void {
this._platformLocation.onPopState(fn);
this._platformLocation.onHashChange(fn);
}
getBaseHref(): string { return this._baseHref; }
prepareExternalUrl(internal: string): string {
return Location.joinWithSlash(this._baseHref, internal);
}
path(includeHash: boolean = false): string {
const pathname = this._platformLocation.pathname +
Location.normalizeQueryParams(this._platformLocation.search);
const hash = this._platformLocation.hash;
return hash && includeHash ? `${pathname}${hash}` : pathname;
}
pushState(state: any, title: string, url: string, queryParams: string) {
var externalUrl = this.prepareExternalUrl(url + Location.normalizeQueryParams(queryParams));
this._platformLocation.pushState(state, title, externalUrl);
}
replaceState(state: any, title: string, url: string, queryParams: string) {
var externalUrl = this.prepareExternalUrl(url + Location.normalizeQueryParams(queryParams));
this._platformLocation.replaceState(state, title, externalUrl);
}
forward(): void { this._platformLocation.forward(); }
back(): void { this._platformLocation.back(); }
}

View File

@ -0,0 +1,63 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* This class should not be used directly by an application developer. Instead, use
* {@link Location}.
*
* `PlatformLocation` encapsulates all calls to DOM apis, which allows the Router to be platform
* agnostic.
* This means that we can have different implementation of `PlatformLocation` for the different
* platforms
* that angular supports. For example, the default `PlatformLocation` is {@link
* BrowserPlatformLocation},
* however when you run your app in a WebWorker you use {@link WebWorkerPlatformLocation}.
*
* The `PlatformLocation` class is used directly by all implementations of {@link LocationStrategy}
* when
* they need to interact with the DOM apis like pushState, popState, etc...
*
* {@link LocationStrategy} in turn is used by the {@link Location} service which is used directly
* by
* the {@link Router} in order to navigate between routes. Since all interactions between {@link
* Router} /
* {@link Location} / {@link LocationStrategy} and DOM apis flow through the `PlatformLocation`
* class
* they are all platform independent.
*
* @stable
*/
export abstract class PlatformLocation {
abstract getBaseHrefFromDOM(): string;
abstract onPopState(fn: UrlChangeListener): void;
abstract onHashChange(fn: UrlChangeListener): void;
get pathname(): string { return null; }
get search(): string { return null; }
get hash(): string { return null; }
abstract replaceState(state: any, title: string, url: string): void;
abstract pushState(state: any, title: string, url: string): void;
abstract forward(): void;
abstract back(): void;
}
/**
* A serializable version of the event from onPopState or onHashChange
*
* @experimental
*/
export interface UrlChangeEvent { type: string; }
/**
* @experimental
*/
export interface UrlChangeListener { (e: UrlChangeEvent): any; }

View File

@ -0,0 +1,25 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* @module
* @description
* This module provides a set of common Pipes.
*/
export {AsyncPipe} from './pipes/async_pipe';
export {COMMON_PIPES} from './pipes/common_pipes';
export {DatePipe} from './pipes/date_pipe';
export {I18nPluralPipe} from './pipes/i18n_plural_pipe';
export {I18nSelectPipe} from './pipes/i18n_select_pipe';
export {JsonPipe} from './pipes/json_pipe';
export {LowerCasePipe} from './pipes/lowercase_pipe';
export {CurrencyPipe, DecimalPipe, PercentPipe} from './pipes/number_pipe';
export {ReplacePipe} from './pipes/replace_pipe';
export {SlicePipe} from './pipes/slice_pipe';
export {UpperCasePipe} from './pipes/uppercase_pipe';

View File

@ -0,0 +1,151 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ChangeDetectorRef, OnDestroy, Pipe, WrappedValue} from '@angular/core';
import {EventEmitter, Observable} from '../facade/async';
import {isBlank, isPresent, isPromise} from '../facade/lang';
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
interface SubscriptionStrategy {
createSubscription(async: any, updateLatestValue: any): any;
dispose(subscription: any): void;
onDestroy(subscription: any): void;
}
class ObservableStrategy implements SubscriptionStrategy {
createSubscription(async: any, updateLatestValue: any): any {
return async.subscribe({next: updateLatestValue, error: (e: any) => { throw e; }});
}
dispose(subscription: any): void { subscription.unsubscribe(); }
onDestroy(subscription: any): void { subscription.unsubscribe(); }
}
class PromiseStrategy implements SubscriptionStrategy {
createSubscription(async: Promise<any>, updateLatestValue: (v: any) => any): any {
return async.then(updateLatestValue, e => { throw e; });
}
dispose(subscription: any): void {}
onDestroy(subscription: any): void {}
}
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
* emitted.
* When a new value is emitted, the `async` pipe marks the component to be checked for changes.
* When the component gets destroyed, the `async` pipe unsubscribes automatically to avoid
* potential memory leaks.
*
* ## Usage
*
* object | async
*
* where `object` is of type `Observable` or of type `Promise`.
*
* ## Examples
*
* This example binds a `Promise` to the view. Clicking the `Resolve` button resolves the
* promise.
*
* {@example core/pipes/ts/async_pipe/async_pipe_example.ts region='AsyncPipePromise'}
*
* It's also possible to use `async` with Observables. The example below binds the `time` Observable
* to the view. Every 500ms, the `time` Observable updates the view with the current time.
*
* {@example core/pipes/ts/async_pipe/async_pipe_example.ts region='AsyncPipeObservable'}
*
* @stable
*/
@Pipe({name: 'async', pure: false})
export class AsyncPipe implements OnDestroy {
/** @internal */
_latestValue: Object = null;
/** @internal */
_latestReturnedValue: Object = null;
/** @internal */
_subscription: Object = null;
/** @internal */
_obj: Observable<any>|Promise<any>|EventEmitter<any> = null;
/** @internal */
_ref: ChangeDetectorRef;
private _strategy: SubscriptionStrategy = null;
constructor(_ref: ChangeDetectorRef) { this._ref = _ref; }
ngOnDestroy(): void {
if (isPresent(this._subscription)) {
this._dispose();
}
}
transform(obj: Observable<any>|Promise<any>|EventEmitter<any>): any {
if (isBlank(this._obj)) {
if (isPresent(obj)) {
this._subscribe(obj);
}
this._latestReturnedValue = this._latestValue;
return this._latestValue;
}
if (obj !== this._obj) {
this._dispose();
return this.transform(obj);
}
if (this._latestValue === this._latestReturnedValue) {
return this._latestReturnedValue;
} else {
this._latestReturnedValue = this._latestValue;
return WrappedValue.wrap(this._latestValue);
}
}
/** @internal */
_subscribe(obj: Observable<any>|Promise<any>|EventEmitter<any>): void {
this._obj = obj;
this._strategy = this._selectStrategy(obj);
this._subscription = this._strategy.createSubscription(
obj, (value: Object) => this._updateLatestValue(obj, value));
}
/** @internal */
_selectStrategy(obj: Observable<any>|Promise<any>|EventEmitter<any>): any {
if (isPromise(obj)) {
return _promiseStrategy;
} else if ((<any>obj).subscribe) {
return _observableStrategy;
} else {
throw new InvalidPipeArgumentException(AsyncPipe, obj);
}
}
/** @internal */
_dispose(): void {
this._strategy.dispose(this._subscription);
this._latestValue = null;
this._latestReturnedValue = null;
this._subscription = null;
this._obj = null;
}
/** @internal */
_updateLatestValue(async: any, value: Object) {
if (async === this._obj) {
this._latestValue = value;
this._ref.markForCheck();
}
}
}

View File

@ -0,0 +1,48 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* @module
* @description
* This module provides a set of common Pipes.
*/
import {AsyncPipe} from './async_pipe';
import {DatePipe} from './date_pipe';
import {I18nPluralPipe} from './i18n_plural_pipe';
import {I18nSelectPipe} from './i18n_select_pipe';
import {JsonPipe} from './json_pipe';
import {LowerCasePipe} from './lowercase_pipe';
import {CurrencyPipe, DecimalPipe, PercentPipe} from './number_pipe';
import {ReplacePipe} from './replace_pipe';
import {SlicePipe} from './slice_pipe';
import {UpperCasePipe} from './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` decorator.
*
* @experimental Contains i18n pipes which are experimental
*/
export const COMMON_PIPES = [
AsyncPipe,
UpperCasePipe,
LowerCasePipe,
JsonPipe,
SlicePipe,
DecimalPipe,
PercentPipe,
CurrencyPipe,
DatePipe,
ReplacePipe,
I18nPluralPipe,
I18nSelectPipe,
];

View File

@ -1,20 +1,17 @@
import {
isDate,
isNumber,
isPresent,
Date,
DateWrapper,
CONST,
isBlank,
FunctionWrapper
} from 'angular2/src/facade/lang';
import {DateFormatter} from 'angular2/src/facade/intl';
import {PipeTransform, WrappedValue, Pipe, Injectable} from 'angular2/core';
import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Pipe, PipeTransform} from '@angular/core';
import {StringMapWrapper} from '../facade/collection';
import {DateFormatter} from '../facade/intl';
import {DateWrapper, NumberWrapper, isBlank, isDate, isString} from '../facade/lang';
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
// TODO: move to a global configurable location along with other i18n components.
var defaultLocale: string = 'en-US';
@ -33,8 +30,9 @@ var defaultLocale: string = 'en-US';
*
* expression | date[:format]
*
* where `expression` is a date object or a number (milliseconds since UTC epoch) and
* `format` indicates which date/time components to include:
* where `expression` is a date object or a number (milliseconds since UTC epoch) or an ISO string
* (https://www.w3.org/TR/NOTE-datetime) and `format` indicates which date/time components to
* include:
*
* | Component | Symbol | Short Form | Long Form | Numeric | 2-digit |
* |-----------|:------:|--------------|-------------------|-----------|-----------|
@ -53,9 +51,6 @@ var defaultLocale: string = 'en-US';
*
* In javascript, only the components specified will be respected (not the ordering,
* punctuations, ...) and details of the formatting will be dependent on the locale.
* On the other hand in Dart version, you can also include quoted text as well as some extra
* date/time components such as quarter. For more information see:
* https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/intl/intl.DateFormat.
*
* `format` can also be one of the following predefined formats:
*
@ -83,10 +78,10 @@ var defaultLocale: string = 'en-US';
* ```
*
* {@example core/pipes/ts/date_pipe/date_pipe_example.ts region='DatePipe'}
*
* @experimental
*/
@CONST()
@Pipe({name: 'date', pure: true})
@Injectable()
export class DatePipe implements PipeTransform {
/** @internal */
static _ALIASES: {[key: string]: String} = {
@ -101,16 +96,17 @@ export class DatePipe implements PipeTransform {
};
transform(value: any, args: any[]): string {
transform(value: any, pattern: string = 'mediumDate'): string {
if (isBlank(value)) return null;
if (!this.supports(value)) {
throw new InvalidPipeArgumentException(DatePipe, value);
}
var pattern: string = isPresent(args) && args.length > 0 ? args[0] : 'mediumDate';
if (isNumber(value)) {
value = DateWrapper.fromMillis(value);
if (NumberWrapper.isNumeric(value)) {
value = DateWrapper.fromMillis(NumberWrapper.parseInt(value, 10));
} else if (isString(value)) {
value = DateWrapper.fromISOString(value);
}
if (StringMapWrapper.contains(DatePipe._ALIASES, pattern)) {
pattern = <string>StringMapWrapper.get(DatePipe._ALIASES, pattern);
@ -118,5 +114,13 @@ export class DatePipe implements PipeTransform {
return DateFormatter.format(value, defaultLocale, pattern);
}
supports(obj: any): boolean { return isDate(obj) || isNumber(obj); }
private supports(obj: any): boolean {
if (isDate(obj) || NumberWrapper.isNumeric(obj)) {
return true;
}
if (isString(obj) && isDate(DateWrapper.fromISOString(obj))) {
return true;
}
return false;
}
}

View File

@ -0,0 +1,75 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Pipe, PipeTransform} from '@angular/core';
import {StringWrapper, isBlank, isStringMap} from '../facade/lang';
import {NgLocalization, getPluralCategory} from '../localization';
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
const _INTERPOLATION_REGEXP: RegExp = /#/g;
/**
* 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 mimics the ICU format,
* see http://userguide.icu-project.org/formatparse/messages
*
* ## Example
*
* ```
* class MyLocalization extends NgLocalization {
* getPluralCategory(value: any) {
* if(value > 1) {
* return 'other';
* }
* }
* }
*
* @Component({
* selector: 'app',
* template: `
* <div>
* {{ messages.length | i18nPlural: messageMapping }}
* </div>
* `,
* providers: [{provide: NgLocalization, useClass: MyLocalization}]
* })
*
* class MyApp {
* messages: any[];
* messageMapping: {[k:string]: string} = {
* '=0': 'No messages.',
* '=1': 'One message.',
* 'other': '# messages.'
* }
* ...
* }
* ```
*
* @experimental
*/
@Pipe({name: 'i18nPlural', pure: true})
export class I18nPluralPipe implements PipeTransform {
constructor(private _localization: NgLocalization) {}
transform(value: number, pluralMap: {[count: string]: string}): string {
if (isBlank(value)) return '';
if (!isStringMap(pluralMap)) {
throw new InvalidPipeArgumentException(I18nPluralPipe, pluralMap);
}
const key = getPluralCategory(value, Object.keys(pluralMap), this._localization);
return StringWrapper.replaceAll(pluralMap[key], _INTERPOLATION_REGEXP, value.toString());
}
}

View File

@ -0,0 +1,55 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Pipe, PipeTransform} from '@angular/core';
import {isBlank, isStringMap} from '../facade/lang';
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 him.',
* 'female': 'Invite her.',
* 'other': 'Invite them.'
* }
* ...
* }
* ```
*
* @experimental
*/
@Pipe({name: 'i18nSelect', pure: true})
export class I18nSelectPipe implements PipeTransform {
transform(value: string, mapping: {[key: string]: string}): string {
if (isBlank(value)) return '';
if (!isStringMap(mapping)) {
throw new InvalidPipeArgumentException(I18nSelectPipe, mapping);
}
return mapping.hasOwnProperty(value) ? mapping[value] : '';
}
}

View File

@ -0,0 +1,16 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {BaseException} from '../facade/exceptions';
import {Type, stringify} from '../facade/lang';
export class InvalidPipeArgumentException extends BaseException {
constructor(type: Type, value: Object) {
super(`Invalid argument '${value}' for pipe '${stringify(type)}'`);
}
}

View File

@ -0,0 +1,26 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Pipe, PipeTransform} from '@angular/core';
import {Json} from '../facade/lang';
/**
* Transforms any input value using `JSON.stringify`. Useful for debugging.
*
* ### Example
* {@example core/pipes/ts/json_pipe/json_pipe_example.ts region='JsonPipe'}
*
* @stable
*/
@Pipe({name: 'json', pure: false})
export class JsonPipe implements PipeTransform {
transform(value: any): string { return Json.stringify(value); }
}

View File

@ -0,0 +1,32 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Pipe, PipeTransform} from '@angular/core';
import {isBlank, isString} from '../facade/lang';
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
/**
* Transforms text to lowercase.
*
* ### Example
*
* {@example core/pipes/ts/lowerupper_pipe/lowerupper_pipe_example.ts region='LowerUpperPipe'}
*
* @experimental
*/
@Pipe({name: 'lowercase'})
export class LowerCasePipe implements PipeTransform {
transform(value: string): string {
if (isBlank(value)) return value;
if (!isString(value)) {
throw new InvalidPipeArgumentException(LowerCasePipe, value);
}
return value.toLowerCase();
}
}

View File

@ -0,0 +1,156 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Pipe, PipeTransform} from '@angular/core';
import {NumberFormatStyle, NumberFormatter} from '../facade/intl';
import {NumberWrapper, Type, isBlank, isNumber, isPresent, isString} from '../facade/lang';
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
var defaultLocale: string = 'en-US';
const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(\-(\d+))?)?$/;
function formatNumber(
pipe: Type, value: number | string, style: NumberFormatStyle, digits: string,
currency: string = null, currencyAsSymbol: boolean = false): string {
if (isBlank(value)) return null;
// Convert strings to numbers
value = isString(value) && NumberWrapper.isNumeric(value) ? +value : value;
if (!isNumber(value)) {
throw new InvalidPipeArgumentException(pipe, value);
}
let minInt: number;
let minFraction: number;
let maxFraction: number;
if (style !== NumberFormatStyle.Currency) {
// rely on Intl default for currency
minInt = 1;
minFraction = 0;
maxFraction = 3;
}
if (isPresent(digits)) {
var parts = digits.match(_NUMBER_FORMAT_REGEXP);
if (parts === null) {
throw new Error(`${digits} is not a valid digit info for number pipes`);
}
if (isPresent(parts[1])) { // min integer digits
minInt = NumberWrapper.parseIntAutoRadix(parts[1]);
}
if (isPresent(parts[3])) { // min fraction digits
minFraction = NumberWrapper.parseIntAutoRadix(parts[3]);
}
if (isPresent(parts[5])) { // max fraction digits
maxFraction = NumberWrapper.parseIntAutoRadix(parts[5]);
}
}
return NumberFormatter.format(value as number, defaultLocale, style, {
minimumIntegerDigits: minInt,
minimumFractionDigits: minFraction,
maximumFractionDigits: maxFraction,
currency: currency,
currencyAsSymbol: currencyAsSymbol
});
}
/**
* WARNING: this pipe uses the Internationalization API.
* Therefore it is only reliable in Chrome and Opera browsers. For other browsers please use an
* polyfill, for example: [https://github.com/andyearnshaw/Intl.js/].
*
* Formats a number as local text. i.e. group sizing and separator and other locale-specific
* configurations are based on the active locale.
*
* ### Usage
*
* expression | number[:digitInfo]
*
* where `expression` is a number and `digitInfo` has the following format:
*
* {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}
*
* - minIntegerDigits is the minimum number of integer digits to use. Defaults to 1.
* - minFractionDigits is the minimum number of digits after fraction. Defaults to 0.
* - maxFractionDigits is the maximum number of digits after fraction. Defaults to 3.
*
* For more information on the acceptable range for each of these numbers and other
* details see your native internationalization library.
*
* ### Example
*
* {@example core/pipes/ts/number_pipe/number_pipe_example.ts region='NumberPipe'}
*
* @experimental
*/
@Pipe({name: 'number'})
export class DecimalPipe implements PipeTransform {
transform(value: any, digits: string = null): string {
return formatNumber(DecimalPipe, value, NumberFormatStyle.Decimal, digits);
}
}
/**
* WARNING: this pipe uses the Internationalization API.
* Therefore it is only reliable in Chrome and Opera browsers. For other browsers please use an
* polyfill, for example: [https://github.com/andyearnshaw/Intl.js/].
*
* Formats a number as local percent.
*
* ### Usage
*
* expression | percent[:digitInfo]
*
* For more information about `digitInfo` see {@link DecimalPipe}
*
* ### Example
*
* {@example core/pipes/ts/number_pipe/number_pipe_example.ts region='PercentPipe'}
*
* @experimental
*/
@Pipe({name: 'percent'})
export class PercentPipe implements PipeTransform {
transform(value: any, digits: string = null): string {
return formatNumber(PercentPipe, value, NumberFormatStyle.Percent, digits);
}
}
/**
* WARNING: this pipe uses the Internationalization API.
* Therefore it is only reliable in Chrome and Opera browsers. For other browsers please use an
* polyfill, for example: [https://github.com/andyearnshaw/Intl.js/].
*
*
* Formats a number as local currency.
*
* ### Usage
*
* expression | currency[:currencyCode[:symbolDisplay[:digitInfo]]]
*
* where `currencyCode` is the ISO 4217 currency code, such as "USD" for the US dollar and
* "EUR" for the euro. `symbolDisplay` is a boolean indicating whether to use the currency
* symbol (e.g. $) or the currency code (e.g. USD) in the output. The default for this value
* is `false`.
* For more information about `digitInfo` see {@link DecimalPipe}
*
* ### Example
*
* {@example core/pipes/ts/number_pipe/number_pipe_example.ts region='CurrencyPipe'}
*
* @experimental
*/
@Pipe({name: 'currency'})
export class CurrencyPipe implements PipeTransform {
transform(
value: any, currencyCode: string = 'USD', symbolDisplay: boolean = false,
digits: string = null): string {
return formatNumber(
CurrencyPipe, value, NumberFormatStyle.Currency, digits, currencyCode, symbolDisplay);
}
}

View File

@ -0,0 +1,87 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Pipe, PipeTransform} from '@angular/core';
import {StringWrapper, isBlank, isFunction, isNumber, isString} from '../facade/lang';
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.
*
* @deprecated The current pipe has limited functionality. The pipe api is not meant to be able
* express complex yet generic value transformations. We recommend that these transformations happen
* in the component logic instead.
*/
@Pipe({name: 'replace'})
export class ReplacePipe implements PipeTransform {
transform(value: any, pattern: string|RegExp, replacement: Function|string): any {
if (isBlank(value)) {
return value;
}
if (!this._supportedInput(value)) {
throw new InvalidPipeArgumentException(ReplacePipe, value);
}
var input = value.toString();
if (!this._supportedPattern(pattern)) {
throw new InvalidPipeArgumentException(ReplacePipe, pattern);
}
if (!this._supportedReplacement(replacement)) {
throw new InvalidPipeArgumentException(ReplacePipe, replacement);
}
if (isFunction(replacement)) {
const rgxPattern = isString(pattern) ? new RegExp(pattern, 'g') : pattern;
return StringWrapper.replaceAllMapped(
input, rgxPattern, <(m: string[]) => string>replacement);
}
if (pattern instanceof RegExp) {
// use the replaceAll variant
return StringWrapper.replaceAll(input, pattern, <string>replacement);
}
return StringWrapper.replace(input, <string>pattern, <string>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

@ -1,7 +1,14 @@
import {isBlank, isString, isArray, StringWrapper, CONST} from 'angular2/src/facade/lang';
import {BaseException} from 'angular2/src/facade/exceptions';
import {ListWrapper} from 'angular2/src/facade/collection';
import {Injectable, PipeTransform, WrappedValue, Pipe} from 'angular2/core';
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Pipe, PipeTransform} from '@angular/core';
import {ListWrapper} from '../facade/collection';
import {StringWrapper, isArray, isBlank, isString} from '../facade/lang';
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
/**
@ -40,6 +47,8 @@ import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
* When operating on a [List], the returned list is always a copy even when all
* the elements are being returned.
*
* When operating on a blank value, returns it.
*
* ## List Example
*
* This `ngFor` example:
@ -54,21 +63,17 @@ import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
* ## String Examples
*
* {@example core/pipes/ts/slice_pipe/slice_pipe_example.ts region='SlicePipe_string'}
*
* @stable
*/
@Pipe({name: 'slice', pure: false})
@Injectable()
export class SlicePipe implements PipeTransform {
transform(value: any, args: any[] = null): any {
if (isBlank(args) || args.length == 0) {
throw new BaseException('Slice pipe requires one argument');
}
transform(value: any, start: number, end: number = null): any {
if (isBlank(value)) return value;
if (!this.supports(value)) {
throw new InvalidPipeArgumentException(SlicePipe, value);
}
if (isBlank(value)) return value;
var start: number = args[0];
var end: number = args.length > 1 ? args[1] : null;
if (isString(value)) {
return StringWrapper.slice(value, start, end);
}

View File

@ -0,0 +1,31 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Pipe, PipeTransform} from '@angular/core';
import {isBlank, isString} from '../facade/lang';
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
/**
* Implements uppercase transforms to text.
*
* ### Example
*
* {@example core/pipes/ts/lowerupper_pipe/lowerupper_pipe_example.ts region='LowerUpperPipe'}
*
* @experimental
*/
@Pipe({name: 'uppercase'})
export class UpperCasePipe implements PipeTransform {
transform(value: string): string {
if (isBlank(value)) return value;
if (!isString(value)) {
throw new InvalidPipeArgumentException(UpperCasePipe, value);
}
return value.toUpperCase();
}
}

View File

@ -0,0 +1,597 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgClass, NgFor} from '@angular/common';
import {Component} from '@angular/core';
import {ComponentFixture, TestComponentBuilder} from '@angular/core/testing';
import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
import {ListWrapper, StringMapWrapper} from '../../src/facade/collection';
function detectChangesAndCheck(fixture: ComponentFixture<any>, classes: string) {
fixture.detectChanges();
expect(fixture.debugElement.children[0].nativeElement.className).toEqual(classes);
}
export function main() {
describe('binding to CSS class list', () => {
it('should clean up when the directive is destroyed',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div *ngFor="let item of items" [ngClass]="item"></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture: ComponentFixture<TestComponent>) => {
fixture.debugElement.componentInstance.items = [['0']];
fixture.detectChanges();
fixture.debugElement.componentInstance.items = [['1']];
detectChangesAndCheck(fixture, '1');
async.done();
});
}));
describe('expressions evaluating to objects', () => {
it('should add classes specified in an object literal',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div [ngClass]="{foo: true, bar: false}"></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
detectChangesAndCheck(fixture, 'foo');
async.done();
});
}));
it('should add classes specified in an object literal without change in class names',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = `<div [ngClass]="{'foo-bar': true, 'fooBar': true}"></div>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
detectChangesAndCheck(fixture, 'foo-bar fooBar');
async.done();
});
}));
it('should add and remove classes based on changes in object literal values',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div [ngClass]="{foo: condition, bar: !condition}"></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
detectChangesAndCheck(fixture, 'foo');
fixture.debugElement.componentInstance.condition = false;
detectChangesAndCheck(fixture, 'bar');
async.done();
});
}));
it('should add and remove classes based on changes to the expression object',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div [ngClass]="objExpr"></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
detectChangesAndCheck(fixture, 'foo');
StringMapWrapper.set(
fixture.debugElement.componentInstance.objExpr, 'bar', true);
detectChangesAndCheck(fixture, 'foo bar');
StringMapWrapper.set(
fixture.debugElement.componentInstance.objExpr, 'baz', true);
detectChangesAndCheck(fixture, 'foo bar baz');
StringMapWrapper.delete(fixture.debugElement.componentInstance.objExpr, 'bar');
detectChangesAndCheck(fixture, 'foo baz');
async.done();
});
}));
it('should add and remove classes based on reference changes to the expression object',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div [ngClass]="objExpr"></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
detectChangesAndCheck(fixture, 'foo');
fixture.debugElement.componentInstance.objExpr = {foo: true, bar: true};
detectChangesAndCheck(fixture, 'foo bar');
fixture.debugElement.componentInstance.objExpr = {baz: true};
detectChangesAndCheck(fixture, 'baz');
async.done();
});
}));
it('should remove active classes when expression evaluates to null',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div [ngClass]="objExpr"></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
detectChangesAndCheck(fixture, 'foo');
fixture.debugElement.componentInstance.objExpr = null;
detectChangesAndCheck(fixture, '');
fixture.debugElement.componentInstance.objExpr = {'foo': false, 'bar': true};
detectChangesAndCheck(fixture, 'bar');
async.done();
});
}));
it('should allow multiple classes per expression',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div [ngClass]="objExpr"></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.objExpr = {
'bar baz': true,
'bar1 baz1': true
};
detectChangesAndCheck(fixture, 'bar baz bar1 baz1');
fixture.debugElement.componentInstance.objExpr = {
'bar baz': false,
'bar1 baz1': true
};
detectChangesAndCheck(fixture, 'bar1 baz1');
async.done();
});
}));
it('should split by one or more spaces between classes',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div [ngClass]="objExpr"></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.objExpr = {'foo bar baz': true};
detectChangesAndCheck(fixture, 'foo bar baz');
async.done();
});
}));
});
describe('expressions evaluating to lists', () => {
it('should add classes specified in a list literal',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = `<div [ngClass]="['foo', 'bar', 'foo-bar', 'fooBar']"></div>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
detectChangesAndCheck(fixture, 'foo bar foo-bar fooBar');
async.done();
});
}));
it('should add and remove classes based on changes to the expression',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div [ngClass]="arrExpr"></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
var arrExpr: string[] = fixture.debugElement.componentInstance.arrExpr;
detectChangesAndCheck(fixture, 'foo');
arrExpr.push('bar');
detectChangesAndCheck(fixture, 'foo bar');
arrExpr[1] = 'baz';
detectChangesAndCheck(fixture, 'foo baz');
ListWrapper.remove(fixture.debugElement.componentInstance.arrExpr, 'baz');
detectChangesAndCheck(fixture, 'foo');
async.done();
});
}));
it('should add and remove classes when a reference changes',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div [ngClass]="arrExpr"></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
detectChangesAndCheck(fixture, 'foo');
fixture.debugElement.componentInstance.arrExpr = ['bar'];
detectChangesAndCheck(fixture, 'bar');
async.done();
});
}));
it('should take initial classes into account when a reference changes',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div class="foo" [ngClass]="arrExpr"></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
detectChangesAndCheck(fixture, 'foo');
fixture.debugElement.componentInstance.arrExpr = ['bar'];
detectChangesAndCheck(fixture, 'foo bar');
async.done();
});
}));
it('should ignore empty or blank class names',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div class="foo" [ngClass]="arrExpr"></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.arrExpr = ['', ' '];
detectChangesAndCheck(fixture, 'foo');
async.done();
});
}));
it('should trim blanks from class names',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div class="foo" [ngClass]="arrExpr"></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.arrExpr = [' bar '];
detectChangesAndCheck(fixture, 'foo bar');
async.done();
});
}));
it('should allow multiple classes per item in arrays',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div [ngClass]="arrExpr"></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.arrExpr =
['foo bar baz', 'foo1 bar1 baz1'];
detectChangesAndCheck(fixture, 'foo bar baz foo1 bar1 baz1');
fixture.debugElement.componentInstance.arrExpr = ['foo bar baz foobar'];
detectChangesAndCheck(fixture, 'foo bar baz foobar');
async.done();
});
}));
});
describe('expressions evaluating to sets', () => {
it('should add and remove classes if the set instance changed',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div [ngClass]="setExpr"></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
var setExpr = new Set<string>();
setExpr.add('bar');
fixture.debugElement.componentInstance.setExpr = setExpr;
detectChangesAndCheck(fixture, 'bar');
setExpr = new Set<string>();
setExpr.add('baz');
fixture.debugElement.componentInstance.setExpr = setExpr;
detectChangesAndCheck(fixture, 'baz');
async.done();
});
}));
});
describe('expressions evaluating to string', () => {
it('should add classes specified in a string literal',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = `<div [ngClass]="'foo bar foo-bar fooBar'"></div>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
detectChangesAndCheck(fixture, 'foo bar foo-bar fooBar');
async.done();
});
}));
it('should add and remove classes based on changes to the expression',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div [ngClass]="strExpr"></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
detectChangesAndCheck(fixture, 'foo');
fixture.debugElement.componentInstance.strExpr = 'foo bar';
detectChangesAndCheck(fixture, 'foo bar');
fixture.debugElement.componentInstance.strExpr = 'baz';
detectChangesAndCheck(fixture, 'baz');
async.done();
});
}));
it('should remove active classes when switching from string to null',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = `<div [ngClass]="strExpr"></div>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
detectChangesAndCheck(fixture, 'foo');
fixture.debugElement.componentInstance.strExpr = null;
detectChangesAndCheck(fixture, '');
async.done();
});
}));
it('should take initial classes into account when switching from string to null',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = `<div class="foo" [ngClass]="strExpr"></div>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
detectChangesAndCheck(fixture, 'foo');
fixture.debugElement.componentInstance.strExpr = null;
detectChangesAndCheck(fixture, 'foo');
async.done();
});
}));
it('should ignore empty and blank strings',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = `<div class="foo" [ngClass]="strExpr"></div>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.strExpr = '';
detectChangesAndCheck(fixture, 'foo');
async.done();
});
}));
});
describe('cooperation with other class-changing constructs', () => {
it('should co-operate with the class attribute',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div [ngClass]="objExpr" class="init foo"></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
StringMapWrapper.set(
fixture.debugElement.componentInstance.objExpr, 'bar', true);
detectChangesAndCheck(fixture, 'init foo bar');
StringMapWrapper.set(
fixture.debugElement.componentInstance.objExpr, 'foo', false);
detectChangesAndCheck(fixture, 'init bar');
fixture.debugElement.componentInstance.objExpr = null;
detectChangesAndCheck(fixture, 'init foo');
async.done();
});
}));
it('should co-operate with the interpolated class attribute',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = `<div [ngClass]="objExpr" class="{{'init foo'}}"></div>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
StringMapWrapper.set(
fixture.debugElement.componentInstance.objExpr, 'bar', true);
detectChangesAndCheck(fixture, `init foo bar`);
StringMapWrapper.set(
fixture.debugElement.componentInstance.objExpr, 'foo', false);
detectChangesAndCheck(fixture, `init bar`);
fixture.debugElement.componentInstance.objExpr = null;
detectChangesAndCheck(fixture, `init foo`);
async.done();
});
}));
it('should co-operate with the class attribute and binding to it',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = `<div [ngClass]="objExpr" class="init" [class]="'foo'"></div>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
StringMapWrapper.set(
fixture.debugElement.componentInstance.objExpr, 'bar', true);
detectChangesAndCheck(fixture, `init foo bar`);
StringMapWrapper.set(
fixture.debugElement.componentInstance.objExpr, 'foo', false);
detectChangesAndCheck(fixture, `init bar`);
fixture.debugElement.componentInstance.objExpr = null;
detectChangesAndCheck(fixture, `init foo`);
async.done();
});
}));
it('should co-operate with the class attribute and class.name binding',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template =
'<div class="init foo" [ngClass]="objExpr" [class.baz]="condition"></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
detectChangesAndCheck(fixture, 'init foo baz');
StringMapWrapper.set(
fixture.debugElement.componentInstance.objExpr, 'bar', true);
detectChangesAndCheck(fixture, 'init foo baz bar');
StringMapWrapper.set(
fixture.debugElement.componentInstance.objExpr, 'foo', false);
detectChangesAndCheck(fixture, 'init baz bar');
fixture.debugElement.componentInstance.condition = false;
detectChangesAndCheck(fixture, 'init bar');
async.done();
});
}));
it('should co-operate with initial class and class attribute binding when binding changes',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div class="init" [ngClass]="objExpr" [class]="strExpr"></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
detectChangesAndCheck(fixture, 'init foo');
StringMapWrapper.set(
fixture.debugElement.componentInstance.objExpr, 'bar', true);
detectChangesAndCheck(fixture, 'init foo bar');
fixture.debugElement.componentInstance.strExpr = 'baz';
detectChangesAndCheck(fixture, 'init bar baz foo');
fixture.debugElement.componentInstance.objExpr = null;
detectChangesAndCheck(fixture, 'init baz');
async.done();
});
}));
});
});
}
@Component({selector: 'test-cmp', directives: [NgClass, NgFor], template: ''})
class TestComponent {
condition: boolean = true;
items: any[];
arrExpr: string[] = ['foo'];
setExpr: Set<string> = new Set<string>();
objExpr = {'foo': true, 'bar': false};
strExpr = 'foo';
constructor() { this.setExpr.add('foo'); }
}

View File

@ -0,0 +1,586 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgFor, NgIf} from '@angular/common';
import {Component, ContentChild, TemplateRef} from '@angular/core';
import {TestComponentBuilder} from '@angular/core/testing';
import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {By} from '@angular/platform-browser/src/dom/debug/by';
import {expect} from '@angular/platform-browser/testing/matchers';
import {ListWrapper} from '../../src/facade/collection';
let thisArg: any;
export function main() {
describe('ngFor', () => {
const TEMPLATE =
'<div><copy-me template="ngFor let item of items">{{item.toString()}};</copy-me></div>';
it('should reflect initial elements',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
tcb.overrideTemplate(TestComponent, TEMPLATE)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('1;2;');
async.done();
});
}));
it('should reflect added elements',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
tcb.overrideTemplate(TestComponent, TEMPLATE)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
(<number[]>fixture.debugElement.componentInstance.items).push(3);
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('1;2;3;');
async.done();
});
}));
it('should reflect removed elements',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
tcb.overrideTemplate(TestComponent, TEMPLATE)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
ListWrapper.removeAt(fixture.debugElement.componentInstance.items, 1);
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('1;');
async.done();
});
}));
it('should reflect moved elements',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
tcb.overrideTemplate(TestComponent, TEMPLATE)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
ListWrapper.removeAt(fixture.debugElement.componentInstance.items, 0);
(<number[]>fixture.debugElement.componentInstance.items).push(1);
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('2;1;');
async.done();
});
}));
it('should reflect a mix of all changes (additions/removals/moves)',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
tcb.overrideTemplate(TestComponent, TEMPLATE)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.items = [0, 1, 2, 3, 4, 5];
fixture.detectChanges();
fixture.debugElement.componentInstance.items = [6, 2, 7, 0, 4, 8];
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('6;2;7;0;4;8;');
async.done();
});
}));
it('should iterate over an array of objects',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
const template =
'<ul><li template="ngFor let item of items">{{item["name"]}};</li></ul>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
// INIT
fixture.debugElement.componentInstance.items =
[{'name': 'misko'}, {'name': 'shyam'}];
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('misko;shyam;');
// GROW
(<any[]>fixture.debugElement.componentInstance.items).push({'name': 'adam'});
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('misko;shyam;adam;');
// SHRINK
ListWrapper.removeAt(fixture.debugElement.componentInstance.items, 2);
ListWrapper.removeAt(fixture.debugElement.componentInstance.items, 0);
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('shyam;');
async.done();
});
}));
it('should gracefully handle nulls',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
const template = '<ul><li template="ngFor let item of null">{{item}};</li></ul>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('');
async.done();
});
}));
it('should gracefully handle ref changing to null and back',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
tcb.overrideTemplate(TestComponent, TEMPLATE)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('1;2;');
fixture.debugElement.componentInstance.items = null;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('');
fixture.debugElement.componentInstance.items = [1, 2, 3];
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('1;2;3;');
async.done();
});
}));
it('should throw on non-iterable ref and suggest using an array',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
tcb.overrideTemplate(TestComponent, TEMPLATE).createAsync(TestComponent).then((fixture) => {
fixture.debugElement.componentInstance.items = 'whaaa';
expect(() => fixture.detectChanges())
.toThrowError(
/Cannot find a differ supporting object 'whaaa' of type 'string'. NgFor only supports binding to Iterables such as Arrays/);
async.done();
});
}));
it('should throw on ref changing to string',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
tcb.overrideTemplate(TestComponent, TEMPLATE)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('1;2;');
fixture.debugElement.componentInstance.items = 'whaaa';
expect(() => fixture.detectChanges()).toThrowError();
async.done();
});
}));
it('should works with duplicates',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
tcb.overrideTemplate(TestComponent, TEMPLATE)
.createAsync(TestComponent)
.then((fixture) => {
var a = new Foo();
fixture.debugElement.componentInstance.items = [a, a];
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('foo;foo;');
async.done();
});
}));
it('should repeat over nested arrays',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
const template = '<div>' +
'<div template="ngFor let item of items">' +
'<div template="ngFor let subitem of item">' +
'{{subitem}}-{{item.length}};' +
'</div>|' +
'</div>' +
'</div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.items = [['a', 'b'], ['c']];
fixture.detectChanges();
fixture.detectChanges();
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('a-2;b-2;|c-1;|');
fixture.debugElement.componentInstance.items = [['e'], ['f', 'g']];
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('e-1;|f-2;g-2;|');
async.done();
});
}));
it('should repeat over nested arrays with no intermediate element',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
const template = '<div><template ngFor let-item [ngForOf]="items">' +
'<div template="ngFor let subitem of item">' +
'{{subitem}}-{{item.length}};' +
'</div></template></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.items = [['a', 'b'], ['c']];
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('a-2;b-2;c-1;');
fixture.debugElement.componentInstance.items = [['e'], ['f', 'g']];
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('e-1;f-2;g-2;');
async.done();
});
}));
it('should repeat over nested ngIf that are the last node in the ngFor temlate',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
const template =
`<div><template ngFor let-item [ngForOf]="items" let-i="index"><div>{{i}}|</div>` +
`<div *ngIf="i % 2 == 0">even|</div></template></div>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
const el = fixture.debugElement.nativeElement;
const items = [1];
fixture.debugElement.componentInstance.items = items;
fixture.detectChanges();
expect(el).toHaveText('0|even|');
items.push(1);
fixture.detectChanges();
expect(el).toHaveText('0|even|1|');
items.push(1);
fixture.detectChanges();
expect(el).toHaveText('0|even|1|2|even|');
async.done();
});
}));
it('should display indices correctly',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
const template =
'<div><copy-me template="ngFor: let item of items; let i=index">{{i.toString()}}</copy-me></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('0123456789');
fixture.debugElement.componentInstance.items = [1, 2, 6, 7, 4, 3, 5, 8, 9, 0];
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('0123456789');
async.done();
});
}));
it('should display first item correctly',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
const template =
'<div><copy-me template="ngFor: let item of items; let isFirst=first">{{isFirst.toString()}}</copy-me></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.items = [0, 1, 2];
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('truefalsefalse');
fixture.debugElement.componentInstance.items = [2, 1];
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('truefalse');
async.done();
});
}));
it('should display last item correctly',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
const template =
'<div><copy-me template="ngFor: let item of items; let isLast=last">{{isLast.toString()}}</copy-me></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.items = [0, 1, 2];
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('falsefalsetrue');
fixture.debugElement.componentInstance.items = [2, 1];
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('falsetrue');
async.done();
});
}));
it('should display even items correctly',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
const template =
'<div><copy-me template="ngFor: let item of items; let isEven=even">{{isEven.toString()}}</copy-me></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.items = [0, 1, 2];
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('truefalsetrue');
fixture.debugElement.componentInstance.items = [2, 1];
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('truefalse');
async.done();
});
}));
it('should display odd items correctly',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
const template =
'<div><copy-me template="ngFor: let item of items; let isOdd=odd">{{isOdd.toString()}}</copy-me></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.items = [0, 1, 2, 3];
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('falsetruefalsetrue');
fixture.debugElement.componentInstance.items = [2, 1];
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('falsetrue');
async.done();
});
}));
it('should allow to use a custom template',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
tcb.overrideTemplate(
TestComponent,
'<ul><template ngFor [ngForOf]="items" [ngForTemplate]="contentTpl"></template></ul>')
.overrideTemplate(
ComponentUsingTestComponent,
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>')
.createAsync(ComponentUsingTestComponent)
.then((fixture) => {
const testComponent = fixture.debugElement.children[0];
testComponent.componentInstance.items = ['a', 'b', 'c'];
fixture.detectChanges();
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
async.done();
});
}));
it('should use a default template if a custom one is null',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
tcb.overrideTemplate(TestComponent, `<ul><template ngFor let-item [ngForOf]="items"
[ngForTemplate]="contentTpl" let-i="index">{{i}}: {{item}};</template></ul>`)
.overrideTemplate(ComponentUsingTestComponent, '<test-cmp></test-cmp>')
.createAsync(ComponentUsingTestComponent)
.then((fixture) => {
const testComponent = fixture.debugElement.children[0];
testComponent.componentInstance.items = ['a', 'b', 'c'];
fixture.detectChanges();
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
async.done();
});
}));
it('should use a custom template when both default and a custom one are present',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
tcb.overrideTemplate(TestComponent, `<ul><template ngFor let-item [ngForOf]="items"
[ngForTemplate]="contentTpl" let-i="index">{{i}}=> {{item}};</template></ul>`)
.overrideTemplate(
ComponentUsingTestComponent,
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>')
.createAsync(ComponentUsingTestComponent)
.then((fixture) => {
const testComponent = fixture.debugElement.children[0];
testComponent.componentInstance.items = ['a', 'b', 'c'];
fixture.detectChanges();
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
async.done();
});
}));
describe('track by', () => {
it('should set the context to the component instance',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
const template =
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByContext.bind(this)"></template>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
thisArg = null;
fixture.detectChanges();
expect(thisArg).toBe(fixture.debugElement.componentInstance);
async.done();
});
}));
it('should not replace tracked items',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
const template =
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById" let-i="index">
<p>{{items[i]}}</p>
</template>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
var buildItemList = () => {
fixture.debugElement.componentInstance.items = [{'id': 'a'}];
fixture.detectChanges();
return fixture.debugElement.queryAll(By.css('p'))[0];
};
var firstP = buildItemList();
var finalP = buildItemList();
expect(finalP.nativeElement).toBe(firstP.nativeElement);
async.done();
});
}));
it('should update implicit local variable on view',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
const template =
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.items = [{'id': 'a', 'color': 'blue'}];
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('blue');
fixture.debugElement.componentInstance.items = [{'id': 'a', 'color': 'red'}];
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('red');
async.done();
});
}));
it('should move items around and keep them updated ',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
const template =
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.items =
[{'id': 'a', 'color': 'blue'}, {'id': 'b', 'color': 'yellow'}];
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('blueyellow');
fixture.debugElement.componentInstance.items =
[{'id': 'b', 'color': 'orange'}, {'id': 'a', 'color': 'red'}];
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('orangered');
async.done();
});
}));
it('should handle added and removed items properly when tracking by index',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
const template =
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByIndex">{{item}}</template></div>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.items = ['a', 'b', 'c', 'd'];
fixture.detectChanges();
fixture.debugElement.componentInstance.items = ['e', 'f', 'g', 'h'];
fixture.detectChanges();
fixture.debugElement.componentInstance.items = ['e', 'f', 'h'];
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('efh');
async.done();
});
}));
});
});
}
class Foo {
toString() { return 'foo'; }
}
@Component({selector: 'test-cmp', directives: [NgFor, NgIf], template: ''})
class TestComponent {
@ContentChild(TemplateRef) contentTpl: TemplateRef<Object>;
items: any;
constructor() { this.items = [1, 2]; }
trackById(index: number, item: any): string { return item['id']; }
trackByIndex(index: number, item: any): number { return index; }
trackByContext(): void { thisArg = this; }
}
@Component({selector: 'outer-cmp', directives: [TestComponent], template: ''})
class ComponentUsingTestComponent {
items: any;
constructor() { this.items = [1, 2]; }
}

View File

@ -0,0 +1,258 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgIf} from '@angular/common';
import {Component} from '@angular/core';
import {TestComponentBuilder} from '@angular/core/testing';
import {AsyncTestCompleter, beforeEach, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {expect} from '@angular/platform-browser/testing/matchers';
export function main() {
describe('ngIf directive', () => {
it('should work in a template attribute',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var html = '<div><copy-me template="ngIf booleanCondition">hello</copy-me></div>';
tcb.overrideTemplate(TestComponent, html)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
expect(getDOM()
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
.length)
.toEqual(1);
expect(fixture.debugElement.nativeElement).toHaveText('hello');
async.done();
});
}));
it('should work in a template element',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var html =
'<div><template [ngIf]="booleanCondition"><copy-me>hello2</copy-me></template></div>';
tcb.overrideTemplate(TestComponent, html)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
expect(getDOM()
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
.length)
.toEqual(1);
expect(fixture.debugElement.nativeElement).toHaveText('hello2');
async.done();
});
}));
it('should toggle node when condition changes',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var html = '<div><copy-me template="ngIf booleanCondition">hello</copy-me></div>';
tcb.overrideTemplate(TestComponent, html)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.booleanCondition = false;
fixture.detectChanges();
expect(getDOM()
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
.length)
.toEqual(0);
expect(fixture.debugElement.nativeElement).toHaveText('');
fixture.debugElement.componentInstance.booleanCondition = true;
fixture.detectChanges();
expect(getDOM()
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
.length)
.toEqual(1);
expect(fixture.debugElement.nativeElement).toHaveText('hello');
fixture.debugElement.componentInstance.booleanCondition = false;
fixture.detectChanges();
expect(getDOM()
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
.length)
.toEqual(0);
expect(fixture.debugElement.nativeElement).toHaveText('');
async.done();
});
}));
it('should handle nested if correctly',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var html =
'<div><template [ngIf]="booleanCondition"><copy-me *ngIf="nestedBooleanCondition">hello</copy-me></template></div>';
tcb.overrideTemplate(TestComponent, html)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.booleanCondition = false;
fixture.detectChanges();
expect(getDOM()
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
.length)
.toEqual(0);
expect(fixture.debugElement.nativeElement).toHaveText('');
fixture.debugElement.componentInstance.booleanCondition = true;
fixture.detectChanges();
expect(getDOM()
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
.length)
.toEqual(1);
expect(fixture.debugElement.nativeElement).toHaveText('hello');
fixture.debugElement.componentInstance.nestedBooleanCondition = false;
fixture.detectChanges();
expect(getDOM()
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
.length)
.toEqual(0);
expect(fixture.debugElement.nativeElement).toHaveText('');
fixture.debugElement.componentInstance.nestedBooleanCondition = true;
fixture.detectChanges();
expect(getDOM()
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
.length)
.toEqual(1);
expect(fixture.debugElement.nativeElement).toHaveText('hello');
fixture.debugElement.componentInstance.booleanCondition = false;
fixture.detectChanges();
expect(getDOM()
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
.length)
.toEqual(0);
expect(fixture.debugElement.nativeElement).toHaveText('');
async.done();
});
}));
it('should update several nodes with if',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var html = '<div>' +
'<copy-me template="ngIf numberCondition + 1 >= 2">helloNumber</copy-me>' +
'<copy-me template="ngIf stringCondition == \'foo\'">helloString</copy-me>' +
'<copy-me template="ngIf functionCondition(stringCondition, numberCondition)">helloFunction</copy-me>' +
'</div>';
tcb.overrideTemplate(TestComponent, html)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
expect(getDOM()
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
.length)
.toEqual(3);
expect(getDOM().getText(fixture.debugElement.nativeElement))
.toEqual('helloNumberhelloStringhelloFunction');
fixture.debugElement.componentInstance.numberCondition = 0;
fixture.detectChanges();
expect(getDOM()
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
.length)
.toEqual(1);
expect(fixture.debugElement.nativeElement).toHaveText('helloString');
fixture.debugElement.componentInstance.numberCondition = 1;
fixture.debugElement.componentInstance.stringCondition = 'bar';
fixture.detectChanges();
expect(getDOM()
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
.length)
.toEqual(1);
expect(fixture.debugElement.nativeElement).toHaveText('helloNumber');
async.done();
});
}));
it('should not add the element twice if the condition goes from true to true (JS)',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var html = '<div><copy-me template="ngIf numberCondition">hello</copy-me></div>';
tcb.overrideTemplate(TestComponent, html)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
expect(getDOM()
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
.length)
.toEqual(1);
expect(fixture.debugElement.nativeElement).toHaveText('hello');
fixture.debugElement.componentInstance.numberCondition = 2;
fixture.detectChanges();
expect(getDOM()
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
.length)
.toEqual(1);
expect(fixture.debugElement.nativeElement).toHaveText('hello');
async.done();
});
}));
it('should not recreate the element if the condition goes from true to true (JS)',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var html = '<div><copy-me template="ngIf numberCondition">hello</copy-me></div>';
tcb.overrideTemplate(TestComponent, html)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
getDOM().addClass(
getDOM().querySelector(fixture.debugElement.nativeElement, 'copy-me'),
'foo');
fixture.debugElement.componentInstance.numberCondition = 2;
fixture.detectChanges();
expect(getDOM().hasClass(
getDOM().querySelector(fixture.debugElement.nativeElement, 'copy-me'),
'foo'))
.toBe(true);
async.done();
});
}));
});
}
@Component({selector: 'test-cmp', directives: [NgIf], template: ''})
class TestComponent {
booleanCondition: boolean;
nestedBooleanCondition: boolean;
numberCondition: number;
stringCondition: string;
functionCondition: Function;
constructor() {
this.booleanCondition = true;
this.nestedBooleanCondition = true;
this.numberCondition = 1;
this.stringCondition = 'foo';
this.functionCondition = function(s: any, n: any): boolean { return s == 'foo' && n == 1; };
}
}

View File

@ -0,0 +1,182 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgLocalization, NgPlural, NgPluralCase} from '@angular/common';
import {Component, Injectable} from '@angular/core';
import {TestComponentBuilder} from '@angular/core/testing';
import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {expect} from '@angular/platform-browser/testing/matchers';
export function main() {
describe('switch', () => {
beforeEachProviders(() => [{provide: NgLocalization, useClass: TestLocalization}]);
it('should display the template according to the exact value',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div>' +
'<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="=0"><li>you have no messages.</li></template>' +
'<template ngPluralCase="=1"><li>you have one message.</li></template>' +
'</ul></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.switchValue = 0;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('you have no messages.');
fixture.debugElement.componentInstance.switchValue = 1;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('you have one message.');
async.done();
});
}));
// https://github.com/angular/angular/issues/9868
// https://github.com/angular/angular/issues/9882
it('should not throw when ngPluralCase contains expressions',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div>' +
'<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="=0"><li>{{ switchValue }}</li></template>' +
'</ul></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.switchValue = 0;
expect(() => fixture.detectChanges()).not.toThrow();
async.done();
});
}));
it('should be applicable to <ng-container> elements',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div>' +
'<ng-container [ngPlural]="switchValue">' +
'<template ngPluralCase="=0">you have no messages.</template>' +
'<template ngPluralCase="=1">you have one message.</template>' +
'</ng-container></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.switchValue = 0;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('you have no messages.');
fixture.debugElement.componentInstance.switchValue = 1;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('you have one message.');
async.done();
});
}));
it('should display the template according to the category',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div>' +
'<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
'<template ngPluralCase="many"><li>you have many messages.</li></template>' +
'</ul></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.switchValue = 2;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement)
.toHaveText('you have a few messages.');
fixture.debugElement.componentInstance.switchValue = 8;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('you have many messages.');
async.done();
});
}));
it('should default to other when no matches are found',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div>' +
'<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
'<template ngPluralCase="other"><li>default message.</li></template>' +
'</ul></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.switchValue = 100;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('default message.');
async.done();
});
}));
it('should prioritize value matches over category matches',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div>' +
'<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
'<template ngPluralCase="=2">you have two messages.</template>' +
'</ul></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.switchValue = 2;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('you have two messages.');
fixture.debugElement.componentInstance.switchValue = 3;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement)
.toHaveText('you have a few messages.');
async.done();
});
}));
});
}
@Injectable()
class TestLocalization extends NgLocalization {
getPluralCategory(value: number): string {
if (value > 1 && value < 4) {
return 'few';
}
if (value >= 4 && value < 10) {
return 'many';
}
return 'other';
}
}
@Component({selector: 'test-cmp', directives: [NgPlural, NgPluralCase], template: ''})
class TestComponent {
switchValue: number = null;
}

View File

@ -0,0 +1,211 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, xdescribe, describe, expect, iit, inject, it, xit,} from '@angular/core/testing/testing_internal';
import {TestComponentBuilder, ComponentFixture} from '@angular/core/testing';
import {Component} from '@angular/core';
import {NgStyle} from '@angular/common/src/directives/ng_style';
function expectNativeEl(fixture: ComponentFixture<any>) {
return <any>expect(fixture.debugElement.children[0].nativeElement);
}
export function main() {
describe('binding to CSS styles', () => {
it('should add styles specified in an object literal',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = `<div [ngStyle]="{'max-width': '40px'}"></div>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
async.done();
});
}));
it('should add and change styles specified in an object expression',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = `<div [ngStyle]="expr"></div>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
var expr: Map<string, any>;
fixture.debugElement.componentInstance.expr = {'max-width': '40px'};
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
expr = fixture.debugElement.componentInstance.expr;
(expr as any)['max-width'] = '30%';
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '30%'});
async.done();
});
}));
it('should add and remove styles specified using style.unit notation',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = `<div [ngStyle]="{'max-width.px': expr}"></div>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.expr = '40';
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
fixture.debugElement.componentInstance.expr = null;
fixture.detectChanges();
expectNativeEl(fixture).not.toHaveCssStyle('max-width');
async.done();
});
}));
it('should update styles using style.unit notation when unit changes',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = `<div [ngStyle]="expr"></div>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.expr = {'max-width.px': '40'};
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
fixture.debugElement.componentInstance.expr = {'max-width.em': '40'};
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40em'});
async.done();
});
}));
// keyValueDiffer is sensitive to key order #9115
it('should change styles specified in an object expression',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
const template = `<div [ngStyle]="expr"></div>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.expr = {
// height, width order is important here
height: '10px',
width: '10px'
};
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'height': '10px', 'width': '10px'});
fixture.debugElement.componentInstance.expr = {
// width, height order is important here
width: '5px',
height: '5px',
};
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'height': '5px', 'width': '5px'});
async.done();
});
}));
it('should remove styles when deleting a key in an object expression',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = `<div [ngStyle]="expr"></div>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.expr = {'max-width': '40px'};
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
delete fixture.debugElement.componentInstance.expr['max-width'];
fixture.detectChanges();
expectNativeEl(fixture).not.toHaveCssStyle('max-width');
async.done();
});
}));
it('should co-operate with the style attribute',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = `<div style="font-size: 12px" [ngStyle]="expr"></div>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.expr = {'max-width': '40px'};
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle(
{'max-width': '40px', 'font-size': '12px'});
delete fixture.debugElement.componentInstance.expr['max-width'];
fixture.detectChanges();
expectNativeEl(fixture).not.toHaveCssStyle('max-width');
expectNativeEl(fixture).toHaveCssStyle({'font-size': '12px'});
async.done();
});
}));
it('should co-operate with the style.[styleName]="expr" special-case in the compiler',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = `<div [style.font-size.px]="12" [ngStyle]="expr"></div>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.expr = {'max-width': '40px'};
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle(
{'max-width': '40px', 'font-size': '12px'});
delete fixture.debugElement.componentInstance.expr['max-width'];
fixture.detectChanges();
expectNativeEl(fixture).not.toHaveCssStyle('max-width');
expectNativeEl(fixture).toHaveCssStyle({'font-size': '12px'});
async.done();
});
}));
});
}
@Component({selector: 'test-cmp', directives: [NgStyle], template: ''})
class TestComponent {
expr: any;
}

View File

@ -0,0 +1,193 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgSwitch, NgSwitchCase, NgSwitchDefault} from '@angular/common';
import {Component} from '@angular/core';
import {TestComponentBuilder} from '@angular/core/testing';
import {AsyncTestCompleter, beforeEach, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {expect} from '@angular/platform-browser/testing/matchers';
export function main() {
describe('switch', () => {
describe('switch value changes', () => {
it('should switch amongst when values',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<template ngSwitchCase="a"><li>when a</li></template>' +
'<template ngSwitchCase="b"><li>when b</li></template>' +
'</ul></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('');
fixture.debugElement.componentInstance.switchValue = 'a';
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('when a');
fixture.debugElement.componentInstance.switchValue = 'b';
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('when b');
async.done();
});
}));
// TODO(robwormald): deprecate and remove
it('should switch amongst when values using switchWhen',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<template ngSwitchWhen="a"><li>when a</li></template>' +
'<template ngSwitchWhen="b"><li>when b</li></template>' +
'</ul></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('');
fixture.debugElement.componentInstance.switchValue = 'a';
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('when a');
fixture.debugElement.componentInstance.switchValue = 'b';
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('when b');
async.done();
});
}));
it('should switch amongst when values with fallback to default',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<li template="ngSwitchCase \'a\'">when a</li>' +
'<li template="ngSwitchDefault">when default</li>' +
'</ul></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('when default');
fixture.debugElement.componentInstance.switchValue = 'a';
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('when a');
fixture.debugElement.componentInstance.switchValue = 'b';
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('when default');
async.done();
});
}));
it('should support multiple whens with the same value',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<template ngSwitchCase="a"><li>when a1;</li></template>' +
'<template ngSwitchCase="b"><li>when b1;</li></template>' +
'<template ngSwitchCase="a"><li>when a2;</li></template>' +
'<template ngSwitchCase="b"><li>when b2;</li></template>' +
'<template ngSwitchDefault><li>when default1;</li></template>' +
'<template ngSwitchDefault><li>when default2;</li></template>' +
'</ul></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
expect(fixture.debugElement.nativeElement)
.toHaveText('when default1;when default2;');
fixture.debugElement.componentInstance.switchValue = 'a';
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('when a1;when a2;');
fixture.debugElement.componentInstance.switchValue = 'b';
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('when b1;when b2;');
async.done();
});
}));
});
describe('when values changes', () => {
it('should switch amongst when values',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<template [ngSwitchCase]="when1"><li>when 1;</li></template>' +
'<template [ngSwitchCase]="when2"><li>when 2;</li></template>' +
'<template ngSwitchDefault><li>when default;</li></template>' +
'</ul></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.debugElement.componentInstance.when1 = 'a';
fixture.debugElement.componentInstance.when2 = 'b';
fixture.debugElement.componentInstance.switchValue = 'a';
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('when 1;');
fixture.debugElement.componentInstance.switchValue = 'b';
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('when 2;');
fixture.debugElement.componentInstance.switchValue = 'c';
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('when default;');
fixture.debugElement.componentInstance.when1 = 'c';
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('when 1;');
fixture.debugElement.componentInstance.when1 = 'd';
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('when default;');
async.done();
});
}));
});
});
}
@Component(
{selector: 'test-cmp', directives: [NgSwitch, NgSwitchCase, NgSwitchDefault], template: ''})
class TestComponent {
switchValue: any;
when1: any;
when2: any;
constructor() {
this.switchValue = null;
this.when1 = null;
this.when2 = null;
}
}

View File

@ -0,0 +1,218 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgTemplateOutlet} from '@angular/common';
import {Component, ContentChildren, Directive, QueryList, TemplateRef} from '@angular/core';
import {TestComponentBuilder} from '@angular/core/testing';
import {AsyncTestCompleter, beforeEach, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {expect} from '@angular/platform-browser/testing/matchers';
export function main() {
describe('insert', () => {
it('should do nothing if templateRef is null',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = `<template [ngTemplateOutlet]="null"></template>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('');
async.done();
});
}));
it('should insert content specified by TemplateRef',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template =
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('');
var refs = fixture.debugElement.children[0].references['refs'];
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('foo');
async.done();
});
}));
it('should clear content if TemplateRef becomes null',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template =
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
var refs = fixture.debugElement.children[0].references['refs'];
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('foo');
fixture.componentInstance.currentTplRef = null;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('');
async.done();
});
}));
it('should swap content if TemplateRef changes',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template =
`<tpl-refs #refs="tplRefs"><template>foo</template><template>bar</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
var refs = fixture.debugElement.children[0].references['refs'];
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('foo');
fixture.componentInstance.currentTplRef = refs.tplRefs.last;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('bar');
async.done();
});
}));
it('should display template if context is null',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template =
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="null"></template>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('');
var refs = fixture.debugElement.children[0].references['refs'];
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('foo');
async.done();
});
}));
it('should reflect initial context and changes',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template =
`<tpl-refs #refs="tplRefs"><template let-foo="foo"><span>{{foo}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
var refs = fixture.debugElement.children[0].references['refs'];
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('bar');
fixture.componentInstance.context.foo = 'alter-bar';
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('alter-bar');
async.done();
});
}));
it('should reflect user defined $implicit property in the context',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template =
`<tpl-refs #refs="tplRefs"><template let-ctx><span>{{ctx.foo}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
var refs = fixture.debugElement.children[0].references['refs'];
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
fixture.componentInstance.context = {
$implicit: fixture.componentInstance.context
};
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('bar');
async.done();
});
}));
it('should reflect context re-binding',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template =
`<tpl-refs #refs="tplRefs"><template let-shawshank="shawshank"><span>{{shawshank}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
var refs = fixture.debugElement.children[0].references['refs'];
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
fixture.componentInstance.context = {shawshank: 'brooks'};
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('brooks');
fixture.componentInstance.context = {shawshank: 'was here'};
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('was here');
async.done();
});
}));
});
}
@Directive({selector: 'tpl-refs', exportAs: 'tplRefs'})
class CaptureTplRefs {
@ContentChildren(TemplateRef) tplRefs: QueryList<TemplateRef<any>>;
}
@Component({selector: 'test-cmp', directives: [NgTemplateOutlet, CaptureTplRefs], template: ''})
class TestComponent {
currentTplRef: TemplateRef<any>;
context: any = {foo: 'bar'};
}

View File

@ -0,0 +1,76 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Component, Directive} from '@angular/core';
import {ElementRef} from '@angular/core/src/linker/element_ref';
import {TestComponentBuilder} from '@angular/core/testing';
import {AsyncTestCompleter, beforeEach, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {expect} from '@angular/platform-browser/testing/matchers';
export function main() {
describe('non-bindable', () => {
it('should not interpolate children',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div>{{text}}<span ngNonBindable>{{text}}</span></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('foo{{text}}');
async.done();
});
}));
it('should ignore directives on child nodes',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div ngNonBindable><span id=child test-dec>{{text}}</span></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
// We must use getDOM().querySelector instead of fixture.query here
// since the elements inside are not compiled.
var span = getDOM().querySelector(fixture.debugElement.nativeElement, '#child');
expect(getDOM().hasClass(span, 'compiled')).toBeFalsy();
async.done();
});
}));
it('should trigger directives on the same node',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
var template = '<div><span id=child ngNonBindable test-dec>{{text}}</span></div>';
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((fixture) => {
fixture.detectChanges();
var span = getDOM().querySelector(fixture.debugElement.nativeElement, '#child');
expect(getDOM().hasClass(span, 'compiled')).toBeTruthy();
async.done();
});
}));
});
}
@Directive({selector: '[test-dec]'})
class TestDirective {
constructor(el: ElementRef) { getDOM().addClass(el.nativeElement, 'compiled'); }
}
@Component({selector: 'test-cmp', directives: [TestDirective], template: ''})
class TestComponent {
text: string;
constructor() { this.text = 'foo'; }
}

View File

@ -0,0 +1,489 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {CheckboxControlValueAccessor, Control, ControlGroup, ControlValueAccessor, DefaultValueAccessor, NgControl, NgControlGroup, NgControlName, NgForm, NgFormControl, NgFormModel, NgModel, SelectControlValueAccessor, Validator, Validators} from '@angular/common/src/forms-deprecated';
import {composeValidators, selectValueAccessor} from '@angular/common/src/forms-deprecated/directives/shared';
import {SimpleChange} from '@angular/core/src/change_detection';
import {fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
import {afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {SpyNgControl, SpyValueAccessor} from '../spies';
class DummyControlValueAccessor implements ControlValueAccessor {
writtenValue: any /** TODO #9100 */;
registerOnChange(fn: any /** TODO #9100 */) {}
registerOnTouched(fn: any /** TODO #9100 */) {}
writeValue(obj: any): void { this.writtenValue = obj; }
}
class CustomValidatorDirective implements Validator {
validate(c: Control): {[key: string]: any} { return {'custom': true}; }
}
function asyncValidator(expected: any /** TODO #9100 */, timeout = 0) {
return (c: any /** TODO #9100 */) => {
return new Promise((resolve) => {
var res = c.value != expected ? {'async': true} : null;
if (timeout == 0) {
resolve(res);
} else {
setTimeout(() => { resolve(res); }, timeout);
}
});
};
}
export function main() {
describe('Form Directives', () => {
var defaultAccessor: DefaultValueAccessor;
beforeEach(() => { defaultAccessor = new DefaultValueAccessor(null, null); });
describe('shared', () => {
describe('selectValueAccessor', () => {
var dir: NgControl;
beforeEach(() => { dir = <any>new SpyNgControl(); });
it('should throw when given an empty array',
() => { expect(() => selectValueAccessor(dir, [])).toThrowError(); });
it('should return the default value accessor when no other provided',
() => { expect(selectValueAccessor(dir, [defaultAccessor])).toEqual(defaultAccessor); });
it('should return checkbox accessor when provided', () => {
var checkboxAccessor = new CheckboxControlValueAccessor(null, null);
expect(selectValueAccessor(dir, [
defaultAccessor, checkboxAccessor
])).toEqual(checkboxAccessor);
});
it('should return select accessor when provided', () => {
var selectAccessor = new SelectControlValueAccessor(null, null);
expect(selectValueAccessor(dir, [
defaultAccessor, selectAccessor
])).toEqual(selectAccessor);
});
it('should throw when more than one build-in accessor is provided', () => {
var checkboxAccessor = new CheckboxControlValueAccessor(null, null);
var selectAccessor = new SelectControlValueAccessor(null, null);
expect(() => selectValueAccessor(dir, [checkboxAccessor, selectAccessor])).toThrowError();
});
it('should return custom accessor when provided', () => {
var customAccessor = new SpyValueAccessor();
var checkboxAccessor = new CheckboxControlValueAccessor(null, null);
expect(selectValueAccessor(dir, <any>[defaultAccessor, customAccessor, checkboxAccessor]))
.toEqual(customAccessor);
});
it('should throw when more than one custom accessor is provided', () => {
var customAccessor: ControlValueAccessor = <any>new SpyValueAccessor();
expect(() => selectValueAccessor(dir, [customAccessor, customAccessor])).toThrowError();
});
});
describe('composeValidators', () => {
it('should compose functions', () => {
var dummy1 = (_: any /** TODO #9100 */) => ({'dummy1': true});
var dummy2 = (_: any /** TODO #9100 */) => ({'dummy2': true});
var v = composeValidators([dummy1, dummy2]);
expect(v(new Control(''))).toEqual({'dummy1': true, 'dummy2': true});
});
it('should compose validator directives', () => {
var dummy1 = (_: any /** TODO #9100 */) => ({'dummy1': true});
var v = composeValidators([dummy1, new CustomValidatorDirective()]);
expect(v(new Control(''))).toEqual({'dummy1': true, 'custom': true});
});
});
});
describe('NgFormModel', () => {
var form: any /** TODO #9100 */;
var formModel: ControlGroup;
var loginControlDir: any /** TODO #9100 */;
beforeEach(() => {
form = new NgFormModel([], []);
formModel = new ControlGroup({
'login': new Control(),
'passwords':
new ControlGroup({'password': new Control(), 'passwordConfirm': new Control()})
});
form.form = formModel;
loginControlDir = new NgControlName(
form, [Validators.required], [asyncValidator('expected')], [defaultAccessor]);
loginControlDir.name = 'login';
loginControlDir.valueAccessor = new DummyControlValueAccessor();
});
it('should reexport control properties', () => {
expect(form.control).toBe(formModel);
expect(form.value).toBe(formModel.value);
expect(form.valid).toBe(formModel.valid);
expect(form.errors).toBe(formModel.errors);
expect(form.pristine).toBe(formModel.pristine);
expect(form.dirty).toBe(formModel.dirty);
expect(form.touched).toBe(formModel.touched);
expect(form.untouched).toBe(formModel.untouched);
});
describe('addControl', () => {
it('should throw when no control found', () => {
var dir = new NgControlName(form, null, null, [defaultAccessor]);
dir.name = 'invalidName';
expect(() => form.addControl(dir))
.toThrowError(new RegExp(`Cannot find control with name: 'invalidName'`));
});
it('should throw when no value accessor', () => {
var dir = new NgControlName(form, null, null, null);
dir.name = 'login';
expect(() => form.addControl(dir))
.toThrowError(new RegExp(`No value accessor for form control with name: 'login'`));
});
it('should throw when no value accessor with path', () => {
const group = new NgControlGroup(form, null, null);
const dir = new NgControlName(group, null, null, null);
group.name = 'passwords';
dir.name = 'password';
expect(() => form.addControl(dir))
.toThrowError(new RegExp(
`No value accessor for form control with path: 'passwords -> password'`));
});
it('should set up validators', fakeAsync(() => {
form.addControl(loginControlDir);
// sync validators are set
expect(formModel.hasError('required', ['login'])).toBe(true);
expect(formModel.hasError('async', ['login'])).toBe(false);
(<Control>formModel.find(['login'])).updateValue('invalid value');
// sync validator passes, running async validators
expect(formModel.pending).toBe(true);
tick();
expect(formModel.hasError('required', ['login'])).toBe(false);
expect(formModel.hasError('async', ['login'])).toBe(true);
}));
it('should write value to the DOM', () => {
(<Control>formModel.find(['login'])).updateValue('initValue');
form.addControl(loginControlDir);
expect((<any>loginControlDir.valueAccessor).writtenValue).toEqual('initValue');
});
it('should add the directive to the list of directives included in the form', () => {
form.addControl(loginControlDir);
expect(form.directives).toEqual([loginControlDir]);
});
});
describe('addControlGroup', () => {
var matchingPasswordsValidator = (g: any /** TODO #9100 */) => {
if (g.controls['password'].value != g.controls['passwordConfirm'].value) {
return {'differentPasswords': true};
} else {
return null;
}
};
it('should set up validator', fakeAsync(() => {
var group = new NgControlGroup(
form, [matchingPasswordsValidator], [asyncValidator('expected')]);
group.name = 'passwords';
form.addControlGroup(group);
(<Control>formModel.find(['passwords', 'password'])).updateValue('somePassword');
(<Control>formModel.find([
'passwords', 'passwordConfirm'
])).updateValue('someOtherPassword');
// sync validators are set
expect(formModel.hasError('differentPasswords', ['passwords'])).toEqual(true);
(<Control>formModel.find([
'passwords', 'passwordConfirm'
])).updateValue('somePassword');
// sync validators pass, running async validators
expect(formModel.pending).toBe(true);
tick();
expect(formModel.hasError('async', ['passwords'])).toBe(true);
}));
});
describe('removeControl', () => {
it('should remove the directive to the list of directives included in the form', () => {
form.addControl(loginControlDir);
form.removeControl(loginControlDir);
expect(form.directives).toEqual([]);
});
});
describe('ngOnChanges', () => {
it('should update dom values of all the directives', () => {
form.addControl(loginControlDir);
(<Control>formModel.find(['login'])).updateValue('new value');
form.ngOnChanges({});
expect((<any>loginControlDir.valueAccessor).writtenValue).toEqual('new value');
});
it('should set up a sync validator', () => {
var formValidator = (c: any /** TODO #9100 */) => ({'custom': true});
var f = new NgFormModel([formValidator], []);
f.form = formModel;
f.ngOnChanges({'form': new SimpleChange(null, null)});
expect(formModel.errors).toEqual({'custom': true});
});
it('should set up an async validator', fakeAsync(() => {
var f = new NgFormModel([], [asyncValidator('expected')]);
f.form = formModel;
f.ngOnChanges({'form': new SimpleChange(null, null)});
tick();
expect(formModel.errors).toEqual({'async': true});
}));
});
});
describe('NgForm', () => {
var form: any /** TODO #9100 */;
var formModel: ControlGroup;
var loginControlDir: any /** TODO #9100 */;
var personControlGroupDir: any /** TODO #9100 */;
beforeEach(() => {
form = new NgForm([], []);
formModel = form.form;
personControlGroupDir = new NgControlGroup(form, [], []);
personControlGroupDir.name = 'person';
loginControlDir = new NgControlName(personControlGroupDir, null, null, [defaultAccessor]);
loginControlDir.name = 'login';
loginControlDir.valueAccessor = new DummyControlValueAccessor();
});
it('should reexport control properties', () => {
expect(form.control).toBe(formModel);
expect(form.value).toBe(formModel.value);
expect(form.valid).toBe(formModel.valid);
expect(form.errors).toBe(formModel.errors);
expect(form.pristine).toBe(formModel.pristine);
expect(form.dirty).toBe(formModel.dirty);
expect(form.touched).toBe(formModel.touched);
expect(form.untouched).toBe(formModel.untouched);
});
describe('addControl & addControlGroup', () => {
it('should create a control with the given name', fakeAsync(() => {
form.addControlGroup(personControlGroupDir);
form.addControl(loginControlDir);
flushMicrotasks();
expect(formModel.find(['person', 'login'])).not.toBeNull;
}));
// should update the form's value and validity
});
describe('removeControl & removeControlGroup', () => {
it('should remove control', fakeAsync(() => {
form.addControlGroup(personControlGroupDir);
form.addControl(loginControlDir);
form.removeControlGroup(personControlGroupDir);
form.removeControl(loginControlDir);
flushMicrotasks();
expect(formModel.find(['person'])).toBeNull();
expect(formModel.find(['person', 'login'])).toBeNull();
}));
// should update the form's value and validity
});
it('should set up sync validator', fakeAsync(() => {
var formValidator = (c: any /** TODO #9100 */) => ({'custom': true});
var f = new NgForm([formValidator], []);
tick();
expect(f.form.errors).toEqual({'custom': true});
}));
it('should set up async validator', fakeAsync(() => {
var f = new NgForm([], [asyncValidator('expected')]);
tick();
expect(f.form.errors).toEqual({'async': true});
}));
});
describe('NgControlGroup', () => {
var formModel: any /** TODO #9100 */;
var controlGroupDir: any /** TODO #9100 */;
beforeEach(() => {
formModel = new ControlGroup({'login': new Control(null)});
var parent = new NgFormModel([], []);
parent.form = new ControlGroup({'group': formModel});
controlGroupDir = new NgControlGroup(parent, [], []);
controlGroupDir.name = 'group';
});
it('should reexport control properties', () => {
expect(controlGroupDir.control).toBe(formModel);
expect(controlGroupDir.value).toBe(formModel.value);
expect(controlGroupDir.valid).toBe(formModel.valid);
expect(controlGroupDir.errors).toBe(formModel.errors);
expect(controlGroupDir.pristine).toBe(formModel.pristine);
expect(controlGroupDir.dirty).toBe(formModel.dirty);
expect(controlGroupDir.touched).toBe(formModel.touched);
expect(controlGroupDir.untouched).toBe(formModel.untouched);
});
});
describe('NgFormControl', () => {
var controlDir: any /** TODO #9100 */;
var control: any /** TODO #9100 */;
var checkProperties = function(control: any /** TODO #9100 */) {
expect(controlDir.control).toBe(control);
expect(controlDir.value).toBe(control.value);
expect(controlDir.valid).toBe(control.valid);
expect(controlDir.errors).toBe(control.errors);
expect(controlDir.pristine).toBe(control.pristine);
expect(controlDir.dirty).toBe(control.dirty);
expect(controlDir.touched).toBe(control.touched);
expect(controlDir.untouched).toBe(control.untouched);
};
beforeEach(() => {
controlDir = new NgFormControl([Validators.required], [], [defaultAccessor]);
controlDir.valueAccessor = new DummyControlValueAccessor();
control = new Control(null);
controlDir.form = control;
});
it('should reexport control properties', () => { checkProperties(control); });
it('should reexport new control properties', () => {
var newControl = new Control(null);
controlDir.form = newControl;
controlDir.ngOnChanges({'form': new SimpleChange(control, newControl)});
checkProperties(newControl);
});
it('should set up validator', () => {
expect(control.valid).toBe(true);
// this will add the required validator and recalculate the validity
controlDir.ngOnChanges({'form': new SimpleChange(null, control)});
expect(control.valid).toBe(false);
});
});
describe('NgModel', () => {
var ngModel: any /** TODO #9100 */;
beforeEach(() => {
ngModel =
new NgModel([Validators.required], [asyncValidator('expected')], [defaultAccessor]);
ngModel.valueAccessor = new DummyControlValueAccessor();
});
it('should reexport control properties', () => {
var control = ngModel.control;
expect(ngModel.control).toBe(control);
expect(ngModel.value).toBe(control.value);
expect(ngModel.valid).toBe(control.valid);
expect(ngModel.errors).toBe(control.errors);
expect(ngModel.pristine).toBe(control.pristine);
expect(ngModel.dirty).toBe(control.dirty);
expect(ngModel.touched).toBe(control.touched);
expect(ngModel.untouched).toBe(control.untouched);
});
it('should throw when no value accessor with unnamed control', () => {
const unnamedDir = new NgModel(null, null, null);
expect(() => unnamedDir.ngOnChanges({}))
.toThrowError(new RegExp(`No value accessor for form control with unspecified name`));
});
it('should set up validator', fakeAsync(() => {
// this will add the required validator and recalculate the validity
ngModel.ngOnChanges({});
tick();
expect(ngModel.control.errors).toEqual({'required': true});
ngModel.control.updateValue('someValue');
tick();
expect(ngModel.control.errors).toEqual({'async': true});
}));
});
describe('NgControlName', () => {
var formModel: any /** TODO #9100 */;
var controlNameDir: any /** TODO #9100 */;
beforeEach(() => {
formModel = new Control('name');
var parent = new NgFormModel([], []);
parent.form = new ControlGroup({'name': formModel});
controlNameDir = new NgControlName(parent, [], [], [defaultAccessor]);
controlNameDir.name = 'name';
});
it('should reexport control properties', () => {
expect(controlNameDir.control).toBe(formModel);
expect(controlNameDir.value).toBe(formModel.value);
expect(controlNameDir.valid).toBe(formModel.valid);
expect(controlNameDir.errors).toBe(formModel.errors);
expect(controlNameDir.pristine).toBe(formModel.pristine);
expect(controlNameDir.dirty).toBe(formModel.dirty);
expect(controlNameDir.touched).toBe(formModel.touched);
expect(controlNameDir.untouched).toBe(formModel.untouched);
});
});
});
}

View File

@ -0,0 +1,69 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Control, FormBuilder} from '@angular/common/src/forms-deprecated';
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
export function main() {
function syncValidator(_: any): any { return null; }
function asyncValidator(_: any) { return Promise.resolve(null); }
describe('Form Builder', () => {
var b: any /** TODO #9100 */;
beforeEach(() => { b = new FormBuilder(); });
it('should create controls from a value', () => {
var g = b.group({'login': 'some value'});
expect(g.controls['login'].value).toEqual('some value');
});
it('should create controls from an array', () => {
var g = b.group(
{'login': ['some value'], 'password': ['some value', syncValidator, asyncValidator]});
expect(g.controls['login'].value).toEqual('some value');
expect(g.controls['password'].value).toEqual('some value');
expect(g.controls['password'].validator).toEqual(syncValidator);
expect(g.controls['password'].asyncValidator).toEqual(asyncValidator);
});
it('should use controls', () => {
var g = b.group({'login': b.control('some value', syncValidator, asyncValidator)});
expect(g.controls['login'].value).toEqual('some value');
expect(g.controls['login'].validator).toBe(syncValidator);
expect(g.controls['login'].asyncValidator).toBe(asyncValidator);
});
it('should create groups with optional controls', () => {
var g = b.group({'login': 'some value'}, {'optionals': {'login': false}});
expect(g.contains('login')).toEqual(false);
});
it('should create groups with a custom validator', () => {
var g = b.group(
{'login': 'some value'}, {'validator': syncValidator, 'asyncValidator': asyncValidator});
expect(g.validator).toBe(syncValidator);
expect(g.asyncValidator).toBe(asyncValidator);
});
it('should create control arrays', () => {
var c = b.control('three');
var a = b.array(
['one', ['two', syncValidator], c, b.array(['four'])], syncValidator, asyncValidator);
expect(a.value).toEqual(['one', 'two', 'three', ['four']]);
expect(a.validator).toBe(syncValidator);
expect(a.asyncValidator).toBe(asyncValidator);
});
});
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,856 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Control, ControlArray, ControlGroup, Validators} from '@angular/common/src/forms-deprecated';
import {fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {EventEmitter} from '../../src/facade/async';
import {isPresent} from '../../src/facade/lang';
export function main() {
function asyncValidator(expected: any /** TODO #9100 */, timeouts = {}) {
return (c: any /** TODO #9100 */) => {
return new Promise((resolve) => {
var t = isPresent((timeouts as any /** TODO #9100 */)[c.value]) ?
(timeouts as any /** TODO #9100 */)[c.value] :
0;
var res = c.value != expected ? {'async': true} : null;
if (t == 0) {
resolve(res);
} else {
setTimeout(() => { resolve(res); }, t);
}
});
};
}
function asyncValidatorReturningObservable(c: any /** TODO #9100 */) {
var e = new EventEmitter();
Promise.resolve(null).then(() => { e.emit({'async': true}); });
return e;
}
describe('Form Model', () => {
describe('Control', () => {
it('should default the value to null', () => {
var c = new Control();
expect(c.value).toBe(null);
});
describe('validator', () => {
it('should run validator with the initial value', () => {
var c = new Control('value', Validators.required);
expect(c.valid).toEqual(true);
});
it('should rerun the validator when the value changes', () => {
var c = new Control('value', Validators.required);
c.updateValue(null);
expect(c.valid).toEqual(false);
});
it('should return errors', () => {
var c = new Control(null, Validators.required);
expect(c.errors).toEqual({'required': true});
});
});
describe('asyncValidator', () => {
it('should run validator with the initial value', fakeAsync(() => {
var c = new Control('value', null, asyncValidator('expected'));
tick();
expect(c.valid).toEqual(false);
expect(c.errors).toEqual({'async': true});
}));
it('should support validators returning observables', fakeAsync(() => {
var c = new Control('value', null, asyncValidatorReturningObservable);
tick();
expect(c.valid).toEqual(false);
expect(c.errors).toEqual({'async': true});
}));
it('should rerun the validator when the value changes', fakeAsync(() => {
var c = new Control('value', null, asyncValidator('expected'));
c.updateValue('expected');
tick();
expect(c.valid).toEqual(true);
}));
it('should run the async validator only when the sync validator passes', fakeAsync(() => {
var c = new Control('', Validators.required, asyncValidator('expected'));
tick();
expect(c.errors).toEqual({'required': true});
c.updateValue('some value');
tick();
expect(c.errors).toEqual({'async': true});
}));
it('should mark the control as pending while running the async validation',
fakeAsync(() => {
var c = new Control('', null, asyncValidator('expected'));
expect(c.pending).toEqual(true);
tick();
expect(c.pending).toEqual(false);
}));
it('should only use the latest async validation run', fakeAsync(() => {
var c =
new Control('', null, asyncValidator('expected', {'long': 200, 'expected': 100}));
c.updateValue('long');
c.updateValue('expected');
tick(300);
expect(c.valid).toEqual(true);
}));
});
describe('dirty', () => {
it('should be false after creating a control', () => {
var c = new Control('value');
expect(c.dirty).toEqual(false);
});
it('should be true after changing the value of the control', () => {
var c = new Control('value');
c.markAsDirty();
expect(c.dirty).toEqual(true);
});
});
describe('updateValue', () => {
var g: any /** TODO #9100 */, c: any /** TODO #9100 */;
beforeEach(() => {
c = new Control('oldValue');
g = new ControlGroup({'one': c});
});
it('should update the value of the control', () => {
c.updateValue('newValue');
expect(c.value).toEqual('newValue');
});
it('should invoke ngOnChanges if it is present', () => {
var ngOnChanges: any /** TODO #9100 */;
c.registerOnChange((v: any /** TODO #9100 */) => ngOnChanges = ['invoked', v]);
c.updateValue('newValue');
expect(ngOnChanges).toEqual(['invoked', 'newValue']);
});
it('should not invoke on change when explicitly specified', () => {
var onChange: any /** TODO #9100 */ = null;
c.registerOnChange((v: any /** TODO #9100 */) => onChange = ['invoked', v]);
c.updateValue('newValue', {emitModelToViewChange: false});
expect(onChange).toBeNull();
});
it('should update the parent', () => {
c.updateValue('newValue');
expect(g.value).toEqual({'one': 'newValue'});
});
it('should not update the parent when explicitly specified', () => {
c.updateValue('newValue', {onlySelf: true});
expect(g.value).toEqual({'one': 'oldValue'});
});
it('should fire an event', fakeAsync(() => {
c.valueChanges.subscribe(
{next: (value: any) => { expect(value).toEqual('newValue'); }});
c.updateValue('newValue');
tick();
}));
it('should not fire an event when explicitly specified', fakeAsync(() => {
c.valueChanges.subscribe({next: (value: any) => { throw 'Should not happen'; }});
c.updateValue('newValue', {emitEvent: false});
tick();
}));
});
describe('valueChanges & statusChanges', () => {
var c: any /** TODO #9100 */;
beforeEach(() => { c = new Control('old', Validators.required); });
it('should fire an event after the value has been updated',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
c.valueChanges.subscribe({
next: (value: any) => {
expect(c.value).toEqual('new');
expect(value).toEqual('new');
async.done();
}
});
c.updateValue('new');
}));
it('should fire an event after the status has been updated to invalid', fakeAsync(() => {
c.statusChanges.subscribe({
next: (status: any) => {
expect(c.status).toEqual('INVALID');
expect(status).toEqual('INVALID');
}
});
c.updateValue('');
tick();
}));
it('should fire an event after the status has been updated to pending', fakeAsync(() => {
var c = new Control('old', Validators.required, asyncValidator('expected'));
var log: any[] /** TODO #9100 */ = [];
c.valueChanges.subscribe({next: (value: any) => log.push(`value: '${value}'`)});
c.statusChanges.subscribe({next: (status: any) => log.push(`status: '${status}'`)});
c.updateValue('');
tick();
c.updateValue('nonEmpty');
tick();
c.updateValue('expected');
tick();
expect(log).toEqual([
'' +
'value: \'\'',
'status: \'INVALID\'',
'value: \'nonEmpty\'',
'status: \'PENDING\'',
'status: \'INVALID\'',
'value: \'expected\'',
'status: \'PENDING\'',
'status: \'VALID\'',
]);
}));
// TODO: remove the if statement after making observable delivery sync
it('should update set errors and status before emitting an event',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
c.valueChanges.subscribe((value: any /** TODO #9100 */) => {
expect(c.valid).toEqual(false);
expect(c.errors).toEqual({'required': true});
async.done();
});
c.updateValue('');
}));
it('should return a cold observable',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
c.updateValue('will be ignored');
c.valueChanges.subscribe({
next: (value: any) => {
expect(value).toEqual('new');
async.done();
}
});
c.updateValue('new');
}));
});
describe('setErrors', () => {
it('should set errors on a control', () => {
var c = new Control('someValue');
c.setErrors({'someError': true});
expect(c.valid).toEqual(false);
expect(c.errors).toEqual({'someError': true});
});
it('should reset the errors and validity when the value changes', () => {
var c = new Control('someValue', Validators.required);
c.setErrors({'someError': true});
c.updateValue('');
expect(c.errors).toEqual({'required': true});
});
it('should update the parent group\'s validity', () => {
var c = new Control('someValue');
var g = new ControlGroup({'one': c});
expect(g.valid).toEqual(true);
c.setErrors({'someError': true});
expect(g.valid).toEqual(false);
});
it('should not reset parent\'s errors', () => {
var c = new Control('someValue');
var g = new ControlGroup({'one': c});
g.setErrors({'someGroupError': true});
c.setErrors({'someError': true});
expect(g.errors).toEqual({'someGroupError': true});
});
it('should reset errors when updating a value', () => {
var c = new Control('oldValue');
var g = new ControlGroup({'one': c});
g.setErrors({'someGroupError': true});
c.setErrors({'someError': true});
c.updateValue('newValue');
expect(c.errors).toEqual(null);
expect(g.errors).toEqual(null);
});
});
});
describe('ControlGroup', () => {
describe('value', () => {
it('should be the reduced value of the child controls', () => {
var g = new ControlGroup({'one': new Control('111'), 'two': new Control('222')});
expect(g.value).toEqual({'one': '111', 'two': '222'});
});
it('should be empty when there are no child controls', () => {
var g = new ControlGroup({});
expect(g.value).toEqual({});
});
it('should support nested groups', () => {
var g = new ControlGroup(
{'one': new Control('111'), 'nested': new ControlGroup({'two': new Control('222')})});
expect(g.value).toEqual({'one': '111', 'nested': {'two': '222'}});
(<Control>(g.controls['nested'].find('two'))).updateValue('333');
expect(g.value).toEqual({'one': '111', 'nested': {'two': '333'}});
});
});
describe('adding and removing controls', () => {
it('should update value and validity when control is added', () => {
var g = new ControlGroup({'one': new Control('1')});
expect(g.value).toEqual({'one': '1'});
expect(g.valid).toBe(true);
g.addControl('two', new Control('2', Validators.minLength(10)));
expect(g.value).toEqual({'one': '1', 'two': '2'});
expect(g.valid).toBe(false);
});
it('should update value and validity when control is removed', () => {
var g = new ControlGroup(
{'one': new Control('1'), 'two': new Control('2', Validators.minLength(10))});
expect(g.value).toEqual({'one': '1', 'two': '2'});
expect(g.valid).toBe(false);
g.removeControl('two');
expect(g.value).toEqual({'one': '1'});
expect(g.valid).toBe(true);
});
});
describe('errors', () => {
it('should run the validator when the value changes', () => {
var simpleValidator = (c: any /** TODO #9100 */) =>
c.controls['one'].value != 'correct' ? {'broken': true} : null;
var c = new Control(null);
var g = new ControlGroup({'one': c}, null, simpleValidator);
c.updateValue('correct');
expect(g.valid).toEqual(true);
expect(g.errors).toEqual(null);
c.updateValue('incorrect');
expect(g.valid).toEqual(false);
expect(g.errors).toEqual({'broken': true});
});
});
describe('dirty', () => {
var c: any /** TODO #9100 */, g: any /** TODO #9100 */;
beforeEach(() => {
c = new Control('value');
g = new ControlGroup({'one': c});
});
it('should be false after creating a control', () => { expect(g.dirty).toEqual(false); });
it('should be false after changing the value of the control', () => {
c.markAsDirty();
expect(g.dirty).toEqual(true);
});
});
describe('optional components', () => {
describe('contains', () => {
var group: any /** TODO #9100 */;
beforeEach(() => {
group = new ControlGroup(
{
'required': new Control('requiredValue'),
'optional': new Control('optionalValue')
},
{'optional': false});
});
// rename contains into has
it('should return false when the component is not included',
() => { expect(group.contains('optional')).toEqual(false); });
it('should return false when there is no component with the given name',
() => { expect(group.contains('something else')).toEqual(false); });
it('should return true when the component is included', () => {
expect(group.contains('required')).toEqual(true);
group.include('optional');
expect(group.contains('optional')).toEqual(true);
});
});
it('should not include an inactive component into the group value', () => {
var group = new ControlGroup(
{'required': new Control('requiredValue'), 'optional': new Control('optionalValue')},
{'optional': false});
expect(group.value).toEqual({'required': 'requiredValue'});
group.include('optional');
expect(group.value).toEqual({'required': 'requiredValue', 'optional': 'optionalValue'});
});
it('should not run Validators on an inactive component', () => {
var group = new ControlGroup(
{
'required': new Control('requiredValue', Validators.required),
'optional': new Control('', Validators.required)
},
{'optional': false});
expect(group.valid).toEqual(true);
group.include('optional');
expect(group.valid).toEqual(false);
});
});
describe('valueChanges', () => {
var g: any /** TODO #9100 */, c1: any /** TODO #9100 */, c2: any /** TODO #9100 */;
beforeEach(() => {
c1 = new Control('old1');
c2 = new Control('old2');
g = new ControlGroup({'one': c1, 'two': c2}, {'two': true});
});
it('should fire an event after the value has been updated',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
g.valueChanges.subscribe({
next: (value: any) => {
expect(g.value).toEqual({'one': 'new1', 'two': 'old2'});
expect(value).toEqual({'one': 'new1', 'two': 'old2'});
async.done();
}
});
c1.updateValue('new1');
}));
it('should fire an event after the control\'s observable fired an event',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
var controlCallbackIsCalled = false;
c1.valueChanges.subscribe({next: (value: any) => { controlCallbackIsCalled = true; }});
g.valueChanges.subscribe({
next: (value: any) => {
expect(controlCallbackIsCalled).toBe(true);
async.done();
}
});
c1.updateValue('new1');
}));
it('should fire an event when a control is excluded',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
g.valueChanges.subscribe({
next: (value: any) => {
expect(value).toEqual({'one': 'old1'});
async.done();
}
});
g.exclude('two');
}));
it('should fire an event when a control is included',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
g.exclude('two');
g.valueChanges.subscribe({
next: (value: any) => {
expect(value).toEqual({'one': 'old1', 'two': 'old2'});
async.done();
}
});
g.include('two');
}));
it('should fire an event every time a control is updated',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
var loggedValues: any[] /** TODO #9100 */ = [];
g.valueChanges.subscribe({
next: (value: any) => {
loggedValues.push(value);
if (loggedValues.length == 2) {
expect(loggedValues).toEqual([
{'one': 'new1', 'two': 'old2'}, {'one': 'new1', 'two': 'new2'}
]);
async.done();
}
}
});
c1.updateValue('new1');
c2.updateValue('new2');
}));
xit('should not fire an event when an excluded control is updated',
inject(
[AsyncTestCompleter], (async: AsyncTestCompleter) => {
// hard to test without hacking zones
}));
});
describe('getError', () => {
it('should return the error when it is present', () => {
var c = new Control('', Validators.required);
var g = new ControlGroup({'one': c});
expect(c.getError('required')).toEqual(true);
expect(g.getError('required', ['one'])).toEqual(true);
});
it('should return null otherwise', () => {
var c = new Control('not empty', Validators.required);
var g = new ControlGroup({'one': c});
expect(c.getError('invalid')).toEqual(null);
expect(g.getError('required', ['one'])).toEqual(null);
expect(g.getError('required', ['invalid'])).toEqual(null);
});
});
describe('asyncValidator', () => {
it('should run the async validator', fakeAsync(() => {
var c = new Control('value');
var g = new ControlGroup({'one': c}, null, null, asyncValidator('expected'));
expect(g.pending).toEqual(true);
tick(1);
expect(g.errors).toEqual({'async': true});
expect(g.pending).toEqual(false);
}));
it('should set the parent group\'s status to pending', fakeAsync(() => {
var c = new Control('value', null, asyncValidator('expected'));
var g = new ControlGroup({'one': c});
expect(g.pending).toEqual(true);
tick(1);
expect(g.pending).toEqual(false);
}));
it('should run the parent group\'s async validator when children are pending',
fakeAsync(() => {
var c = new Control('value', null, asyncValidator('expected'));
var g = new ControlGroup({'one': c}, null, null, asyncValidator('expected'));
tick(1);
expect(g.errors).toEqual({'async': true});
expect(g.find(['one']).errors).toEqual({'async': true});
}));
});
});
describe('ControlArray', () => {
describe('adding/removing', () => {
var a: ControlArray;
var c1: any /** TODO #9100 */, c2: any /** TODO #9100 */, c3: any /** TODO #9100 */;
beforeEach(() => {
a = new ControlArray([]);
c1 = new Control(1);
c2 = new Control(2);
c3 = new Control(3);
});
it('should support pushing', () => {
a.push(c1);
expect(a.length).toEqual(1);
expect(a.controls).toEqual([c1]);
});
it('should support removing', () => {
a.push(c1);
a.push(c2);
a.push(c3);
a.removeAt(1);
expect(a.controls).toEqual([c1, c3]);
});
it('should support inserting', () => {
a.push(c1);
a.push(c3);
a.insert(1, c2);
expect(a.controls).toEqual([c1, c2, c3]);
});
});
describe('value', () => {
it('should be the reduced value of the child controls', () => {
var a = new ControlArray([new Control(1), new Control(2)]);
expect(a.value).toEqual([1, 2]);
});
it('should be an empty array when there are no child controls', () => {
var a = new ControlArray([]);
expect(a.value).toEqual([]);
});
});
describe('errors', () => {
it('should run the validator when the value changes', () => {
var simpleValidator = (c: any /** TODO #9100 */) =>
c.controls[0].value != 'correct' ? {'broken': true} : null;
var c = new Control(null);
var g = new ControlArray([c], simpleValidator);
c.updateValue('correct');
expect(g.valid).toEqual(true);
expect(g.errors).toEqual(null);
c.updateValue('incorrect');
expect(g.valid).toEqual(false);
expect(g.errors).toEqual({'broken': true});
});
});
describe('dirty', () => {
var c: Control;
var a: ControlArray;
beforeEach(() => {
c = new Control('value');
a = new ControlArray([c]);
});
it('should be false after creating a control', () => { expect(a.dirty).toEqual(false); });
it('should be false after changing the value of the control', () => {
c.markAsDirty();
expect(a.dirty).toEqual(true);
});
});
describe('pending', () => {
var c: Control;
var a: ControlArray;
beforeEach(() => {
c = new Control('value');
a = new ControlArray([c]);
});
it('should be false after creating a control', () => {
expect(c.pending).toEqual(false);
expect(a.pending).toEqual(false);
});
it('should be true after changing the value of the control', () => {
c.markAsPending();
expect(c.pending).toEqual(true);
expect(a.pending).toEqual(true);
});
it('should not update the parent when onlySelf = true', () => {
c.markAsPending({onlySelf: true});
expect(c.pending).toEqual(true);
expect(a.pending).toEqual(false);
});
});
describe('valueChanges', () => {
var a: ControlArray;
var c1: any /** TODO #9100 */, c2: any /** TODO #9100 */;
beforeEach(() => {
c1 = new Control('old1');
c2 = new Control('old2');
a = new ControlArray([c1, c2]);
});
it('should fire an event after the value has been updated',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
a.valueChanges.subscribe({
next: (value: any) => {
expect(a.value).toEqual(['new1', 'old2']);
expect(value).toEqual(['new1', 'old2']);
async.done();
}
});
c1.updateValue('new1');
}));
it('should fire an event after the control\'s observable fired an event',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
var controlCallbackIsCalled = false;
c1.valueChanges.subscribe({next: (value: any) => { controlCallbackIsCalled = true; }});
a.valueChanges.subscribe({
next: (value: any) => {
expect(controlCallbackIsCalled).toBe(true);
async.done();
}
});
c1.updateValue('new1');
}));
it('should fire an event when a control is removed',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
a.valueChanges.subscribe({
next: (value: any) => {
expect(value).toEqual(['old1']);
async.done();
}
});
a.removeAt(1);
}));
it('should fire an event when a control is added',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
a.removeAt(1);
a.valueChanges.subscribe({
next: (value: any) => {
expect(value).toEqual(['old1', 'old2']);
async.done();
}
});
a.push(c2);
}));
});
describe('find', () => {
it('should return null when path is null', () => {
var g = new ControlGroup({});
expect(g.find(null)).toEqual(null);
});
it('should return null when path is empty', () => {
var g = new ControlGroup({});
expect(g.find([])).toEqual(null);
});
it('should return null when path is invalid', () => {
var g = new ControlGroup({});
expect(g.find(['one', 'two'])).toEqual(null);
});
it('should return a child of a control group', () => {
var g = new ControlGroup(
{'one': new Control('111'), 'nested': new ControlGroup({'two': new Control('222')})});
expect(g.find(['nested', 'two']).value).toEqual('222');
expect(g.find(['one']).value).toEqual('111');
expect(g.find('nested/two').value).toEqual('222');
expect(g.find('one').value).toEqual('111');
});
it('should return an element of an array', () => {
var g = new ControlGroup({'array': new ControlArray([new Control('111')])});
expect(g.find(['array', 0]).value).toEqual('111');
});
});
describe('asyncValidator', () => {
it('should run the async validator', fakeAsync(() => {
var c = new Control('value');
var g = new ControlArray([c], null, asyncValidator('expected'));
expect(g.pending).toEqual(true);
tick(1);
expect(g.errors).toEqual({'async': true});
expect(g.pending).toEqual(false);
}));
});
});
});
}

View File

@ -0,0 +1,196 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {AbstractControl, Control, ControlArray, ControlGroup, Validators} from '@angular/common/src/forms-deprecated';
import {fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
import {Observable} from 'rxjs/Observable';
import {EventEmitter} from '../../src/facade/async';
import {normalizeAsyncValidator} from '../../src/forms-deprecated/directives/normalize_validator';
export function main() {
function validator(key: string, error: any) {
return function(c: AbstractControl) {
var r = {};
(r as any)[key] = error;
return r;
};
}
class AsyncValidatorDirective {
constructor(private expected: string, private error: any) {}
validate(c: any): {[key: string]: any;} {
return Observable.create((obs: any) => {
const error = this.expected !== c.value ? this.error : null;
obs.next(error);
obs.complete();
});
}
}
describe('Validators', () => {
describe('required', () => {
it('should error on an empty string',
() => { expect(Validators.required(new Control(''))).toEqual({'required': true}); });
it('should error on null',
() => { expect(Validators.required(new Control(null))).toEqual({'required': true}); });
it('should not error on a non-empty string',
() => { expect(Validators.required(new Control('not empty'))).toEqual(null); });
it('should accept zero as valid',
() => { expect(Validators.required(new Control(0))).toEqual(null); });
});
describe('minLength', () => {
it('should not error on an empty string',
() => { expect(Validators.minLength(2)(new Control(''))).toEqual(null); });
it('should not error on null',
() => { expect(Validators.minLength(2)(new Control(null))).toEqual(null); });
it('should not error on valid strings',
() => { expect(Validators.minLength(2)(new Control('aa'))).toEqual(null); });
it('should error on short strings', () => {
expect(Validators.minLength(2)(new Control('a'))).toEqual({
'minlength': {'requiredLength': 2, 'actualLength': 1}
});
});
});
describe('maxLength', () => {
it('should not error on an empty string',
() => { expect(Validators.maxLength(2)(new Control(''))).toEqual(null); });
it('should not error on null',
() => { expect(Validators.maxLength(2)(new Control(null))).toEqual(null); });
it('should not error on valid strings',
() => { expect(Validators.maxLength(2)(new Control('aa'))).toEqual(null); });
it('should error on long strings', () => {
expect(Validators.maxLength(2)(new Control('aaa'))).toEqual({
'maxlength': {'requiredLength': 2, 'actualLength': 3}
});
});
});
describe('pattern', () => {
it('should not error on an empty string',
() => { expect(Validators.pattern('[a-zA-Z ]*')(new Control(''))).toEqual(null); });
it('should not error on null',
() => { expect(Validators.pattern('[a-zA-Z ]*')(new Control(null))).toEqual(null); });
it('should not error on valid strings',
() => { expect(Validators.pattern('[a-zA-Z ]*')(new Control('aaAA'))).toEqual(null); });
it('should error on failure to match string', () => {
expect(Validators.pattern('[a-zA-Z ]*')(new Control('aaa0'))).toEqual({
'pattern': {'requiredPattern': '^[a-zA-Z ]*$', 'actualValue': 'aaa0'}
});
});
});
it('should normalize and evaluate async validator-directives correctly', fakeAsync(() => {
const c = Validators.composeAsync(
[normalizeAsyncValidator(new AsyncValidatorDirective('expected', {'one': true}))]);
let value: any = null;
c(new Control()).then((v: any) => value = v);
tick(1);
expect(value).toEqual({'one': true});
}));
describe('compose', () => {
it('should return null when given null',
() => { expect(Validators.compose(null)).toBe(null); });
it('should collect errors from all the validators', () => {
var c = Validators.compose([validator('a', true), validator('b', true)]);
expect(c(new Control(''))).toEqual({'a': true, 'b': true});
});
it('should run validators left to right', () => {
var c = Validators.compose([validator('a', 1), validator('a', 2)]);
expect(c(new Control(''))).toEqual({'a': 2});
});
it('should return null when no errors', () => {
var c = Validators.compose([Validators.nullValidator, Validators.nullValidator]);
expect(c(new Control(''))).toEqual(null);
});
it('should ignore nulls', () => {
var c = Validators.compose([null, Validators.required]);
expect(c(new Control(''))).toEqual({'required': true});
});
});
describe('composeAsync', () => {
function asyncValidator(expected: any /** TODO #9100 */, response: any /** TODO #9100 */) {
return (c: any /** TODO #9100 */) => {
var emitter = new EventEmitter();
var res = c.value != expected ? response : null;
Promise.resolve(null).then(() => {
emitter.emit(res);
// this is required because of a bug in ObservableWrapper
// where callComplete can fire before callEmit
// remove this one the bug is fixed
setTimeout(() => { emitter.complete(); }, 0);
});
return emitter;
};
}
it('should return null when given null',
() => { expect(Validators.composeAsync(null)).toEqual(null); });
it('should collect errors from all the validators', fakeAsync(() => {
var c = Validators.composeAsync([
asyncValidator('expected', {'one': true}), asyncValidator('expected', {'two': true})
]);
var value: any /** TODO #9100 */ = null;
(<Promise<any>>c(new Control('invalid'))).then(v => value = v);
tick(1);
expect(value).toEqual({'one': true, 'two': true});
}));
it('should return null when no errors', fakeAsync(() => {
var c = Validators.composeAsync([asyncValidator('expected', {'one': true})]);
var value: any /** TODO #9100 */ = null;
(<Promise<any>>c(new Control('expected'))).then(v => value = v);
tick(1);
expect(value).toEqual(null);
}));
it('should ignore nulls', fakeAsync(() => {
var c = Validators.composeAsync([asyncValidator('expected', {'one': true}), null]);
var value: any /** TODO #9100 */ = null;
(<Promise<any>>c(new Control('invalid'))).then(v => value = v);
tick(1);
expect(value).toEqual({'one': true});
}));
});
});
}

View File

@ -0,0 +1,218 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {AsyncPipe} from '@angular/common';
import {WrappedValue} from '@angular/core';
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
import {EventEmitter} from '../../src/facade/async';
import {isBlank} from '../../src/facade/lang';
import {SpyChangeDetectorRef} from '../spies';
export function main() {
describe('AsyncPipe', () => {
describe('Observable', () => {
var emitter: EventEmitter<any>;
var pipe: AsyncPipe;
var ref: any;
var message = {};
beforeEach(() => {
emitter = new EventEmitter();
ref = new SpyChangeDetectorRef();
pipe = new AsyncPipe(ref);
});
describe('transform', () => {
it('should return null when subscribing to an observable',
() => { expect(pipe.transform(emitter)).toBe(null); });
it('should return the latest available value wrapped',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
pipe.transform(emitter);
emitter.emit(message);
setTimeout(() => {
expect(pipe.transform(emitter)).toEqual(new WrappedValue(message));
async.done();
}, 0);
}));
it('should return same value when nothing has changed since the last call',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
pipe.transform(emitter);
emitter.emit(message);
setTimeout(() => {
pipe.transform(emitter);
expect(pipe.transform(emitter)).toBe(message);
async.done();
}, 0);
}));
it('should dispose of the existing subscription when subscribing to a new observable',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
pipe.transform(emitter);
var newEmitter = new EventEmitter();
expect(pipe.transform(newEmitter)).toBe(null);
emitter.emit(message);
// this should not affect the pipe
setTimeout(() => {
expect(pipe.transform(newEmitter)).toBe(null);
async.done();
}, 0);
}));
it('should request a change detection check upon receiving a new value',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
pipe.transform(emitter);
emitter.emit(message);
setTimeout(() => {
expect(ref.spy('markForCheck')).toHaveBeenCalled();
async.done();
}, 10);
}));
});
describe('ngOnDestroy', () => {
it('should do nothing when no subscription',
() => { expect(() => pipe.ngOnDestroy()).not.toThrow(); });
it('should dispose of the existing subscription',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
pipe.transform(emitter);
pipe.ngOnDestroy();
emitter.emit(message);
setTimeout(() => {
expect(pipe.transform(emitter)).toBe(null);
async.done();
}, 0);
}));
});
});
describe('Promise', () => {
var message = new Object();
var pipe: AsyncPipe;
var resolve: (result: any) => void;
var reject: (error: any) => void;
var promise: Promise<any>;
var ref: SpyChangeDetectorRef;
// adds longer timers for passing tests in IE
var timer = (!isBlank(getDOM()) && browserDetection.isIE) ? 50 : 10;
beforeEach(() => {
promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
ref = new SpyChangeDetectorRef();
pipe = new AsyncPipe(<any>ref);
});
describe('transform', () => {
it('should return null when subscribing to a promise',
() => { expect(pipe.transform(promise)).toBe(null); });
it('should return the latest available value',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
pipe.transform(promise);
resolve(message);
setTimeout(() => {
expect(pipe.transform(promise)).toEqual(new WrappedValue(message));
async.done();
}, timer);
}));
it('should return unwrapped value when nothing has changed since the last call',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
pipe.transform(promise);
resolve(message);
setTimeout(() => {
pipe.transform(promise);
expect(pipe.transform(promise)).toBe(message);
async.done();
}, timer);
}));
it('should dispose of the existing subscription when subscribing to a new promise',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
pipe.transform(promise);
var promise = new Promise<any>(() => {});
expect(pipe.transform(promise)).toBe(null);
// this should not affect the pipe, so it should return WrappedValue
resolve(message);
setTimeout(() => {
expect(pipe.transform(promise)).toBe(null);
async.done();
}, timer);
}));
it('should request a change detection check upon receiving a new value',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
var markForCheck = ref.spy('markForCheck');
pipe.transform(promise);
resolve(message);
setTimeout(() => {
expect(markForCheck).toHaveBeenCalled();
async.done();
}, timer);
}));
describe('ngOnDestroy', () => {
it('should do nothing when no source',
() => { expect(() => pipe.ngOnDestroy()).not.toThrow(); });
it('should dispose of the existing source',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
pipe.transform(promise);
expect(pipe.transform(promise)).toBe(null);
resolve(message);
setTimeout(() => {
expect(pipe.transform(promise)).toEqual(new WrappedValue(message));
pipe.ngOnDestroy();
expect(pipe.transform(promise)).toBe(null);
async.done();
}, timer);
}));
});
});
});
describe('null', () => {
it('should return null when given null', () => {
var pipe = new AsyncPipe(null);
expect(pipe.transform(null)).toEqual(null);
});
});
describe('other types', () => {
it('should throw when given an invalid object', () => {
var pipe = new AsyncPipe(null);
expect(() => pipe.transform(<any>'some bogus object')).toThrowError();
});
});
});
}

View File

@ -0,0 +1,100 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {DatePipe} from '@angular/common';
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
import {DateWrapper} from '../../src/facade/lang';
export function main() {
describe('DatePipe', () => {
var date: Date;
var pipe: DatePipe;
beforeEach(() => {
date = DateWrapper.create(2015, 6, 15, 9, 3, 1);
pipe = new DatePipe();
});
it('should be marked as pure',
() => { expect(new PipeResolver().resolve(DatePipe).pure).toEqual(true); });
// TODO(mlaval): enable tests when Intl API is no longer used, see
// https://github.com/angular/angular/issues/3333
// Have to restrict to Chrome as IE uses a different formatting
if (browserDetection.supportsIntlApi && browserDetection.isChromeDesktop) {
describe('supports', () => {
it('should support date', () => { expect(() => pipe.transform(date)).not.toThrow(); });
it('should support int', () => { expect(() => pipe.transform(123456789)).not.toThrow(); });
it('should support numeric strings',
() => { expect(() => pipe.transform('123456789')).not.toThrow(); });
it('should support ISO string',
() => { expect(() => pipe.transform('2015-06-15T21:43:11Z')).not.toThrow(); });
it('should not support other objects', () => {
expect(() => pipe.transform({})).toThrow();
expect(() => pipe.transform('')).toThrow();
});
});
describe('transform', () => {
it('should format each component correctly', () => {
expect(pipe.transform(date, 'y')).toEqual('2015');
expect(pipe.transform(date, 'yy')).toEqual('15');
expect(pipe.transform(date, 'M')).toEqual('6');
expect(pipe.transform(date, 'MM')).toEqual('06');
expect(pipe.transform(date, 'MMM')).toEqual('Jun');
expect(pipe.transform(date, 'MMMM')).toEqual('June');
expect(pipe.transform(date, 'd')).toEqual('15');
expect(pipe.transform(date, 'E')).toEqual('Mon');
expect(pipe.transform(date, 'EEEE')).toEqual('Monday');
expect(pipe.transform(date, 'h')).toEqual('9');
expect(pipe.transform(date, 'hh')).toEqual('09');
expect(pipe.transform(date, 'HH')).toEqual('09');
expect(pipe.transform(date, 'j')).toEqual('9 AM');
expect(pipe.transform(date, 'm')).toEqual('3');
expect(pipe.transform(date, 's')).toEqual('1');
expect(pipe.transform(date, 'mm')).toEqual('03');
expect(pipe.transform(date, 'ss')).toEqual('01');
expect(pipe.transform(date, 'Z')).toBeDefined();
});
it('should format common multi component patterns', () => {
expect(pipe.transform(date, 'E, M/d/y')).toEqual('Mon, 6/15/2015');
expect(pipe.transform(date, 'E, M/d')).toEqual('Mon, 6/15');
expect(pipe.transform(date, 'MMM d')).toEqual('Jun 15');
expect(pipe.transform(date, 'dd/MM/yyyy')).toEqual('15/06/2015');
expect(pipe.transform(date, 'MM/dd/yyyy')).toEqual('06/15/2015');
expect(pipe.transform(date, 'yMEd')).toEqual('20156Mon15');
expect(pipe.transform(date, 'MEd')).toEqual('6Mon15');
expect(pipe.transform(date, 'MMMd')).toEqual('Jun15');
expect(pipe.transform(date, 'yMMMMEEEEd')).toEqual('Monday, June 15, 2015');
expect(pipe.transform(date, 'ms')).toEqual('31');
expect(pipe.transform(date, 'jm')).toEqual('9:03 AM');
});
it('should format with pattern aliases', () => {
expect(pipe.transform(date, 'medium')).toEqual('Jun 15, 2015, 9:03:01 AM');
expect(pipe.transform(date, 'short')).toEqual('6/15/2015, 9:03 AM');
expect(pipe.transform(date, 'dd/MM/yyyy')).toEqual('15/06/2015');
expect(pipe.transform(date, 'MM/dd/yyyy')).toEqual('06/15/2015');
expect(pipe.transform(date, 'fullDate')).toEqual('Monday, June 15, 2015');
expect(pipe.transform(date, 'longDate')).toEqual('June 15, 2015');
expect(pipe.transform(date, 'mediumDate')).toEqual('Jun 15, 2015');
expect(pipe.transform(date, 'shortDate')).toEqual('6/15/2015');
expect(pipe.transform(date, 'mediumTime')).toEqual('9:03:01 AM');
expect(pipe.transform(date, 'shortTime')).toEqual('9:03 AM');
});
});
}
});
}

View File

@ -0,0 +1,68 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {I18nPluralPipe, NgLocalization} from '@angular/common';
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
export function main() {
describe('I18nPluralPipe', () => {
var localization: NgLocalization;
var pipe: I18nPluralPipe;
var mapping = {
'=0': 'No messages.',
'=1': 'One message.',
'many': 'Many messages.',
'other': 'There are # messages, that is #.',
};
beforeEach(() => {
localization = new TestLocalization();
pipe = new I18nPluralPipe(localization);
});
it('should be marked as pure',
() => { expect(new PipeResolver().resolve(I18nPluralPipe).pure).toEqual(true); });
describe('transform', () => {
it('should return 0 text if value is 0', () => {
var val = pipe.transform(0, mapping);
expect(val).toEqual('No messages.');
});
it('should return 1 text if value is 1', () => {
var val = pipe.transform(1, mapping);
expect(val).toEqual('One message.');
});
it('should return category messages', () => {
var val = pipe.transform(4, mapping);
expect(val).toEqual('Many messages.');
});
it('should interpolate the value into the text where indicated', () => {
var val = pipe.transform(6, mapping);
expect(val).toEqual('There are 6 messages, that is 6.');
});
it('should use "" if value is undefined', () => {
var val = pipe.transform(void(0), mapping);
expect(val).toEqual('');
});
it('should not support bad arguments',
() => { expect(() => pipe.transform(0, <any>'hey')).toThrowError(); });
});
});
}
class TestLocalization extends NgLocalization {
getPluralCategory(value: number): string { return value > 1 && value < 6 ? 'many' : 'other'; }
}

View File

@ -0,0 +1,49 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {I18nSelectPipe} from '@angular/common';
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
export function main() {
describe('I18nSelectPipe', () => {
var pipe: I18nSelectPipe;
var mapping = {'male': 'Invite him.', 'female': 'Invite her.', 'other': 'Invite them.'};
beforeEach(() => { pipe = new I18nSelectPipe(); });
it('should be marked as pure',
() => { expect(new PipeResolver().resolve(I18nSelectPipe).pure).toEqual(true); });
describe('transform', () => {
it('should return male text if value is male', () => {
var val = pipe.transform('male', mapping);
expect(val).toEqual('Invite him.');
});
it('should return female text if value is female', () => {
var val = pipe.transform('female', mapping);
expect(val).toEqual('Invite her.');
});
it('should return "" if value is anything other than male or female', () => {
var val = pipe.transform('Anything else', mapping);
expect(val).toEqual('');
});
it('should use "" if value is undefined', () => {
var val = pipe.transform(void(0), mapping);
expect(val).toEqual('');
});
it('should not support bad arguments',
() => { expect(() => pipe.transform('male', <any>'hey')).toThrowError(); });
});
});
}

View File

@ -0,0 +1,82 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {JsonPipe} from '@angular/common';
import {Component} from '@angular/core';
import {TestComponentBuilder} from '@angular/core/testing';
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {expect} from '@angular/platform-browser/testing/matchers';
import {Json, StringWrapper} from '../../src/facade/lang';
export function main() {
describe('JsonPipe', () => {
var regNewLine = '\n';
var inceptionObj: any;
var inceptionObjString: string;
var pipe: JsonPipe;
function normalize(obj: string): string { return StringWrapper.replace(obj, regNewLine, ''); }
beforeEach(() => {
inceptionObj = {dream: {dream: {dream: 'Limbo'}}};
inceptionObjString = '{\n' +
' "dream": {\n' +
' "dream": {\n' +
' "dream": "Limbo"\n' +
' }\n' +
' }\n' +
'}';
pipe = new JsonPipe();
});
describe('transform', () => {
it('should return JSON-formatted string',
() => { expect(pipe.transform(inceptionObj)).toEqual(inceptionObjString); });
it('should return JSON-formatted string even when normalized', () => {
var dream1 = normalize(pipe.transform(inceptionObj));
var dream2 = normalize(inceptionObjString);
expect(dream1).toEqual(dream2);
});
it('should return JSON-formatted string similar to Json.stringify', () => {
var dream1 = normalize(pipe.transform(inceptionObj));
var dream2 = normalize(Json.stringify(inceptionObj));
expect(dream1).toEqual(dream2);
});
});
describe('integration', () => {
it('should work with mutable objects',
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
tcb.createAsync(TestComp).then((fixture) => {
let mutable: number[] = [1];
fixture.debugElement.componentInstance.data = mutable;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('[\n 1\n]');
mutable.push(2);
fixture.detectChanges();
expect(fixture.debugElement.nativeElement).toHaveText('[\n 1,\n 2\n]');
async.done();
});
}));
});
});
}
@Component({selector: 'test-comp', template: '{{data | json}}', pipes: [JsonPipe]})
class TestComp {
data: any;
}

View File

@ -0,0 +1,42 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {LowerCasePipe} from '@angular/common';
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
export function main() {
describe('LowerCasePipe', () => {
var upper: string;
var lower: string;
var pipe: LowerCasePipe;
beforeEach(() => {
lower = 'something';
upper = 'SOMETHING';
pipe = new LowerCasePipe();
});
describe('transform', () => {
it('should return lowercase', () => {
var val = pipe.transform(upper);
expect(val).toEqual(lower);
});
it('should lowercase when there is a new value', () => {
var val = pipe.transform(upper);
expect(val).toEqual(lower);
var val2 = pipe.transform('WAT');
expect(val2).toEqual('wat');
});
it('should not support other objects',
() => { expect(() => pipe.transform(<any>{})).toThrowError(); });
});
});
}

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