Compare commits

..

2 Commits

Author SHA1 Message Date
4f1f29d7d2 docs(changelog): changelog for beta.3 2016-02-03 10:11:37 -08:00
b1b73dc39e chore(release): bump version to beta.3 2016-02-03 09:50:21 -08:00
320 changed files with 2750 additions and 4739 deletions

1
.gitignore vendored
View File

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

View File

@ -9,19 +9,13 @@ branches:
cache:
directories:
- node_modules
- $HOME/.pub-cache
- $HOME/.chrome/chromium
before_cache:
# Undo the pollution of the typescript_next build
- npm install typescript
env:
global:
- 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
- KARMA_BROWSERS=DartiumWithWebPlatform
- E2E_BROWSERS=Dartium
- LOGS_DIR=/tmp/angular-build/logs
- SAUCE_USERNAME=angular-ci
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
@ -42,7 +36,8 @@ env:
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
# Disable dart dev build, which is timing out after 2h. #6823
# - MODE=dart DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
- MODE=saucelabs_required DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
- MODE=browserstack_required DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
- MODE=saucelabs_optional DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
@ -51,7 +46,6 @@ env:
- 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=typescript_next 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
@ -59,8 +53,8 @@ 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"
# Tracked in https://github.com/angular/angular/issues/7050
- env: "MODE=typescript_next DART_CHANNEL=stable DART_VERSION=$DART_STABLE_VERSION"
# TODO(alxhub): remove when dartdoc #1039 is in dev channel
- env: "MODE=dart DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
addons:
firefox: "38.0"
@ -70,8 +64,6 @@ before_install:
- node tools/analytics/build-analytics start ci job
- node tools/analytics/build-analytics start ci before_install
- echo ${TSDRC} > .tsdrc
- ./scripts/ci/install_chromium.sh
- 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
@ -116,7 +108,6 @@ notifications:
- https://webhooks.gitter.im/e/1ef62e23078036f9cee4
# trigger Buildtime Trend Service to parse Travis CI log
- https://buildtimetrend.herokuapp.com/travis
- http://104.197.9.155:8484/hubot/travis/activity
on_success: always # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: false # default: false

View File

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

View File

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

View File

@ -981,16 +981,15 @@ gulp.task('!pre.test.typings.layoutNodeModule', ['build.js.cjs'], function() {
.pipe(gulp.dest(path.join(tmpdir, 'node_modules')));
});
gulp.task('!pre.test.typings.copyTypingsSpec', function() {
return gulp.src(['typing_spec/*.ts'], {base: 'typing_spec'}).pipe(gulp.dest(tmpdir));
return gulp.src(['typing_spec/*.ts'], {base: 'typing_spec'}).pipe(gulp.dest(path.join(tmpdir)));
});
gulp.task('test.typings',
['!pre.test.typings.layoutNodeModule', '!pre.test.typings.copyTypingsSpec'], function() {
var tsc = require('gulp-typescript');
return gulp.src([tmpdir + '/*.ts'])
return gulp.src([tmpdir + '/**'])
.pipe(tsc({
target: 'ES6',
target: 'ES5',
module: 'commonjs',
experimentalDecorators: true,
noImplicitAny: true,

View File

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

View File

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

View File

@ -4,7 +4,7 @@ angular.module('ngComponentRouter').
// Because Angular 1 has no notion of a root component, we use an object with unique identity
// to represent this. Can be overloaded with a component name
value('$routerRootComponent', new Object()).
factory('$rootRouter', ['$q', '$location', '$$directiveIntrospector', '$browser', '$rootScope', '$injector', '$routerRootComponent', routerFactory]);
factory('$router', ['$q', '$location', '$$directiveIntrospector', '$browser', '$rootScope', '$injector', '$routerRootComponent', routerFactory]);
function routerFactory($q, $location, $$directiveIntrospector, $browser, $rootScope, $injector, $routerRootComponent) {
@ -57,7 +57,7 @@ function routerFactory($q, $location, $$directiveIntrospector, $browser, $rootSc
});
var router = new RootRouter(registry, location, $routerRootComponent);
$rootScope.$watch(function () { return $location.url(); }, function (path) {
$rootScope.$watch(function () { return $location.path(); }, function (path) {
if (router.lastNavigationAttempt !== path) {
router.navigateByUrl(path);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -18,12 +18,12 @@ dependencies:
logging: '>=0.9.0 <0.12.0'
observe: '^0.13.1'
protobuf: '^0.5.0'
quiver: '^0.21.4'
source_span: '^1.0.0'
stack_trace: '^1.1.1'
dev_dependencies:
code_transformers: '>=0.2.9+4 <0.4.0'
guinness: '^0.1.18'
quiver: '^0.21.4'
test: '^0.12.6'
transformers:
- angular2

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -16,7 +16,7 @@ import {HtmlAst, HtmlAttrAst, HtmlTextAst, HtmlElementAst} from './html_ast';
import {Injectable} from 'angular2/src/core/di';
import {HtmlToken, HtmlTokenType, tokenizeHtml} from './html_lexer';
import {ParseError, ParseLocation, ParseSourceSpan} from './parse_util';
import {HtmlTagDefinition, getHtmlTagDefinition, getNsPrefix, mergeNsAndName} from './html_tags';
import {HtmlTagDefinition, getHtmlTagDefinition, getNsPrefix} from './html_tags';
export class HtmlTreeError extends ParseError {
static create(elementName: string, location: ParseLocation, msg: string): HtmlTreeError {
@ -238,6 +238,10 @@ class TreeBuilder {
}
}
function mergeNsAndName(prefix: string, localName: string): string {
return isPresent(prefix) ? `@${prefix}:${localName}` : localName;
}
function getElementFullName(prefix: string, localName: string,
parentElement: HtmlElementAst): string {
if (isBlank(prefix)) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,10 +7,11 @@ import {Parser, AST, ASTWithSource} from 'angular2/src/core/change_detection/cha
import {TemplateBinding} from 'angular2/src/core/change_detection/parser/ast';
import {CompileDirectiveMetadata, CompilePipeMetadata} from './directive_metadata';
import {HtmlParser} from './html_parser';
import {splitNsName, mergeNsAndName} from './html_tags';
import {splitNsName} from './html_tags';
import {ParseSourceSpan, ParseError, ParseLocation} from './parse_util';
import {RecursiveAstVisitor, BindingPipe} from 'angular2/src/core/change_detection/parser/ast';
import {
ElementAst,
BoundElementPropertyAst,
@ -432,9 +433,8 @@ class TemplateParseVisitor implements HtmlAstVisitor {
var parts = splitAtColon(name, [null, name]);
var target = parts[0];
var eventName = parts[1];
var ast = this._parseAction(expression, sourceSpan);
targetMatchableAttrs.push([name, ast.source]);
targetEvents.push(new BoundEventAst(eventName, target, ast, sourceSpan));
targetEvents.push(new BoundEventAst(eventName, target,
this._parseAction(expression, sourceSpan), sourceSpan));
// Don't detect directives for event names for now,
// so don't add the event name to the matchableAttrs
}
@ -583,12 +583,6 @@ class TemplateParseVisitor implements HtmlAstVisitor {
} else {
if (parts[0] == ATTRIBUTE_PREFIX) {
boundPropertyName = parts[1];
let nsSeparatorIdx = boundPropertyName.indexOf(':');
if (nsSeparatorIdx > -1) {
let ns = boundPropertyName.substring(0, nsSeparatorIdx);
let name = boundPropertyName.substring(nsSeparatorIdx + 1);
boundPropertyName = mergeNsAndName(ns, name);
}
bindingType = PropertyBindingType.Attribute;
} else if (parts[0] == CLASS_PREFIX) {
boundPropertyName = parts[1];

View File

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

View File

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

View File

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

View File

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

View File

@ -57,8 +57,9 @@ export class ChangeDetectorJITGenerator {
this.directiveRecords = definition.directiveRecords;
this._names = new CodegenNameUtil(this.records, this.eventBindings, this.directiveRecords,
this.changeDetectionUtilVarName);
this._logic = new CodegenLogicUtil(this._names, this.changeDetectionUtilVarName,
this.changeDetectorStateVarName);
this._logic =
new CodegenLogicUtil(this._names, this.changeDetectionUtilVarName,
this.changeDetectorStateVarName, this.changeDetectionStrategy);
this.typeName = sanitizeName(`ChangeDetector_${this.id}`);
}
@ -173,7 +174,7 @@ export class ChangeDetectorJITGenerator {
var evalRecord = this._logic.genEventBindingEvalValue(eb, r);
var markPath = this._genMarkPathToRootAsCheckOnce(r);
var prevDefault = this._genUpdatePreventDefault(eb, r);
return `${markPath}\n${evalRecord}\n${prevDefault}`;
return `${evalRecord}\n${markPath}\n${prevDefault}`;
} else {
return this._logic.genEventBindingEvalValue(eb, r);
}

View File

@ -4,6 +4,7 @@ import {codify, combineGeneratedStrings, rawString} from './codegen_facade';
import {ProtoRecord, RecordType} from './proto_record';
import {BindingTarget} from './binding_record';
import {DirectiveRecord} from './directive_record';
import {ChangeDetectionStrategy} from './constants';
import {BaseException} from 'angular2/src/facade/exceptions';
/**
@ -11,7 +12,8 @@ import {BaseException} from 'angular2/src/facade/exceptions';
*/
export class CodegenLogicUtil {
constructor(private _names: CodegenNameUtil, private _utilName: string,
private _changeDetectorStateName: string) {}
private _changeDetectorStateName: string,
private _changeDetection: ChangeDetectionStrategy) {}
/**
* Generates a statement which updates the local variable representing `protoRec` with the current
@ -49,12 +51,13 @@ export class CodegenLogicUtil {
break;
case RecordType.PropertyRead:
rhs = `${context}.${protoRec.name}`;
rhs = this._observe(`${context}.${protoRec.name}`, protoRec);
break;
case RecordType.SafeProperty:
var read = `${context}.${protoRec.name}`;
rhs = `${this._utilName}.isValueBlank(${context}) ? null : ${read}`;
var read = this._observe(`${context}.${protoRec.name}`, protoRec);
rhs =
`${this._utilName}.isValueBlank(${context}) ? null : ${this._observe(read, protoRec)}`;
break;
case RecordType.PropertyWrite:
@ -62,16 +65,17 @@ export class CodegenLogicUtil {
break;
case RecordType.Local:
rhs = `${localsAccessor}.get(${rawString(protoRec.name)})`;
rhs = this._observe(`${localsAccessor}.get(${rawString(protoRec.name)})`, protoRec);
break;
case RecordType.InvokeMethod:
rhs = `${context}.${protoRec.name}(${argString})`;
rhs = this._observe(`${context}.${protoRec.name}(${argString})`, protoRec);
break;
case RecordType.SafeMethodInvoke:
var invoke = `${context}.${protoRec.name}(${argString})`;
rhs = `${this._utilName}.isValueBlank(${context}) ? null : ${invoke}`;
rhs =
`${this._utilName}.isValueBlank(${context}) ? null : ${this._observe(invoke, protoRec)}`;
break;
case RecordType.InvokeClosure:
@ -91,7 +95,7 @@ export class CodegenLogicUtil {
break;
case RecordType.KeyedRead:
rhs = `${context}[${getLocalName(protoRec.args[0])}]`;
rhs = this._observe(`${context}[${getLocalName(protoRec.args[0])}]`, protoRec);
break;
case RecordType.KeyedWrite:
@ -108,6 +112,16 @@ export class CodegenLogicUtil {
return `${getLocalName(protoRec.selfIndex)} = ${rhs};`;
}
/** @internal */
_observe(exp: string, rec: ProtoRecord): string {
// This is an experimental feature. Works only in Dart.
if (this._changeDetection === ChangeDetectionStrategy.OnPushObserve) {
return `this.observeValue(${exp}, ${rec.selfIndex})`;
} else {
return exp;
}
}
genPropertyBindingTargets(propertyBindingTargets: BindingTarget[],
genDebugInfo: boolean): string {
var bs = propertyBindingTargets.map(b => {
@ -188,7 +202,15 @@ export class CodegenLogicUtil {
}
}
private _genReadDirective(index: number) { return `this.getDirectiveFor(directives, ${index})`; }
private _genReadDirective(index: number) {
var directiveExpr = `this.getDirectiveFor(directives, ${index})`;
// This is an experimental feature. Works only in Dart.
if (this._changeDetection === ChangeDetectionStrategy.OnPushObserve) {
return `this.observeDirective(${directiveExpr}, ${index})`;
} else {
return directiveExpr;
}
}
genHydrateDetectors(directiveRecords: DirectiveRecord[]): string {
var res = [];

View File

@ -62,6 +62,11 @@ export enum ChangeDetectionStrategy {
* `Default` means that the change detector's mode will be set to `CheckAlways` during hydration.
*/
Default,
/**
* This is an experimental feature. Works only in Dart.
*/
OnPushObserve
}
/**
@ -73,7 +78,8 @@ export var CHANGE_DETECTION_STRATEGY_VALUES = [
ChangeDetectionStrategy.CheckAlways,
ChangeDetectionStrategy.Detached,
ChangeDetectionStrategy.OnPush,
ChangeDetectionStrategy.Default
ChangeDetectionStrategy.Default,
ChangeDetectionStrategy.OnPushObserve
];
/**

View File

@ -40,9 +40,6 @@ export class DefaultIterableDiffer implements IterableDiffer {
private _movesTail: CollectionChangeRecord = null;
private _removalsHead: CollectionChangeRecord = null;
private _removalsTail: CollectionChangeRecord = null;
// Keeps track of records where custom track by is the same, but item identity has changed
private _identityChangesHead: CollectionChangeRecord = null;
private _identityChangesTail: CollectionChangeRecord = null;
constructor(private _trackByFn?: TrackByFn) {
this._trackByFn = isPresent(this._trackByFn) ? this._trackByFn : trackByIdentity;
@ -87,13 +84,6 @@ export class DefaultIterableDiffer implements IterableDiffer {
}
}
forEachIdentityChange(fn: Function) {
var record: CollectionChangeRecord;
for (record = this._identityChangesHead; record !== null; record = record._nextIdentityChange) {
fn(record);
}
}
diff(collection: any): DefaultIterableDiffer {
if (isBlank(collection)) collection = [];
if (!isListLikeIterable(collection)) {
@ -133,7 +123,7 @@ export class DefaultIterableDiffer implements IterableDiffer {
// TODO(misko): can we limit this to duplicates only?
record = this._verifyReinsertion(record, item, itemTrackBy, index);
}
if (!looseIdentical(record.item, item)) this._addIdentityChange(record, item);
record.item = item;
}
record = record._next;
@ -145,12 +135,9 @@ export class DefaultIterableDiffer implements IterableDiffer {
if (record === null || !looseIdentical(record.trackById, itemTrackBy)) {
record = this._mismatch(record, item, itemTrackBy, index);
mayBeDirty = true;
} else {
if (mayBeDirty) {
// TODO(misko): can we limit this to duplicates only?
record = this._verifyReinsertion(record, item, itemTrackBy, index);
}
if (!looseIdentical(record.item, item)) this._addIdentityChange(record, item);
} else if (mayBeDirty) {
// TODO(misko): can we limit this to duplicates only?
record = this._verifyReinsertion(record, item, itemTrackBy, index);
}
record = record._next;
index++;
@ -163,12 +150,9 @@ export class DefaultIterableDiffer implements IterableDiffer {
return this.isDirty;
}
/* CollectionChanges is considered dirty if it has any additions, moves, removals, or identity
* changes.
*/
// CollectionChanges is considered dirty if it has any additions, moves or removals.
get isDirty(): boolean {
return this._additionsHead !== null || this._movesHead !== null ||
this._removalsHead !== null || this._identityChangesHead !== null;
return this._additionsHead !== null || this._movesHead !== null || this._removalsHead !== null;
}
/**
@ -199,7 +183,6 @@ export class DefaultIterableDiffer implements IterableDiffer {
}
this._movesHead = this._movesTail = null;
this._removalsHead = this._removalsTail = null;
this._identityChangesHead = this._identityChangesTail = null;
// todo(vicb) when assert gets supported
// assert(!this.isDirty);
@ -233,18 +216,12 @@ export class DefaultIterableDiffer implements IterableDiffer {
record = this._linkedRecords === null ? null : this._linkedRecords.get(itemTrackBy, index);
if (record !== null) {
// We have seen this before, we need to move it forward in the collection.
// But first we need to check if identity changed, so we can update in view if necessary
if (!looseIdentical(record.item, item)) this._addIdentityChange(record, item);
this._moveAfter(record, previousRecord, index);
} else {
// Never seen it, check evicted list.
record = this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy);
if (record !== null) {
// It is an item which we have evicted earlier: reinsert it back into the list.
// But first we need to check if identity changed, so we can update in view if necessary
if (!looseIdentical(record.item, item)) this._addIdentityChange(record, item);
this._reinsertAfter(record, previousRecord, index);
} else {
// It is a new item: add it.
@ -292,6 +269,7 @@ export class DefaultIterableDiffer implements IterableDiffer {
record.currentIndex = index;
this._addToMoves(record, index);
}
record.item = item;
return record;
}
@ -325,9 +303,6 @@ export class DefaultIterableDiffer implements IterableDiffer {
if (this._removalsTail !== null) {
this._removalsTail._nextRemoved = null;
}
if (this._identityChangesTail !== null) {
this._identityChangesTail._nextIdentityChange = null;
}
}
/** @internal */
@ -494,18 +469,6 @@ export class DefaultIterableDiffer implements IterableDiffer {
return record;
}
/** @internal */
_addIdentityChange(record: CollectionChangeRecord, item: any) {
record.item = item;
if (this._identityChangesTail === null) {
this._identityChangesTail = this._identityChangesHead = record;
} else {
this._identityChangesTail = this._identityChangesTail._nextIdentityChange = record;
}
return record;
}
toString(): string {
var list = [];
this.forEachItem((record) => list.push(record));
@ -522,13 +485,9 @@ export class DefaultIterableDiffer implements IterableDiffer {
var removals = [];
this.forEachRemovedItem((record) => removals.push(record));
var identityChanges = [];
this.forEachIdentityChange((record) => identityChanges.push(record));
return "collection: " + list.join(', ') + "\n" + "previous: " + previous.join(', ') + "\n" +
"additions: " + additions.join(', ') + "\n" + "moves: " + moves.join(', ') + "\n" +
"removals: " + removals.join(', ') + "\n" + "identityChanges: " +
identityChanges.join(', ') + "\n";
"removals: " + removals.join(', ') + "\n";
}
}
@ -554,9 +513,6 @@ export class CollectionChangeRecord {
_nextAdded: CollectionChangeRecord = null;
/** @internal */
_nextMoved: CollectionChangeRecord = null;
/** @internal */
_nextIdentityChange: CollectionChangeRecord = null;
constructor(public item: any, public trackById: any) {}

View File

@ -60,11 +60,9 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
if (proto.isSkipRecord()) {
protoIdx += this._computeSkipLength(protoIdx, proto, values);
} else {
if (proto.lastInBinding) {
this._markPathAsCheckOnce(proto);
}
var res = this._calculateCurrValue(proto, values, locals);
if (proto.lastInBinding) {
this._markPathAsCheckOnce(proto);
return res;
} else {
this._writeSelf(proto, res, values);
@ -110,6 +108,12 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
this.values[0] = this.context;
this.dispatcher = dispatcher;
if (this.strategy === ChangeDetectionStrategy.OnPushObserve) {
for (var i = 0; i < this.directiveIndices.length; ++i) {
var index = this.directiveIndices[i];
super.observeDirective(this._getDirectiveFor(index), i);
}
}
this.outputSubscriptions = [];
for (var i = 0; i < this._directiveRecords.length; ++i) {
var r = this._directiveRecords[i];
@ -294,6 +298,9 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
}
var currValue = this._calculateCurrValue(proto, values, locals);
if (this.strategy === ChangeDetectionStrategy.OnPushObserve) {
super.observeValue(currValue, proto.selfIndex);
}
if (proto.shouldBeChecked()) {
var prevValue = this._readSelf(proto, values);

View File

@ -91,7 +91,7 @@ export class ChangeDetectionError extends WrappedException {
* This is an internal Angular error.
*/
export class DehydratedException extends BaseException {
constructor(details: string) { super(`Attempt to use a dehydrated detector: ${details}`); }
constructor() { super('Attempt to use a dehydrated detector.'); }
}
/**

View File

@ -0,0 +1,5 @@
library change_detection.observable_facade;
import 'package:observe/observe.dart';
bool isObservable(value) => value is Observable;

View File

@ -0,0 +1,3 @@
export function isObservable(value: any): boolean {
return false;
}

View File

@ -248,12 +248,15 @@ class _Scanner {
}
scanCharacter(start: number, code: number): Token {
assert(this.peek == code);
this.advance();
return newCharacterToken(start, code);
}
scanOperator(start: number, str: string): Token {
assert(this.peek == StringWrapper.charCodeAt(str, 0));
assert(SetWrapper.has(OPERATORS, str));
this.advance();
return newOperatorToken(start, str);
}
@ -271,6 +274,7 @@ class _Scanner {
*/
scanComplexOperator(start: number, one: string, twoCode: number, two: string, threeCode?: number,
three?: string): Token {
assert(this.peek == StringWrapper.charCodeAt(one, 0));
this.advance();
var str: string = one;
if (this.peek == twoCode) {
@ -281,10 +285,12 @@ class _Scanner {
this.advance();
str += three;
}
assert(SetWrapper.has(OPERATORS, str));
return newOperatorToken(start, str);
}
scanIdentifier(): Token {
assert(isIdentifierStart(this.peek));
var start: number = this.index;
this.advance();
while (isIdentifierPart(this.peek)) this.advance();
@ -297,6 +303,7 @@ class _Scanner {
}
scanNumber(start: number): Token {
assert(isDigit(this.peek));
var simple: boolean = (this.index === start);
this.advance(); // Skip initial digit.
while (true) {
@ -322,6 +329,7 @@ class _Scanner {
}
scanString(): Token {
assert(this.peek == $SQ || this.peek == $DQ);
var start: number = this.index;
var quote: number = this.peek;
this.advance(); // Skip initial quote.

View File

@ -49,7 +49,7 @@ import {
var _implicitReceiver = new ImplicitReceiver();
// TODO(tbosch): Cannot make this const/final right now because of the transpiler...
var INTERPOLATION_REGEXP = /\{\{([\s\S]*?)\}\}/g;
var INTERPOLATION_REGEXP = /\{\{(.*?)\}\}/g;
class ParseException extends BaseException {
constructor(message: string, input: string, errLocation: string, ctxLocation?: any) {

View File

@ -31,7 +31,7 @@ export {
ResolvedProvider,
provide
} from './di/provider';
export {Key} from './di/key';
export {Key, TypeLiteral} from './di/key';
export {
NoProviderError,
AbstractProviderError,

View File

@ -17,7 +17,6 @@ import {
OutOfBoundsError
} from './exceptions';
import {FunctionWrapper, Type, isPresent, isBlank, CONST_EXPR} from 'angular2/src/facade/lang';
import {BaseException} from 'angular2/src/facade/exceptions';
import {Key} from './key';
import {SelfMetadata, HostMetadata, SkipSelfMetadata} from './metadata';
@ -777,26 +776,7 @@ export class Injector {
var deps = resolvedFactory.dependencies;
var length = deps.length;
var d0: any;
var d1: any;
var d2: any;
var d3: any;
var d4: any;
var d5: any;
var d6: any;
var d7: any;
var d8: any;
var d9: any;
var d10: any;
var d11: any;
var d12: any;
var d13: any;
var d14: any;
var d15: any;
var d16: any;
var d17: any;
var d18: any;
var d19: any;
var d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, d17, d18, d19;
try {
d0 = length > 0 ? this._getByDependency(provider, deps[0], visibility) : null;
d1 = length > 1 ? this._getByDependency(provider, deps[1], visibility) : null;
@ -894,9 +874,6 @@ export class Injector {
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16,
d17, d18, d19);
break;
default:
throw new BaseException(
`Cannot instantiate '${provider.key.displayName}' because it has more than 20 dependencies`);
}
} catch (e) {
throw new InstantiationError(this, e, e.stack, provider.key);
@ -1004,7 +981,7 @@ export class Injector {
}
get displayName(): string {
return `Injector(providers: [${_mapProviders(this, (b: ResolvedProvider) => ` "${b.key.displayName}" `).join(", ")}])`;
return `Injector(providers: [${_mapProviders(this, b => ` "${b.key.displayName}" `).join(", ")}])`;
}
toString(): string { return this.displayName; }

View File

@ -1,7 +1,10 @@
import {stringify, CONST, Type, isBlank} from 'angular2/src/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
import {TypeLiteral} from './type_literal';
import {resolveForwardRef} from './forward_ref';
export {TypeLiteral} from './type_literal';
/**
* A unique object used for retrieving items from the {@link Injector}.
*
@ -50,6 +53,13 @@ export class KeyRegistry {
get(token: Object): Key {
if (token instanceof Key) return token;
// TODO: workaround for https://github.com/Microsoft/TypeScript/issues/3123
var theToken = token;
if (token instanceof TypeLiteral) {
theToken = token.type;
}
token = theToken;
if (this._allKeys.has(token)) {
return this._allKeys.get(token);
}

View File

@ -9,7 +9,7 @@ import {CONST} from 'angular2/src/facade/lang';
* var t = new OpaqueToken("value");
*
* var injector = Injector.resolveAndCreate([
* provide(t, {useValue: "bindingValue"})
* provide(t, {useValue: "providedValue"})
* ]);
*
* expect(injector.get(t)).toEqual("bindingValue");

View File

@ -0,0 +1,26 @@
library angular2.di.type_literal;
/**
* Use type literals as DI keys corresponding to generic types.
*
* Example:
*
* ```
* Injector.resolveAndCreate([
* bind(new TypeLiteral<List<int>>()).toValue([1, 2, 3])
* ]);
*
* class Foo {
* // Delend on `List<int>` normally.
* Foo(List<int> list) { ... }
* }
* ```
*
* This capability might be added to the language one day. See:
*
* https://code.google.com/p/dart/issues/detail?id=11923
*/
class TypeLiteral<T> {
const TypeLiteral();
Type get type => T;
}

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