refactor: move angular source to /packages rather than modules/@angular

This commit is contained in:
Jason Aden
2017-03-02 10:48:42 -08:00
parent 5ad5301a3e
commit 3e51a19983
1051 changed files with 18 additions and 18 deletions

View File

@ -0,0 +1,34 @@
# API Examples
This folder contains small example apps that get in-lined into our API docs.
Each example contains tests for application behavior (as opposed to testing Angular's
behavior) just like an Angular application developer would write.
# Running the examples
```
# # execute the following command only when framework code changes
./build.sh
# run when test change
./modules/@angular/examples/build.sh
# start server
$(npm bin)/gulp serve-examples
```
navigate to [http://localhost:8001](http://localhost:8001)
# Running the tests
```
# run only when framework code changes
./build.sh
# run to compile tests and run them
./modules/@angular/examples/test.sh
```
NOTE: sometimes the http server does not exit properly and it retains the `8001` port.
in such a case you can use `lsof -i:8001` to see which process it is and then use `kill`
to remove it. (Or in single command: `lsof -i:8001 -t | xargs kill`)

View File

@ -0,0 +1,20 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
(function(global: any) {
writeScriptTag('/vendor/zone.js');
writeScriptTag('/vendor/system.js');
writeScriptTag('/vendor/Reflect.js');
writeScriptTag('/_common/system-config.js');
if (location.pathname.indexOf('/upgrade/') != -1) {
writeScriptTag('/vendor/angular.js');
}
function writeScriptTag(scriptUrl: string, onload: string = '') {
document.write('<script src="' + scriptUrl + '" onload="' + onload + '"></script>');
}
}(window));

View File

@ -0,0 +1,26 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/* tslint:disable:no-console */
import * as webdriver from 'selenium-webdriver';
declare var browser: any;
declare var expect: any;
// TODO (juliemr): remove this method once this becomes a protractor plugin
export function verifyNoBrowserErrors() {
browser.manage().logs().get('browser').then(function(browserLog: any[]) {
const errors: any[] = [];
browserLog.filter(logEntry => {
const msg = logEntry.message;
console.log('>> ' + msg);
if (logEntry.level.value >= webdriver.logging.Level.INFO.value) {
errors.push(msg);
}
});
expect(errors).toEqual([]);
});
}

View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon" />
<script type="text/javascript" src="bootstrap.js"></script>
<script type="text/javascript">
System.import('main-dynamic').catch(console.error.bind(console));
</script>
</head>
<body>
<example-app>
loading...
</example-app>
</body>
</html>

View File

@ -0,0 +1,13 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import * as mod from './module';
if (mod.AppModule) {
platformBrowserDynamic().bootstrapModule(mod.AppModule);
}

19
packages/examples/_common/module.d.ts vendored Normal file
View File

@ -0,0 +1,19 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/*
DO NOT DELETE THIS FILE
=======================
The purpose of this file is to allow `main-dynamic.ts` to be tsc-compiled
BEFORE it is copied over to each of the associated example directories
within `dist/examples`.
*/
export class AppModule {};

View File

@ -0,0 +1,32 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
System.config({
defaultJSExtensions: true,
map: {
'@angular/common': '/vendor/@angular/common/bundles/common.umd.js',
'@angular/compiler': '/vendor/@angular/compiler/bundles/compiler.umd.js',
'@angular/animations': '/vendor/@angular/animations/bundles/animations.umd.js',
'@angular/platform-browser/animations':
'/vendor/@angular/platform-browser/bundles/platform-browser-animations.umd.js',
'@angular/core': '/vendor/@angular/core/bundles/core.umd.js',
'@angular/forms': '/vendor/@angular/forms/bundles/forms.umd.js',
'@angular/http': '/vendor/@angular/forms/bundles/http.umd.js',
'@angular/platform-browser':
'/vendor/@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic':
'/vendor/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/router': '/vendor/@angular/router/bundles/router.umd.js',
'@angular/upgrade': '/vendor/@angular/upgrade/bundles/upgrade.umd.js',
'@angular/upgrade/static': '/vendor/@angular/upgrade/bundles/upgrade-static.umd.js',
'rxjs': '/vendor/rxjs',
},
packages: {
// rxjs: {format: 'cjs', exports: 'Rx' }
rxjs: {defaultExtension: 'js'}
}
});

41
packages/examples/build.sh Executable file
View File

@ -0,0 +1,41 @@
#!/usr/bin/env bash
set -u -e -o pipefail
#
# This script is used to compile and copy the contents for each of
# example directories over to the dist/examples directory so that they
# can be tested with karma and protractor. The `gulp serve-examples` command
# can be used to run each of the examples in isolation via http as well.
#
cd `dirname $0`
DIST="../../../dist/examples";
rm -rf -- $DIST
$(npm bin)/tsc -p ./tsconfig-build.json
mkdir $DIST/vendor/
ln -s ../../../dist/packages-dist/ $DIST/vendor/@angular
for FILE in \
../../../node_modules/angular/angular.js \
../../../node_modules/zone.js/dist/zone.js \
../../../node_modules/systemjs/dist/system.js \
../../../node_modules/reflect-metadata/Reflect.js \
../../../node_modules/rxjs
do
ln -s $FILE $DIST/vendor/`basename $FILE`
done
for MODULE in `find . -name module.ts`; do
FINAL_DIR_PATH=$DIST/`dirname $MODULE`
echo "==== $MODULE"
cp _common/*.html $FINAL_DIR_PATH
cp $DIST/_common/*.js $FINAL_DIR_PATH
cp $DIST/_common/*.js.map $FINAL_DIR_PATH
find `dirname $MODULE` -name \*.css -exec cp {} $FINAL_DIR_PATH \;
done

View File

@ -0,0 +1,30 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {browser, by, element, protractor} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
function waitForElement(selector: string) {
const EC = (<any>protractor).ExpectedConditions;
// Waits for the element with id 'abc' to be present on the dom.
browser.wait(EC.presenceOf($(selector)), 20000);
}
describe('Location', () => {
afterEach(verifyNoBrowserErrors);
it('should verify paths', () => {
browser.get('/common/location/ts/#/bar/baz');
waitForElement('hash-location');
expect(element.all(by.css('path-location code')).get(0).getText())
.toEqual('/common/location/ts');
expect(element.all(by.css('hash-location code')).get(0).getText()).toEqual('/bar/baz');
});
});

View File

@ -0,0 +1,26 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// #docregion LocationComponent
import {HashLocationStrategy, Location, LocationStrategy} from '@angular/common';
import {Component} from '@angular/core';
@Component({
selector: 'hash-location',
providers: [Location, {provide: LocationStrategy, useClass: HashLocationStrategy}],
template: `
<h1>HashLocationStrategy</h1>
Current URL is: <code>{{location.path()}}</code><br>
Normalize: <code>/foo/bar/</code> is: <code>{{location.normalize('foo/bar')}}</code><br>
`
})
export class HashLocationComponent {
location: Location;
constructor(location: Location) { this.location = location; }
}
// #enddocregion

View File

@ -0,0 +1,30 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {APP_BASE_HREF} from '@angular/common';
import {Component, NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {HashLocationComponent} from './hash_location_component';
import {PathLocationComponent} from './path_location_component';
@Component({
selector: 'example-app',
template: `<hash-location></hash-location><path-location></path-location>`
})
export class ExampleAppComponent {
}
@NgModule({
declarations: [ExampleAppComponent, PathLocationComponent, HashLocationComponent],
providers: [{provide: APP_BASE_HREF, useValue: '/'}],
imports: [BrowserModule],
bootstrap: [ExampleAppComponent]
})
export class AppModule {
}

View File

@ -0,0 +1,26 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// #docregion LocationComponent
import {Location, LocationStrategy, PathLocationStrategy} from '@angular/common';
import {Component} from '@angular/core';
@Component({
selector: 'path-location',
providers: [Location, {provide: LocationStrategy, useClass: PathLocationStrategy}],
template: `
<h1>PathLocationStrategy</h1>
Current URL is: <code>{{location.path()}}</code><br>
Normalize: <code>/foo/bar/</code> is: <code>{{location.normalize('foo/bar')}}</code><br>
`
})
export class PathLocationComponent {
location: Location;
constructor(location: Location) { this.location = location; }
}
// #enddocregion

View File

@ -0,0 +1,43 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {$, ExpectedConditions, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
function waitForElement(selector: string) {
const EC = ExpectedConditions;
// Waits for the element with id 'abc' to be present on the dom.
browser.wait(EC.presenceOf($(selector)), 20000);
}
describe('ngComponentOutlet', () => {
const URL = 'common/ngComponentOutlet/ts/';
afterEach(verifyNoBrowserErrors);
describe('ng-component-outlet-example', () => {
it('should render simple', () => {
browser.get(URL);
waitForElement('ng-component-outlet-simple-example');
expect(element.all(by.css('hello-world')).getText()).toEqual(['Hello World!']);
});
it('should render complete', () => {
browser.get(URL);
waitForElement('ng-component-outlet-complete-example');
expect(element.all(by.css('complete-component')).getText()).toEqual(['Complete: Ahoj Svet!']);
});
it('should render other module', () => {
browser.get(URL);
waitForElement('ng-component-outlet-other-module-example');
expect(element.all(by.css('other-module-component')).getText()).toEqual([
'Other Module Component!'
]);
});
});
});

View File

@ -0,0 +1,114 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {CommonModule} from '@angular/common';
import {Compiler, Component, Injectable, Injector, NgModule, NgModuleFactory, ReflectiveInjector} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
// #docregion SimpleExample
@Component({selector: 'hello-world', template: 'Hello World!'})
class HelloWorld {
}
@Component({
selector: 'ng-component-outlet-simple-example',
template: `<ng-container *ngComponentOutlet="HelloWorld"></ng-container>`
})
class NgTemplateOutletSimpleExample {
// This field is necessary to expose HelloWorld to the template.
HelloWorld = HelloWorld;
}
// #enddocregion
// #docregion CompleteExample
@Injectable()
class Greeter {
suffix = '!';
}
@Component({
selector: 'complete-component',
template: `Complete: <ng-content></ng-content> <ng-content></ng-content>{{ greeter.suffix }}`
})
class CompleteComponent {
constructor(public greeter: Greeter) {}
}
@Component({
selector: 'ng-component-outlet-complete-example',
template: `
<ng-container *ngComponentOutlet="CompleteComponent;
injector: myInjector;
content: myContent"></ng-container>`
})
class NgTemplateOutletCompleteExample {
// This field is necessary to expose CompleteComponent to the template.
CompleteComponent = CompleteComponent;
myInjector: Injector;
myContent = [[document.createTextNode('Ahoj')], [document.createTextNode('Svet')]];
constructor(injector: Injector) {
this.myInjector = ReflectiveInjector.resolveAndCreate([Greeter], injector);
}
}
// #enddocregion
// #docregion NgModuleFactoryExample
@Component({selector: 'other-module-component', template: `Other Module Component!`})
class OtherModuleComponent {
}
@Component({
selector: 'ng-component-outlet-other-module-example',
template: `
<ng-container *ngComponentOutlet="OtherModuleComponent;
ngModuleFactory: myModule;"></ng-container>`
})
class NgTemplateOutletOtherModuleExample {
// This field is necessary to expose OtherModuleComponent to the template.
OtherModuleComponent = OtherModuleComponent;
myModule: NgModuleFactory<any>;
constructor(compiler: Compiler) { this.myModule = compiler.compileModuleSync(OtherModule); }
}
// #enddocregion
@Component({
selector: 'example-app',
template: `<ng-component-outlet-simple-example></ng-component-outlet-simple-example>
<hr/>
<ng-component-outlet-complete-example></ng-component-outlet-complete-example>
<hr/>
<ng-component-outlet-other-module-example></ng-component-outlet-other-module-example>`
})
class ExampleApp {
}
@NgModule({
imports: [BrowserModule],
declarations: [
ExampleApp, NgTemplateOutletSimpleExample, NgTemplateOutletCompleteExample,
NgTemplateOutletOtherModuleExample, HelloWorld, CompleteComponent
],
entryComponents: [HelloWorld, CompleteComponent],
bootstrap: [ExampleApp]
})
export class AppModule {
}
@NgModule({
imports: [CommonModule],
declarations: [OtherModuleComponent],
entryComponents: [OtherModuleComponent]
})
export class OtherModule {
}

View File

@ -0,0 +1,72 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {$, ExpectedConditions, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
function waitForElement(selector: string) {
const EC = ExpectedConditions;
// Waits for the element with id 'abc' to be present on the dom.
browser.wait(EC.presenceOf($(selector)), 20000);
}
describe('ngIf', () => {
const URL = 'common/ngIf/ts/';
afterEach(verifyNoBrowserErrors);
describe('ng-if-simple', () => {
let comp = 'ng-if-simple';
it('should hide/show content', () => {
browser.get(URL);
waitForElement(comp);
expect(element.all(by.css(comp)).get(0).getText()).toEqual('hide show = true\nText to show');
element(by.css(comp + ' button')).click();
expect(element.all(by.css(comp)).get(0).getText()).toEqual('show show = false');
});
});
describe('ng-if-else', () => {
let comp = 'ng-if-else';
it('should hide/show content', () => {
browser.get(URL);
waitForElement(comp);
expect(element.all(by.css(comp)).get(0).getText()).toEqual('hide show = true\nText to show');
element(by.css(comp + ' button')).click();
expect(element.all(by.css(comp)).get(0).getText())
.toEqual('show show = false\nAlternate text while primary text is hidden');
});
});
describe('ng-if-then-else', () => {
let comp = 'ng-if-then-else';
it('should hide/show content', () => {
browser.get(URL);
waitForElement(comp);
expect(element.all(by.css(comp)).get(0).getText())
.toEqual('hide Switch Primary show = true\nPrimary text to show');
element.all(by.css(comp + ' button')).get(1).click();
expect(element.all(by.css(comp)).get(0).getText())
.toEqual('hide Switch Primary show = true\nSecondary text to show');
element.all(by.css(comp + ' button')).get(0).click();
expect(element.all(by.css(comp)).get(0).getText())
.toEqual('show Switch Primary show = false\nAlternate text while primary text is hidden');
});
});
describe('ng-if-let', () => {
let comp = 'ng-if-let';
it('should hide/show content', () => {
browser.get(URL);
waitForElement(comp);
expect(element.all(by.css(comp)).get(0).getText())
.toEqual('Next User\nWaiting... (user is null)');
element(by.css(comp + ' button')).click();
expect(element.all(by.css(comp)).get(0).getText()).toEqual('Next User\nHello Smith, John!');
});
});
});

View File

@ -0,0 +1,128 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Component, NgModule, OnInit, TemplateRef, ViewChild} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {Subject} from 'rxjs/Subject';
// #docregion NgIfSimple
@Component({
selector: 'ng-if-simple',
template: `
<button (click)="show = !show">{{show ? 'hide' : 'show'}}</button>
show = {{show}}
<br>
<div *ngIf="show">Text to show</div>
`
})
class NgIfSimple {
show: boolean = true;
}
// #enddocregion
// #docregion NgIfElse
@Component({
selector: 'ng-if-else',
template: `
<button (click)="show = !show">{{show ? 'hide' : 'show'}}</button>
show = {{show}}
<br>
<div *ngIf="show; else elseBlock">Text to show</div>
<ng-template #elseBlock>Alternate text while primary text is hidden</ng-template>
`
})
class NgIfElse {
show: boolean = true;
}
// #enddocregion
// #docregion NgIfThenElse
@Component({
selector: 'ng-if-then-else',
template: `
<button (click)="show = !show">{{show ? 'hide' : 'show'}}</button>
<button (click)="switchPrimary()">Switch Primary</button>
show = {{show}}
<br>
<div *ngIf="show; then thenBlock; else elseBlock">this is ignored</div>
<ng-template #primaryBlock>Primary text to show</ng-template>
<ng-template #secondaryBlock>Secondary text to show</ng-template>
<ng-template #elseBlock>Alternate text while primary text is hidden</ng-template>
`
})
class NgIfThenElse implements OnInit {
thenBlock: TemplateRef<any> = null;
show: boolean = true;
@ViewChild('primaryBlock')
primaryBlock: TemplateRef<any> = null;
@ViewChild('secondaryBlock')
secondaryBlock: TemplateRef<any> = null;
switchPrimary() {
this.thenBlock = this.thenBlock === this.primaryBlock ? this.secondaryBlock : this.primaryBlock;
}
ngOnInit() { this.thenBlock = this.primaryBlock; }
}
// #enddocregion
// #docregion NgIfLet
@Component({
selector: 'ng-if-let',
template: `
<button (click)="nextUser()">Next User</button>
<br>
<div *ngIf="userObservable | async; else loading; let user">
Hello {{user.last}}, {{user.first}}!
</div>
<ng-template #loading let-user>Waiting... (user is {{user|json}})</ng-template>
`
})
class NgIfLet {
userObservable = new Subject<{first: string, last: string}>();
first = ['John', 'Mike', 'Mary', 'Bob'];
firstIndex = 0;
last = ['Smith', 'Novotny', 'Angular'];
lastIndex = 0;
nextUser() {
let first = this.first[this.firstIndex++];
if (this.firstIndex >= this.first.length) this.firstIndex = 0;
let last = this.last[this.lastIndex++];
if (this.lastIndex >= this.last.length) this.lastIndex = 0;
this.userObservable.next({first, last});
}
}
// #enddocregion
@Component({
selector: 'example-app',
template: `
<ng-if-simple></ng-if-simple>
<hr>
<ng-if-else></ng-if-else>
<hr>
<ng-if-then-else></ng-if-then-else>
<hr>
<ng-if-let></ng-if-let>
<hr>
`
})
class ExampleApp {
}
@NgModule({
imports: [BrowserModule],
declarations: [ExampleApp, NgIfSimple, NgIfElse, NgIfThenElse, NgIfLet],
bootstrap: [ExampleApp]
})
export class AppModule {
}

View File

@ -0,0 +1,31 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {$, ExpectedConditions, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
function waitForElement(selector: string) {
const EC = ExpectedConditions;
// Waits for the element with id 'abc' to be present on the dom.
browser.wait(EC.presenceOf($(selector)), 20000);
}
describe('ngTemplateOutlet', () => {
const URL = 'common/ngTemplateOutlet/ts/';
afterEach(verifyNoBrowserErrors);
describe('ng-template-outlet-example', () => {
it('should render', () => {
browser.get(URL);
waitForElement('ng-template-outlet-example');
expect(element.all(by.css('ng-template-outlet-example span')).getText()).toEqual([
'Hello', 'Hello World!', 'Ahoj Svet!'
]);
});
});
});

View File

@ -0,0 +1,49 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Component, NgModule, OnInit, TemplateRef, ViewChild} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {Subject} from 'rxjs/Subject';
// #docregion NgTemplateOutlet
@Component({
selector: 'ng-template-outlet-example',
template: `
<ng-container *ngTemplateOutlet="greet"></ng-container>
<hr>
<ng-container *ngTemplateOutlet="eng; context: myContext"></ng-container>
<hr>
<ng-container *ngTemplateOutlet="svk; context: myContext"></ng-container>
<hr>
<ng-template #greet><span>Hello</span></ng-template>
<ng-template #eng let-name><span>Hello {{name}}!</span></ng-template>
<ng-template #svk let-person="localSk"><span>Ahoj {{person}}!</span></ng-template>
`
})
class NgTemplateOutletExample {
myContext = {$implicit: 'World', localSk: 'Svet'};
}
// #enddocregion
@Component({
selector: 'example-app',
template: `<ng-template-outlet-example></ng-template-outlet-example>`
})
class ExampleApp {
}
@NgModule({
imports: [BrowserModule],
declarations: [ExampleApp, NgTemplateOutletExample],
bootstrap: [ExampleApp]
})
export class AppModule {
}

View File

@ -0,0 +1,69 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Component} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import {Subscriber} from 'rxjs/Subscriber';
// #docregion AsyncPipePromise
@Component({
selector: 'async-promise-pipe',
template: `<div>
<code>promise|async</code>:
<button (click)="clicked()">{{ arrived ? 'Reset' : 'Resolve' }}</button>
<span>Wait for it... {{ greeting | async }}</span>
</div>`
})
export class AsyncPromisePipeComponent {
greeting: Promise<string> = null;
arrived: boolean = false;
private resolve: Function = null;
constructor() { this.reset(); }
reset() {
this.arrived = false;
this.greeting = new Promise<string>((resolve, reject) => { this.resolve = resolve; });
}
clicked() {
if (this.arrived) {
this.reset();
} else {
this.resolve('hi there!');
this.arrived = true;
}
}
}
// #enddocregion
// #docregion AsyncPipeObservable
@Component({
selector: 'async-observable-pipe',
template: '<div><code>observable|async</code>: Time: {{ time | async }}</div>'
})
export class AsyncObservablePipeComponent {
time = new Observable<string>((observer: Subscriber<string>) => {
setInterval(() => observer.next(new Date().toString()), 1000);
});
}
// #enddocregion
// For some reason protractor hangs on setInterval. So we will run outside of angular zone so that
// protractor will not see us. Also we want to have this outside the docregion so as not to confuse
// the reader.
function setInterval(fn: Function, delay: number) {
const zone = Zone.current;
let rootZone = zone;
while (rootZone.parent) {
rootZone = rootZone.parent;
}
rootZone.run(
() => { window.setInterval(function() { zone.run(fn, this, arguments as any); }, delay); });
}

View File

@ -0,0 +1,23 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Component} from '@angular/core';
// #docregion DatePipe
@Component({
selector: 'date-pipe',
template: `<div>
<p>Today is {{today | date}}</p>
<p>Or if you prefer, {{today | date:'fullDate'}}</p>
<p>The time is {{today | date:'jmZ'}}</p>
</div>`
})
export class DatePipeComponent {
today: number = Date.now();
}
// #enddocregion

View File

@ -0,0 +1,44 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {$, ExpectedConditions, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
function waitForElement(selector: string) {
const EC = ExpectedConditions;
// Waits for the element with id 'abc' to be present on the dom.
browser.wait(EC.presenceOf($(selector)), 20000);
}
describe('pipe', () => {
afterEach(verifyNoBrowserErrors);
describe('async', () => {
const URL = '/common/pipes/ts/';
it('should resolve and display promise', () => {
browser.get(URL);
waitForElement('async-promise-pipe');
expect(element.all(by.css('async-promise-pipe span')).get(0).getText())
.toEqual('Wait for it...');
element(by.css('async-promise-pipe button')).click();
expect(element.all(by.css('async-promise-pipe span')).get(0).getText())
.toEqual('Wait for it... hi there!');
});
it('should update lowercase/uppercase', () => {
browser.get(URL);
waitForElement('lowerupper-pipe');
element(by.css('lowerupper-pipe input')).sendKeys('Hello World!');
expect(element.all(by.css('lowerupper-pipe pre')).get(0).getText())
.toEqual('\'hello world!\'');
expect(element.all(by.css('lowerupper-pipe pre')).get(1).getText())
.toEqual('\'HELLO WORLD!\'');
});
});
});

View File

@ -0,0 +1,30 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Component} from '@angular/core';
// #docregion I18nPluralPipeComponent
@Component({
selector: 'i18n-plural-pipe',
template: `<div>{{ messages.length | i18nPlural: messageMapping }}</div>`
})
export class I18nPluralPipeComponent {
messages: any[] = ['Message 1'];
messageMapping:
{[k: string]: string} = {'=0': 'No messages.', '=1': 'One message.', 'other': '# messages.'};
}
// #enddocregion
// #docregion I18nSelectPipeComponent
@Component(
{selector: 'i18n-select-pipe', template: `<div>{{gender | i18nSelect: inviteMap}} </div>`})
export class I18nSelectPipeComponent {
gender: string = 'male';
inviteMap: any = {'male': 'Invite him.', 'female': 'Invite her.', 'other': 'Invite them.'};
}
//#enddocregion

View File

@ -0,0 +1,24 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Component} from '@angular/core';
// #docregion JsonPipe
@Component({
selector: 'json-pipe',
template: `<div>
<p>Without JSON pipe:</p>
<pre>{{object}}</pre>
<p>With JSON pipe:</p>
<pre>{{object | json}}</pre>
</div>`
})
export class JsonPipeComponent {
object: Object = {foo: 'bar', baz: 'qux', nested: {xyz: 3, numbers: [1, 2, 3, 4, 5]}};
}
// #enddocregion

View File

@ -0,0 +1,24 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Component} from '@angular/core';
// #docregion LowerUpperPipe
@Component({
selector: 'lowerupper-pipe',
template: `<div>
<label>Name: </label><input #name (keyup)="change(name.value)" type="text">
<p>In lowercase: <pre>'{{value | lowercase}}'</pre>
<p>In uppercase: <pre>'{{value | uppercase}}'</pre>
</div>`
})
export class LowerUpperPipeComponent {
value: string;
change(value: string) { this.value = value; }
}
// #enddocregion

View File

@ -0,0 +1,66 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Component, NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {AsyncObservablePipeComponent, AsyncPromisePipeComponent} from './async_pipe';
import {DatePipeComponent} from './date_pipe';
import {I18nPluralPipeComponent, I18nSelectPipeComponent} from './i18n_pipe';
import {JsonPipeComponent} from './json_pipe';
import {LowerUpperPipeComponent} from './lowerupper_pipe';
import {CurrencyPipeComponent, NumberPipeComponent, PercentPipeComponent} from './number_pipe';
import {SlicePipeListComponent, SlicePipeStringComponent} from './slice_pipe';
@Component({
selector: 'example-app',
template: `
<h1>Pipe Example</h1>
<h2><code>async</code></h2>
<async-promise-pipe></async-promise-pipe>
<async-observable-pipe></async-observable-pipe>
<h2><code>date</code></h2>
<date-pipe></date-pipe>
<h2><code>json</code></h2>
<json-pipe></json-pipe>
<h2><code>lower</code>, <code>upper</code></h2>
<lowerupper-pipe></lowerupper-pipe>
<h2><code>number</code></h2>
<number-pipe></number-pipe>
<percent-pipe></percent-pipe>
<currency-pipe></currency-pipe>
<h2><code>slice</code></h2>
<slice-string-pipe></slice-string-pipe>
<slice-list-pipe></slice-list-pipe>
<h2><code>i18n</code></h2>
<i18n-plural-pipe></i18n-plural-pipe>
<i18n-select-pipe></i18n-select-pipe>
`
})
export class ExampleAppComponent {
}
@NgModule({
declarations: [
AsyncPromisePipeComponent, AsyncObservablePipeComponent, ExampleAppComponent, JsonPipeComponent,
DatePipeComponent, LowerUpperPipeComponent, NumberPipeComponent, PercentPipeComponent,
CurrencyPipeComponent, SlicePipeStringComponent, SlicePipeListComponent,
I18nPluralPipeComponent, I18nSelectPipeComponent
],
imports: [BrowserModule],
bootstrap: [ExampleAppComponent]
})
export class AppModule {
}

View File

@ -0,0 +1,53 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Component} from '@angular/core';
// #docregion NumberPipe
@Component({
selector: 'number-pipe',
template: `<div>
<p>e (no formatting): {{e}}</p>
<p>e (3.1-5): {{e | number:'3.1-5'}}</p>
<p>pi (no formatting): {{pi}}</p>
<p>pi (3.5-5): {{pi | number:'3.5-5'}}</p>
</div>`
})
export class NumberPipeComponent {
pi: number = 3.141592;
e: number = 2.718281828459045;
}
// #enddocregion
// #docregion PercentPipe
@Component({
selector: 'percent-pipe',
template: `<div>
<p>A: {{a | percent}}</p>
<p>B: {{b | percent:'4.3-5'}}</p>
</div>`
})
export class PercentPipeComponent {
a: number = 0.259;
b: number = 1.3495;
}
// #enddocregion
// #docregion CurrencyPipe
@Component({
selector: 'currency-pipe',
template: `<div>
<p>A: {{a | currency:'USD':false}}</p>
<p>B: {{b | currency:'USD':true:'4.2-2'}}</p>
</div>`
})
export class CurrencyPipeComponent {
a: number = 0.259;
b: number = 1.3495;
}
// #enddocregion

View File

@ -0,0 +1,38 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Component} from '@angular/core';
// #docregion SlicePipe_string
@Component({
selector: 'slice-string-pipe',
template: `<div>
<p>{{str}}[0:4]: '{{str | slice:0:4}}' - output is expected to be 'abcd'</p>
<p>{{str}}[4:0]: '{{str | slice:4:0}}' - output is expected to be ''</p>
<p>{{str}}[-4]: '{{str | slice:-4}}' - output is expected to be 'ghij'</p>
<p>{{str}}[-4:-2]: '{{str | slice:-4:-2}}' - output is expected to be 'gh'</p>
<p>{{str}}[-100]: '{{str | slice:-100}}' - output is expected to be 'abcdefghij'</p>
<p>{{str}}[100]: '{{str | slice:100}}' - output is expected to be ''</p>
</div>`
})
export class SlicePipeStringComponent {
str: string = 'abcdefghij';
}
// #enddocregion
// #docregion SlicePipe_list
@Component({
selector: 'slice-list-pipe',
template: `<ul>
<li *ngFor="let i of collection | slice:1:3">{{i}}</li>
</ul>`
})
export class SlicePipeListComponent {
collection: string[] = ['a', 'b', 'c', 'd'];
}
// #enddocregion

View File

@ -0,0 +1,38 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {UrlResolver} from '@angular/compiler';
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
let MyApp: any;
// #docregion url_resolver
class MyUrlResolver extends UrlResolver {
resolve(baseUrl: string, url: string): string {
// Serve CSS files from a special CDN.
if (url.substr(-4) === '.css') {
return super.resolve('http://cdn.myapp.com/css/', url);
}
return super.resolve(baseUrl, url);
}
}
@NgModule({
imports: [BrowserModule],
providers: [{provide: UrlResolver, useClass: MyUrlResolver}],
bootstrap: [MyApp]
})
class AppModule {
}
export function main() {
platformBrowserDynamic().bootstrapModule(AppModule);
}
// #enddocregion

View File

@ -0,0 +1,56 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// #docregion Component
import {Component, NgModule, animate, state, style, transition, trigger} from '@angular/core';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
@Component({
selector: 'example-app',
styles: [`
.toggle-container {
background-color:white;
border:10px solid black;
width:200px;
text-align:center;
line-height:100px;
font-size:50px;
box-sizing:border-box;
overflow:hidden;
}
`],
animations: [trigger(
'openClose',
[
state('collapsed, void', style({height: '0px', color: 'maroon', borderColor: 'maroon'})),
state('expanded', style({height: '*', borderColor: 'green', color: 'green'})),
transition(
'collapsed <=> expanded', [animate(500, style({height: '250px'})), animate(500)])
])],
template: `
<button (click)="expand()">Open</button>
<button (click)="collapse()">Closed</button>
<hr />
<div class="toggle-container" [@openClose]="stateExpression">
Look at this box
</div>
`
})
export class MyExpandoCmp {
stateExpression: string;
constructor() { this.collapse(); }
expand() { this.stateExpression = 'expanded'; }
collapse() { this.stateExpression = 'collapsed'; }
}
@NgModule(
{imports: [BrowserAnimationsModule], declarations: [MyExpandoCmp], bootstrap: [MyExpandoCmp]})
export class AppModule {
}
// #enddocregion

View File

@ -0,0 +1,30 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {$, ExpectedConditions, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../../_common/e2e_util';
function waitForElement(selector: string) {
const EC = ExpectedConditions;
// Waits for the element with id 'abc' to be present on the dom.
browser.wait(EC.presenceOf($(selector)), 20000);
}
describe('animation example', () => {
afterEach(verifyNoBrowserErrors);
describe('index view', () => {
const URL = '/core/animation/ts/dsl/';
it('should list out the current collection of items', () => {
browser.get(URL);
waitForElement('.toggle-container');
expect(element.all(by.css('.toggle-container')).get(0).getText()).toEqual('Look at this box');
});
});
});

View File

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

View File

@ -0,0 +1,16 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {DebugElement} from '@angular/core';
let debugElement: DebugElement;
let predicate: any;
// #docregion scope_all
debugElement.query(predicate);
// #enddocregion

View File

@ -0,0 +1,43 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// #docregion Component
import {Component, ContentChild, Directive, Input} from '@angular/core';
@Directive({selector: 'pane'})
export class Pane {
@Input() id: string;
}
@Component({
selector: 'tab',
template: `
<div>pane: {{pane?.id}}</div>
`
})
export class Tab {
@ContentChild(Pane) pane: Pane;
}
@Component({
selector: 'example-app',
template: `
<tab>
<pane id="1" *ngIf="shouldShow"></pane>
<pane id="2" *ngIf="!shouldShow"></pane>
</tab>
<button (click)="toggle()">Toggle</button>
`,
})
export class ContentChildComp {
shouldShow = true;
toggle() { this.shouldShow = !this.shouldShow; }
}
// #enddocregion

View File

@ -0,0 +1,24 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// #docregion HowTo
import {AfterContentInit, ContentChild, Directive} from '@angular/core';
@Directive({selector: 'child-directive'})
class ChildDirective {
}
@Directive({selector: 'someDir'})
class SomeDir implements AfterContentInit {
@ContentChild(ChildDirective) contentChild: ChildDirective;
ngAfterContentInit() {
// contentChild is set
}
}
// #enddocregion

View File

@ -0,0 +1,30 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ElementFinder, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../../_common/e2e_util';
describe('contentChild example', () => {
afterEach(verifyNoBrowserErrors);
let button: ElementFinder;
let result: ElementFinder;
beforeEach(() => {
browser.get('/core/di/ts/contentChild/index.html');
button = element(by.css('button'));
result = element(by.css('div'));
});
it('should query content child', () => {
expect(result.getText()).toEqual('pane: 1');
button.click();
expect(result.getText()).toEqual('pane: 2');
});
});

View File

@ -0,0 +1,19 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {ContentChildComp, Pane, Tab} from './content_child_example';
@NgModule({
imports: [BrowserModule],
declarations: [ContentChildComp, Pane, Tab],
bootstrap: [ContentChildComp]
})
export class AppModule {
}

View File

@ -0,0 +1,46 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// #docregion Component
import {Component, ContentChildren, Directive, Input, QueryList} from '@angular/core';
@Directive({selector: 'pane'})
export class Pane {
@Input() id: string;
}
@Component({
selector: 'tab',
template: `
<div>panes: {{serializedPanes}}</div>
`
})
export class Tab {
@ContentChildren(Pane) panes: QueryList<Pane>;
get serializedPanes(): string { return this.panes ? this.panes.map(p => p.id).join(', ') : ''; }
}
@Component({
selector: 'example-app',
template: `
<tab>
<pane id="1"></pane>
<pane id="2"></pane>
<pane id="3" *ngIf="shouldShow"></pane>
</tab>
<button (click)="show()">Show 3</button>
`,
})
export class ContentChildrenComp {
shouldShow = false;
show() { this.shouldShow = true; }
}
// #enddocregion

View File

@ -0,0 +1,24 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// #docregion HowTo
import {AfterContentInit, ContentChildren, Directive, QueryList} from '@angular/core';
@Directive({selector: 'child-directive'})
class ChildDirective {
}
@Directive({selector: 'someDir'})
class SomeDir implements AfterContentInit {
@ContentChildren(ChildDirective) contentChildren: QueryList<ChildDirective>;
ngAfterContentInit() {
// contentChildren is set
}
}
// #enddocregion

View File

@ -0,0 +1,30 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ElementFinder, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../../_common/e2e_util';
describe('contentChildren example', () => {
afterEach(verifyNoBrowserErrors);
let button: ElementFinder;
let result: ElementFinder;
beforeEach(() => {
browser.get('/core/di/ts/contentChildren/index.html');
button = element(by.css('button'));
result = element(by.css('div'));
});
it('should query content children', () => {
expect(result.getText()).toEqual('panes: 1, 2');
button.click();
expect(result.getText()).toEqual('panes: 1, 2, 3');
});
});

View File

@ -0,0 +1,19 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {ContentChildrenComp, Pane, Tab} from './content_children_example';
@NgModule({
imports: [BrowserModule],
declarations: [ContentChildrenComp, Pane, Tab],
bootstrap: [ContentChildrenComp]
})
export class AppModule {
}

View File

@ -0,0 +1,50 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Inject, ReflectiveInjector, forwardRef, resolveForwardRef} from '@angular/core';
export function main() {
describe('forwardRef examples', () => {
it('ForwardRefFn example works', () => {
// #docregion forward_ref_fn
const ref = forwardRef(() => Lock);
// #enddocregion
expect(ref).not.toBeNull();
class Lock {}
});
it('can be used to inject a class defined later', () => {
// #docregion forward_ref
class Door {
lock: Lock;
// Door attempts to inject Lock, despite it not being defined yet.
// forwardRef makes this possible.
constructor(@Inject(forwardRef(() => Lock)) lock: Lock) { this.lock = lock; }
}
// Only at this point Lock is defined.
class Lock {}
const injector = ReflectiveInjector.resolveAndCreate([Door, Lock]);
const door = injector.get(Door);
expect(door instanceof Door).toBeTruthy();
expect(door.lock instanceof Lock).toBeTruthy();
// #enddocregion
});
it('can be unwrapped', () => {
// #docregion resolve_forward_ref
const ref = forwardRef(() => 'refValue');
expect(resolveForwardRef(ref)).toEqual('refValue');
expect(resolveForwardRef('regularValue')).toEqual('regularValue');
// #enddocregion
});
});
}

View File

@ -0,0 +1,41 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {InjectionToken, Injector, ReflectiveInjector} from '@angular/core';
export function main() {
describe('injector metadata examples', () => {
it('works', () => {
// #docregion Injector
const injector: Injector =
ReflectiveInjector.resolveAndCreate([{provide: 'validToken', useValue: 'Value'}]);
expect(injector.get('validToken')).toEqual('Value');
expect(() => injector.get('invalidToken')).toThrowError();
expect(injector.get('invalidToken', 'notFound')).toEqual('notFound');
// #enddocregion
});
it('injects injector', () => {
// #docregion injectInjector
const injector = ReflectiveInjector.resolveAndCreate([]);
expect(injector.get(Injector)).toBe(injector);
// #enddocregion
});
it('should infer type', () => {
// #docregion InjectionToken
const BASE_URL = new InjectionToken<string>('BaseUrl');
const injector =
ReflectiveInjector.resolveAndCreate([{provide: BASE_URL, useValue: 'http://localhost'}]);
const url = injector.get(BASE_URL);
// here `url` is inferred to be `string` because `BASE_URL` is `InjectionToken<string>`.
expect(url).toBe('http://localhost');
// #enddocregion
});
});
}

View File

@ -0,0 +1,185 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Component, Directive, Host, Inject, Injectable, Optional, ReflectiveInjector, Self, SkipSelf} from '@angular/core';
import {ComponentFixture, TestBed} from '@angular/core/testing';
export function main() {
describe('di metadata examples', () => {
describe('Inject', () => {
it('works', () => {
// #docregion Inject
class Engine {}
@Injectable()
class Car {
constructor(@Inject('MyEngine') public engine: Engine) {}
}
const injector =
ReflectiveInjector.resolveAndCreate([{provide: 'MyEngine', useClass: Engine}, Car]);
expect(injector.get(Car).engine instanceof Engine).toBe(true);
// #enddocregion
});
it('works without decorator', () => {
// #docregion InjectWithoutDecorator
class Engine {}
@Injectable()
class Car {
constructor(public engine: Engine) {
} // same as constructor(@Inject(Engine) engine:Engine)
}
const injector = ReflectiveInjector.resolveAndCreate([Engine, Car]);
expect(injector.get(Car).engine instanceof Engine).toBe(true);
// #enddocregion
});
});
describe('Optional', () => {
it('works', () => {
// #docregion Optional
class Engine {}
@Injectable()
class Car {
constructor(@Optional() public engine: Engine) {}
}
const injector = ReflectiveInjector.resolveAndCreate([Car]);
expect(injector.get(Car).engine).toBeNull();
// #enddocregion
});
});
describe('Injectable', () => {
it('works', () => {
// #docregion Injectable
@Injectable()
class UsefulService {
}
@Injectable()
class NeedsService {
constructor(public service: UsefulService) {}
}
const injector = ReflectiveInjector.resolveAndCreate([NeedsService, UsefulService]);
expect(injector.get(NeedsService).service instanceof UsefulService).toBe(true);
// #enddocregion
});
it('throws without Injectable', () => {
// #docregion InjectableThrows
class UsefulService {}
class NeedsService {
constructor(public service: UsefulService) {}
}
expect(() => ReflectiveInjector.resolveAndCreate([NeedsService, UsefulService])).toThrow();
// #enddocregion
});
});
describe('Self', () => {
it('works', () => {
// #docregion Self
class Dependency {}
@Injectable()
class NeedsDependency {
constructor(@Self() public dependency: Dependency) {}
}
let inj = ReflectiveInjector.resolveAndCreate([Dependency, NeedsDependency]);
const nd = inj.get(NeedsDependency);
expect(nd.dependency instanceof Dependency).toBe(true);
inj = ReflectiveInjector.resolveAndCreate([Dependency]);
const child = inj.resolveAndCreateChild([NeedsDependency]);
expect(() => child.get(NeedsDependency)).toThrowError();
// #enddocregion
});
});
describe('SkipSelf', () => {
it('works', () => {
// #docregion SkipSelf
class Dependency {}
@Injectable()
class NeedsDependency {
constructor(@SkipSelf() public dependency: Dependency) { this.dependency = dependency; }
}
const parent = ReflectiveInjector.resolveAndCreate([Dependency]);
const child = parent.resolveAndCreateChild([NeedsDependency]);
expect(child.get(NeedsDependency).dependency instanceof Dependency).toBe(true);
const inj = ReflectiveInjector.resolveAndCreate([Dependency, NeedsDependency]);
expect(() => inj.get(NeedsDependency)).toThrowError();
// #enddocregion
});
});
describe('Host', () => {
it('works', () => {
// #docregion Host
class OtherService {}
class HostService {}
@Directive({selector: 'child-directive'})
class ChildDirective {
logs: string[] = [];
constructor(@Optional() @Host() os: OtherService, @Optional() @Host() hs: HostService) {
// os is null: true
this.logs.push(`os is null: ${os === null}`);
// hs is an instance of HostService: true
this.logs.push(`hs is an instance of HostService: ${hs instanceof HostService}`);
}
}
@Component({
selector: 'parent-cmp',
viewProviders: [HostService],
template: '<child-directive></child-directive>',
})
class ParentCmp {
}
@Component({
selector: 'app',
viewProviders: [OtherService],
template: '<parent-cmp></parent-cmp>',
})
class App {
}
// #enddocregion
TestBed.configureTestingModule({
declarations: [App, ParentCmp, ChildDirective],
});
let cmp: ComponentFixture<App>;
expect(() => cmp = TestBed.createComponent(App)).not.toThrow();
expect(cmp.debugElement.children[0].children[0].injector.get(ChildDirective).logs).toEqual([
'os is null: true',
'hs is an instance of HostService: true',
]);
});
});
});
}

View File

@ -0,0 +1,149 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Inject, Injectable, InjectionToken, Optional, ReflectiveInjector} from '@angular/core';
export function main() {
describe('Provider examples', () => {
describe('TypeProvider', () => {
it('works', () => {
// #docregion TypeProvider
@Injectable()
class Greeting {
salutation = 'Hello';
}
const injector = ReflectiveInjector.resolveAndCreate([
Greeting, // Shorthand for { provide: Greeting, useClass: Greeting }
]);
expect(injector.get(Greeting).salutation).toBe('Hello');
// #enddocregion
});
});
describe('ValueProvider', () => {
it('works', () => {
// #docregion ValueProvider
const injector =
ReflectiveInjector.resolveAndCreate([{provide: String, useValue: 'Hello'}]);
expect(injector.get(String)).toEqual('Hello');
// #enddocregion
});
});
describe('MultiProviderAspect', () => {
it('works', () => {
// #docregion MultiProviderAspect
const injector = ReflectiveInjector.resolveAndCreate([
{provide: 'local', multi: true, useValue: 'en'},
{provide: 'local', multi: true, useValue: 'sk'},
]);
const locales: string[] = injector.get('local');
expect(locales).toEqual(['en', 'sk']);
// #enddocregion
});
});
describe('ClassProvider', () => {
it('works', () => {
// #docregion ClassProvider
abstract class Shape { name: string; }
class Square extends Shape {
name = 'square';
}
const injector = ReflectiveInjector.resolveAndCreate([{provide: Shape, useClass: Square}]);
const shape: Shape = injector.get(Shape);
expect(shape.name).toEqual('square');
expect(shape instanceof Square).toBe(true);
// #enddocregion
});
it('is different then useExisting', () => {
// #docregion ClassProviderDifference
class Greeting {
salutation = 'Hello';
}
class FormalGreeting extends Greeting {
salutation = 'Greetings';
}
const injector = ReflectiveInjector.resolveAndCreate(
[FormalGreeting, {provide: Greeting, useClass: FormalGreeting}]);
// The injector returns different instances.
// See: {provide: ?, useExisting: ?} if you want the same instance.
expect(injector.get(FormalGreeting)).not.toBe(injector.get(Greeting));
// #enddocregion
});
});
describe('ExistingProvider', () => {
it('works', () => {
// #docregion ExistingProvider
class Greeting {
salutation = 'Hello';
}
class FormalGreeting extends Greeting {
salutation = 'Greetings';
}
const injector = ReflectiveInjector.resolveAndCreate(
[FormalGreeting, {provide: Greeting, useExisting: FormalGreeting}]);
expect(injector.get(Greeting).salutation).toEqual('Greetings');
expect(injector.get(FormalGreeting).salutation).toEqual('Greetings');
expect(injector.get(FormalGreeting)).toBe(injector.get(Greeting));
// #enddocregion
});
});
describe('FactoryProvider', () => {
it('works', () => {
// #docregion FactoryProvider
const Location = new InjectionToken('location');
const Hash = new InjectionToken('hash');
const injector = ReflectiveInjector.resolveAndCreate([
{provide: Location, useValue: 'http://angular.io/#someLocation'}, {
provide: Hash,
useFactory: (location: string) => location.split('#')[1],
deps: [Location]
}
]);
expect(injector.get(Hash)).toEqual('someLocation');
// #enddocregion
});
it('supports optional dependencies', () => {
// #docregion FactoryProviderOptionalDeps
const Location = new InjectionToken('location');
const Hash = new InjectionToken('hash');
const injector = ReflectiveInjector.resolveAndCreate([{
provide: Hash,
useFactory: (location: string) => `Hash for: ${location}`,
// use a nested array to define metadata for dependencies.
deps: [[new Optional(), Location]]
}]);
expect(injector.get(Hash)).toEqual('Hash for: null');
// #enddocregion
});
});
});
}

View File

@ -0,0 +1,30 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ElementFinder, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../../_common/e2e_util';
describe('viewChild example', () => {
afterEach(verifyNoBrowserErrors);
let button: ElementFinder;
let result: ElementFinder;
beforeEach(() => {
browser.get('/core/di/ts/viewChild/index.html');
button = element(by.css('button'));
result = element(by.css('div'));
});
it('should query view child', () => {
expect(result.getText()).toEqual('Selected: 1');
button.click();
expect(result.getText()).toEqual('Selected: 2');
});
});

View File

@ -0,0 +1,17 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {Pane, ViewChildComp} from './view_child_example';
@NgModule(
{imports: [BrowserModule], declarations: [ViewChildComp, Pane], bootstrap: [ViewChildComp]})
export class AppModule {
}

View File

@ -0,0 +1,37 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// #docregion Component
import {Component, Directive, Input, ViewChild} from '@angular/core';
@Directive({selector: 'pane'})
export class Pane {
@Input() id: string;
}
@Component({
selector: 'example-app',
template: `
<pane id="1" *ngIf="shouldShow"></pane>
<pane id="2" *ngIf="!shouldShow"></pane>
<button (click)="toggle()">Toggle</button>
<div>Selected: {{selectedPane}}</div>
`,
})
export class ViewChildComp {
@ViewChild(Pane)
set pane(v: Pane) {
setTimeout(() => { this.selectedPane = v.id; }, 0);
}
selectedPane: string = '';
shouldShow = true;
toggle() { this.shouldShow = !this.shouldShow; }
}
// #enddocregion

View File

@ -0,0 +1,24 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// #docregion HowTo
import {AfterViewInit, Component, Directive, ViewChild} from '@angular/core';
@Directive({selector: 'child-directive'})
class ChildDirective {
}
@Component({selector: 'someCmp', templateUrl: 'someCmp.html'})
class SomeCmp implements AfterViewInit {
@ViewChild(ChildDirective) child: ChildDirective;
ngAfterViewInit() {
// child is set
}
}
// #enddocregion

View File

@ -0,0 +1,31 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ElementFinder, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../../_common/e2e_util';
describe('viewChildren example', () => {
afterEach(verifyNoBrowserErrors);
let button: ElementFinder;
let result: ElementFinder;
beforeEach(() => {
browser.get('/core/di/ts/viewChildren/index.html');
button = element(by.css('button'));
result = element(by.css('div'));
});
it('should query view children', () => {
expect(result.getText()).toEqual('panes: 1, 2');
button.click();
expect(result.getText()).toEqual('panes: 1, 2, 3');
});
});

View File

@ -0,0 +1,20 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {Pane, ViewChildrenComp} from './view_children_example';
@NgModule({
imports: [BrowserModule],
declarations: [ViewChildrenComp, Pane],
bootstrap: [ViewChildrenComp]
})
export class AppModule {
}

View File

@ -0,0 +1,46 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// #docregion Component
import {AfterViewInit, Component, Directive, Input, QueryList, ViewChildren} from '@angular/core';
@Directive({selector: 'pane'})
export class Pane {
@Input() id: string;
}
@Component({
selector: 'example-app',
template: `
<pane id="1"></pane>
<pane id="2"></pane>
<pane id="3" *ngIf="shouldShow"></pane>
<button (click)="show()">Show 3</button>
<div>panes: {{serializedPanes}}</div>
`,
})
export class ViewChildrenComp implements AfterViewInit {
@ViewChildren(Pane) panes: QueryList<Pane>;
serializedPanes: string = '';
shouldShow = false;
show() { this.shouldShow = true; }
ngAfterViewInit() {
this.calculateSerializedPanes();
this.panes.changes.subscribe((r) => { this.calculateSerializedPanes(); });
}
calculateSerializedPanes() {
setTimeout(() => { this.serializedPanes = this.panes.map(p => p.id).join(', '); }, 0);
}
}
// #enddocregion

View File

@ -0,0 +1,24 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// #docregion HowTo
import {AfterViewInit, Component, Directive, QueryList, ViewChildren} from '@angular/core';
@Directive({selector: 'child-directive'})
class ChildDirective {
}
@Component({selector: 'someCmp', templateUrl: 'someCmp.html'})
class SomeCmp implements AfterViewInit {
@ViewChildren(ChildDirective) viewChildren: QueryList<ChildDirective>;
ngAfterViewInit() {
// viewChildren is set
}
}
// #enddocregion

View File

View File

@ -0,0 +1,27 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Component, NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
// #docregion bootstrap
@Component({selector: 'my-app', template: 'Hello {{ name }}!'})
class MyApp {
name: string = 'World';
}
@NgModule({imports: [BrowserModule], bootstrap: [MyApp]})
class AppModule {
}
export function main() {
platformBrowserDynamic().bootstrapModule(AppModule);
}
// #enddocregion

View File

@ -0,0 +1,150 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, Component, DoCheck, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, Type} from '@angular/core';
import {TestBed} from '@angular/core/testing';
export function main() {
describe('lifecycle hooks examples', () => {
it('should work with ngOnInit', () => {
// #docregion OnInit
@Component({selector: 'my-cmp', template: `...`})
class MyComponent implements OnInit {
ngOnInit() {
// ...
}
}
// #enddocregion
expect(createAndLogComponent(MyComponent)).toEqual([['ngOnInit', []]]);
});
it('should work with ngDoCheck', () => {
// #docregion DoCheck
@Component({selector: 'my-cmp', template: `...`})
class MyComponent implements DoCheck {
ngDoCheck() {
// ...
}
}
// #enddocregion
expect(createAndLogComponent(MyComponent)).toEqual([['ngDoCheck', []]]);
});
it('should work with ngAfterContentChecked', () => {
// #docregion AfterContentChecked
@Component({selector: 'my-cmp', template: `...`})
class MyComponent implements AfterContentChecked {
ngAfterContentChecked() {
// ...
}
}
// #enddocregion
expect(createAndLogComponent(MyComponent)).toEqual([['ngAfterContentChecked', []]]);
});
it('should work with ngAfterContentInit', () => {
// #docregion AfterContentInit
@Component({selector: 'my-cmp', template: `...`})
class MyComponent implements AfterContentInit {
ngAfterContentInit() {
// ...
}
}
// #enddocregion
expect(createAndLogComponent(MyComponent)).toEqual([['ngAfterContentInit', []]]);
});
it('should work with ngAfterViewChecked', () => {
// #docregion AfterViewChecked
@Component({selector: 'my-cmp', template: `...`})
class MyComponent implements AfterViewChecked {
ngAfterViewChecked() {
// ...
}
}
// #enddocregion
expect(createAndLogComponent(MyComponent)).toEqual([['ngAfterViewChecked', []]]);
});
it('should work with ngAfterViewInit', () => {
// #docregion AfterViewInit
@Component({selector: 'my-cmp', template: `...`})
class MyComponent implements AfterViewInit {
ngAfterViewInit() {
// ...
}
}
// #enddocregion
expect(createAndLogComponent(MyComponent)).toEqual([['ngAfterViewInit', []]]);
});
it('should work with ngOnDestroy', () => {
// #docregion OnDestroy
@Component({selector: 'my-cmp', template: `...`})
class MyComponent implements OnDestroy {
ngOnDestroy() {
// ...
}
}
// #enddocregion
expect(createAndLogComponent(MyComponent)).toEqual([['ngOnDestroy', []]]);
});
it('should work with ngOnChanges', () => {
// #docregion OnChanges
@Component({selector: 'my-cmp', template: `...`})
class MyComponent implements OnChanges {
@Input()
prop: number;
ngOnChanges(changes: SimpleChanges) {
// changes.prop contains the old and the new value...
}
}
// #enddocregion
const log = createAndLogComponent(MyComponent, ['prop']);
expect(log.length).toBe(1);
expect(log[0][0]).toBe('ngOnChanges');
const changes: SimpleChanges = log[0][1][0];
expect(changes['prop'].currentValue).toBe(true);
});
});
function createAndLogComponent(clazz: Type<any>, inputs: string[] = []): any[] {
const log: any[] = [];
createLoggingSpiesFromProto(clazz, log);
const inputBindings = inputs.map(input => `[${input}] = true`).join(' ');
@Component({template: `<my-cmp ${inputBindings}></my-cmp>`})
class ParentComponent {
}
const fixture = TestBed.configureTestingModule({declarations: [ParentComponent, clazz]})
.createComponent(ParentComponent);
fixture.detectChanges();
fixture.destroy();
return log;
}
function createLoggingSpiesFromProto(clazz: Type<any>, log: any[]) {
const proto = clazz.prototype;
Object.keys(proto).forEach((method) => {
proto[method] = (...args: any[]) => { log.push([method, args]); };
});
}
}

View File

@ -0,0 +1,51 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Attribute, Component, Directive, Pipe} from '@angular/core';
class CustomDirective {};
// #docregion component
@Component({selector: 'greet', template: 'Hello {{name}}!'})
class Greet {
name: string = 'World';
}
// #enddocregion
// #docregion attributeFactory
@Component({selector: 'page', template: 'Title: {{title}}'})
class Page {
title: string;
constructor(@Attribute('title') title: string) { this.title = title; }
}
// #enddocregion
// #docregion attributeMetadata
@Directive({selector: 'input'})
class InputAttrDirective {
constructor(@Attribute('type') type: string) {
// type would be 'text' in this example
}
}
// #enddocregion
// #docregion directive
@Directive({selector: 'input'})
class InputDirective {
constructor() {
// Add some logic.
}
}
// #enddocregion
// #docregion pipe
@Pipe({name: 'lowercase'})
class Lowercase {
transform(v: string, args: any[]) { return v.toLowerCase(); }
}
// #enddocregion

View File

@ -0,0 +1,19 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Component, createPlatformFactory} from '@angular/core';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
// #docregion longform
@Component({selector: 'my-app', template: 'Hello World'})
class MyApp {
}
const myPlatformFactory = createPlatformFactory(platformBrowserDynamic, 'myPlatform');
myPlatformFactory().bootstrapModule(MyApp);
// #enddocregion

View File

@ -0,0 +1,13 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Component} from '@angular/core';
@Component({selector: 'my-component', template: '<h1>My Component</h1>'})
export class MyComponent {
}

View File

@ -0,0 +1,20 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// #docregion enableProdMode
import {NgModule, enableProdMode} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {MyComponent} from './my_component';
enableProdMode();
@NgModule({imports: [BrowserModule], bootstrap: [MyComponent]})
class AppModule {
}
platformBrowserDynamic().bootstrapModule(AppModule);
// #enddocregion

View File

@ -0,0 +1,37 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ElementArrayFinder, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
describe('formBuilder example', () => {
afterEach(verifyNoBrowserErrors);
let inputs: ElementArrayFinder;
let paragraphs: ElementArrayFinder;
beforeEach(() => {
browser.get('/forms/ts/formBuilder/index.html');
inputs = element.all(by.css('input'));
paragraphs = element.all(by.css('p'));
});
it('should populate the UI with initial values', () => {
expect(inputs.get(0).getAttribute('value')).toEqual('Nancy');
expect(inputs.get(1).getAttribute('value')).toEqual('Drew');
});
it('should update the validation status', () => {
expect(paragraphs.get(1).getText()).toEqual('Validation status: VALID');
inputs.get(0).click();
inputs.get(0).clear();
inputs.get(0).sendKeys('a');
expect(paragraphs.get(1).getText()).toEqual('Validation status: INVALID');
});
});

View File

@ -0,0 +1,42 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// #docregion Component
import {Component, Inject} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
@Component({
selector: 'example-app',
template: `
<form [formGroup]="form">
<div formGroupName="name">
<input formControlName="first" placeholder="First">
<input formControlName="last" placeholder="Last">
</div>
<input formControlName="email" placeholder="Email">
<button>Submit</button>
</form>
<p>Value: {{ form.value | json }}</p>
<p>Validation status: {{ form.status }}</p>
`
})
export class FormBuilderComp {
form: FormGroup;
constructor(@Inject(FormBuilder) fb: FormBuilder) {
this.form = fb.group({
name: fb.group({
first: ['Nancy', Validators.minLength(2)],
last: 'Drew',
}),
email: '',
});
}
}
// #enddocregion

View File

@ -0,0 +1,20 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgModule} from '@angular/core';
import {ReactiveFormsModule} from '@angular/forms';
import {BrowserModule} from '@angular/platform-browser';
import {FormBuilderComp} from './form_builder_example';
@NgModule({
imports: [BrowserModule, ReactiveFormsModule],
declarations: [FormBuilderComp],
bootstrap: [FormBuilderComp]
})
export class AppModule {
}

View File

@ -0,0 +1,43 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ElementArrayFinder, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
describe('nestedFormArray example', () => {
afterEach(verifyNoBrowserErrors);
let inputs: ElementArrayFinder;
let buttons: ElementArrayFinder;
beforeEach(() => {
browser.get('/forms/ts/nestedFormArray/index.html');
inputs = element.all(by.css('input'));
buttons = element.all(by.css('button'));
});
it('should populate the UI with initial values', () => {
expect(inputs.get(0).getAttribute('value')).toEqual('SF');
expect(inputs.get(1).getAttribute('value')).toEqual('NY');
});
it('should add inputs programmatically', () => {
expect(inputs.count()).toBe(2);
buttons.get(1).click();
inputs = element.all(by.css('input'));
expect(inputs.count()).toBe(3);
});
it('should set the value programmatically', () => {
buttons.get(2).click();
expect(inputs.get(0).getAttribute('value')).toEqual('LA');
expect(inputs.get(1).getAttribute('value')).toEqual('MTV');
});
});

View File

@ -0,0 +1,20 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgModule} from '@angular/core';
import {ReactiveFormsModule} from '@angular/forms';
import {BrowserModule} from '@angular/platform-browser';
import {NestedFormArray} from './nested_form_array_example';
@NgModule({
imports: [BrowserModule, ReactiveFormsModule],
declarations: [NestedFormArray],
bootstrap: [NestedFormArray]
})
export class AppModule {
}

View File

@ -0,0 +1,49 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/* tslint:disable:no-console */
// #docregion Component
import {Component} from '@angular/core';
import {FormArray, FormControl, FormGroup} from '@angular/forms';
@Component({
selector: 'example-app',
template: `
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<div formArrayName="cities">
<div *ngFor="let city of cities.controls; let i=index">
<input [formControlName]="i" placeholder="City">
</div>
</div>
<button>Submit</button>
</form>
<button (click)="addCity()">Add City</button>
<button (click)="setPreset()">Set preset</button>
`,
})
export class NestedFormArray {
form = new FormGroup({
cities: new FormArray([
new FormControl('SF'),
new FormControl('NY'),
]),
});
get cities(): FormArray { return this.form.get('cities') as FormArray; }
addCity() { this.cities.push(new FormControl()); }
onSubmit() {
console.log(this.cities.value); // ['SF', 'NY']
console.log(this.form.value); // { cities: ['SF', 'NY'] }
}
setPreset() { this.cities.patchValue(['LA', 'MTV']); }
}
// #enddocregion

View File

@ -0,0 +1,44 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ElementFinder, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
describe('nestedFormGroup example', () => {
afterEach(verifyNoBrowserErrors);
let firstInput: ElementFinder;
let lastInput: ElementFinder;
let button: ElementFinder;
beforeEach(() => {
browser.get('/forms/ts/nestedFormGroup/index.html');
firstInput = element(by.css('[formControlName="first"]'));
lastInput = element(by.css('[formControlName="last"]'));
button = element(by.css('button:not([type="submit"])'));
});
it('should populate the UI with initial values', () => {
expect(firstInput.getAttribute('value')).toEqual('Nancy');
expect(lastInput.getAttribute('value')).toEqual('Drew');
});
it('should show the error when name is invalid', () => {
firstInput.click();
firstInput.clear();
firstInput.sendKeys('a');
expect(element(by.css('p')).getText()).toEqual('Name is invalid.');
});
it('should set the value programmatically', () => {
button.click();
expect(firstInput.getAttribute('value')).toEqual('Bess');
expect(lastInput.getAttribute('value')).toEqual('Marvin');
});
});

View File

@ -0,0 +1,20 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgModule} from '@angular/core';
import {ReactiveFormsModule} from '@angular/forms';
import {BrowserModule} from '@angular/platform-browser';
import {NestedFormGroupComp} from './nested_form_group_example';
@NgModule({
imports: [BrowserModule, ReactiveFormsModule],
declarations: [NestedFormGroupComp],
bootstrap: [NestedFormGroupComp]
})
export class AppModule {
}

View File

@ -0,0 +1,53 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/* tslint:disable:no-console */
// #docregion Component
import {Component} from '@angular/core';
import {FormControl, FormGroup, Validators} from '@angular/forms';
@Component({
selector: 'example-app',
template: `
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<p *ngIf="name.invalid">Name is invalid.</p>
<div formGroupName="name">
<input formControlName="first" placeholder="First name">
<input formControlName="last" placeholder="Last name">
</div>
<input formControlName="email" placeholder="Email">
<button type="submit">Submit</button>
</form>
<button (click)="setPreset()">Set preset</button>
`,
})
export class NestedFormGroupComp {
form = new FormGroup({
name: new FormGroup({
first: new FormControl('Nancy', Validators.minLength(2)),
last: new FormControl('Drew', Validators.required)
}),
email: new FormControl()
});
get first(): any { return this.form.get('name.first'); }
get name(): any { return this.form.get('name'); }
onSubmit() {
console.log(this.first.value); // 'Nancy'
console.log(this.name.value); // {first: 'Nancy', last: 'Drew'}
console.log(this.form.value); // {name: {first: 'Nancy', last: 'Drew'}, email: ''}
console.log(this.form.status); // VALID
}
setPreset() { this.name.setValue({first: 'Bess', last: 'Marvin'}); }
}
// #enddocregion

View File

@ -0,0 +1,42 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ElementArrayFinder, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
describe('ngModelGroup example', () => {
afterEach(verifyNoBrowserErrors);
let inputs: ElementArrayFinder;
let buttons: ElementArrayFinder;
beforeEach(() => {
browser.get('/forms/ts/ngModelGroup/index.html');
inputs = element.all(by.css('input'));
buttons = element.all(by.css('button'));
});
it('should populate the UI with initial values', () => {
expect(inputs.get(0).getAttribute('value')).toEqual('Nancy');
expect(inputs.get(1).getAttribute('value')).toEqual('Drew');
});
it('should show the error when name is invalid', () => {
inputs.get(0).click();
inputs.get(0).clear();
inputs.get(0).sendKeys('a');
expect(element(by.css('p')).getText()).toEqual('Name is invalid.');
});
it('should set the value when changing the domain model', () => {
buttons.get(1).click();
expect(inputs.get(0).getAttribute('value')).toEqual('Bess');
expect(inputs.get(1).getAttribute('value')).toEqual('Marvin');
});
});

View File

@ -0,0 +1,20 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {BrowserModule} from '@angular/platform-browser';
import {NgModelGroupComp} from './ng_model_group_example';
@NgModule({
imports: [BrowserModule, FormsModule],
declarations: [NgModelGroupComp],
bootstrap: [NgModelGroupComp]
})
export class AppModule {
}

View File

@ -0,0 +1,42 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/* tslint:disable:no-console */
// #docregion Component
import {Component} from '@angular/core';
import {NgForm} from '@angular/forms';
@Component({
selector: 'example-app',
template: `
<form #f="ngForm" (ngSubmit)="onSubmit(f)">
<p *ngIf="nameCtrl.invalid">Name is invalid.</p>
<div ngModelGroup="name" #nameCtrl="ngModelGroup">
<input name="first" [ngModel]="name.first" minlength="2">
<input name="last" [ngModel]="name.last" required>
</div>
<input name="email" ngModel>
<button>Submit</button>
</form>
<button (click)="setValue()">Set value</button>
`,
})
export class NgModelGroupComp {
name = {first: 'Nancy', last: 'Drew'};
onSubmit(f: NgForm) {
console.log(f.value); // {name: {first: 'Nancy', last: 'Drew'}, email: ''}
console.log(f.valid); // true
}
setValue() { this.name = {first: 'Bess', last: 'Marvin'}; }
}
// #enddocregion

View File

@ -0,0 +1,42 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ElementArrayFinder, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
describe('radioButtons example', () => {
afterEach(verifyNoBrowserErrors);
let inputs: ElementArrayFinder;
let paragraphs: ElementArrayFinder;
beforeEach(() => {
browser.get('/forms/ts/radioButtons/index.html');
inputs = element.all(by.css('input'));
paragraphs = element.all(by.css('p'));
});
it('should populate the UI with initial values', () => {
expect(inputs.get(0).getAttribute('checked')).toEqual(null);
expect(inputs.get(1).getAttribute('checked')).toEqual('true');
expect(inputs.get(2).getAttribute('checked')).toEqual(null);
expect(paragraphs.get(0).getText()).toEqual('Form value: { "food": "lamb" }');
expect(paragraphs.get(1).getText()).toEqual('myFood value: lamb');
});
it('update model and other buttons as the UI value changes', () => {
inputs.get(0).click();
expect(inputs.get(0).getAttribute('checked')).toEqual('true');
expect(inputs.get(1).getAttribute('checked')).toEqual(null);
expect(inputs.get(2).getAttribute('checked')).toEqual(null);
expect(paragraphs.get(0).getText()).toEqual('Form value: { "food": "beef" }');
expect(paragraphs.get(1).getText()).toEqual('myFood value: beef');
});
});

View File

@ -0,0 +1,20 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {BrowserModule} from '@angular/platform-browser';
import {RadioButtonComp} from './radio_button_example';
@NgModule({
imports: [BrowserModule, FormsModule],
declarations: [RadioButtonComp],
bootstrap: [RadioButtonComp]
})
export class AppModule {
}

View File

@ -0,0 +1,28 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// #docregion TemplateDriven
import {Component} from '@angular/core';
@Component({
selector: 'example-app',
template: `
<form #f="ngForm">
<input type="radio" value="beef" name="food" [(ngModel)]="myFood"> Beef
<input type="radio" value="lamb" name="food" [(ngModel)]="myFood"> Lamb
<input type="radio" value="fish" name="food" [(ngModel)]="myFood"> Fish
</form>
<p>Form value: {{ f.value | json }}</p> <!-- {food: 'lamb' } -->
<p>myFood value: {{ myFood }}</p> <!-- 'lamb' -->
`,
})
export class RadioButtonComp {
myFood = 'lamb';
}
// #enddocregion

View File

@ -0,0 +1,38 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ElementArrayFinder, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
describe('radioButtons example', () => {
afterEach(verifyNoBrowserErrors);
let inputs: ElementArrayFinder;
beforeEach(() => {
browser.get('/forms/ts/reactiveRadioButtons/index.html');
inputs = element.all(by.css('input'));
});
it('should populate the UI with initial values', () => {
expect(inputs.get(0).getAttribute('checked')).toEqual(null);
expect(inputs.get(1).getAttribute('checked')).toEqual('true');
expect(inputs.get(2).getAttribute('checked')).toEqual(null);
expect(element(by.css('p')).getText()).toEqual('Form value: { "food": "lamb" }');
});
it('update model and other buttons as the UI value changes', () => {
inputs.get(0).click();
expect(inputs.get(0).getAttribute('checked')).toEqual('true');
expect(inputs.get(1).getAttribute('checked')).toEqual(null);
expect(inputs.get(2).getAttribute('checked')).toEqual(null);
expect(element(by.css('p')).getText()).toEqual('Form value: { "food": "beef" }');
});
});

View File

@ -0,0 +1,20 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgModule} from '@angular/core';
import {ReactiveFormsModule} from '@angular/forms';
import {BrowserModule} from '@angular/platform-browser';
import {ReactiveRadioButtonComp} from './reactive_radio_button_example';
@NgModule({
imports: [BrowserModule, ReactiveFormsModule],
declarations: [ReactiveRadioButtonComp],
bootstrap: [ReactiveRadioButtonComp]
})
export class AppModule {
}

View File

@ -0,0 +1,30 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// #docregion Reactive
import {Component} from '@angular/core';
import {FormControl, FormGroup} from '@angular/forms';
@Component({
selector: 'example-app',
template: `
<form [formGroup]="form">
<input type="radio" formControlName="food" value="beef" > Beef
<input type="radio" formControlName="food" value="lamb"> Lamb
<input type="radio" formControlName="food" value="fish"> Fish
</form>
<p>Form value: {{ form.value | json }}</p> <!-- {food: 'lamb' } -->
`,
})
export class ReactiveRadioButtonComp {
form = new FormGroup({
food: new FormControl('lamb'),
});
}
// #enddocregion

View File

@ -0,0 +1,37 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ElementArrayFinder, ElementFinder, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
describe('reactiveSelectControl example', () => {
afterEach(verifyNoBrowserErrors);
let select: ElementFinder;
let options: ElementArrayFinder;
let p: ElementFinder;
beforeEach(() => {
browser.get('/forms/ts/reactiveSelectControl/index.html');
select = element(by.css('select'));
options = element.all(by.css('option'));
p = element(by.css('p'));
});
it('should populate the initial selection', () => {
expect(select.getAttribute('value')).toEqual('3: Object');
expect(options.get(3).getAttribute('selected')).toBe('true');
});
it('should update the model when the value changes in the UI', () => {
select.click();
options.get(0).click();
expect(p.getText()).toEqual('Form value: { "state": { "name": "Arizona", "abbrev": "AZ" } }');
});
});

View File

@ -0,0 +1,20 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgModule} from '@angular/core';
import {ReactiveFormsModule} from '@angular/forms';
import {BrowserModule} from '@angular/platform-browser';
import {ReactiveSelectComp} from './reactive_select_control_example';
@NgModule({
imports: [BrowserModule, ReactiveFormsModule],
declarations: [ReactiveSelectComp],
bootstrap: [ReactiveSelectComp]
})
export class AppModule {
}

View File

@ -0,0 +1,41 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// #docregion Component
import {Component} from '@angular/core';
import {FormControl, FormGroup} from '@angular/forms';
@Component({
selector: 'example-app',
template: `
<form [formGroup]="form">
<select formControlName="state">
<option *ngFor="let state of states" [ngValue]="state">
{{ state.abbrev }}
</option>
</select>
</form>
<p>Form value: {{ form.value | json }}</p>
<!-- {state: {name: 'New York', abbrev: 'NY'} } -->
`,
})
export class ReactiveSelectComp {
states = [
{name: 'Arizona', abbrev: 'AZ'},
{name: 'California', abbrev: 'CA'},
{name: 'Colorado', abbrev: 'CO'},
{name: 'New York', abbrev: 'NY'},
{name: 'Pennsylvania', abbrev: 'PA'},
];
form = new FormGroup({
state: new FormControl(this.states[3]),
});
}
// #enddocregion

View File

@ -0,0 +1,35 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ElementArrayFinder, ElementFinder, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
describe('selectControl example', () => {
afterEach(verifyNoBrowserErrors);
let select: ElementFinder;
let options: ElementArrayFinder;
let p: ElementFinder;
beforeEach(() => {
browser.get('/forms/ts/selectControl/index.html');
select = element(by.css('select'));
options = element.all(by.css('option'));
p = element(by.css('p'));
});
it('should initially select the placeholder option',
() => { expect(options.get(0).getAttribute('selected')).toBe('true'); });
it('should update the model when the value changes in the UI', () => {
select.click();
options.get(1).click();
expect(p.getText()).toEqual('Form value: { "state": { "name": "Arizona", "abbrev": "AZ" } }');
});
});

View File

@ -0,0 +1,20 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {BrowserModule} from '@angular/platform-browser';
import {SelectControlComp} from './select_control_example';
@NgModule({
imports: [BrowserModule, FormsModule],
declarations: [SelectControlComp],
bootstrap: [SelectControlComp]
})
export class AppModule {
}

View File

@ -0,0 +1,37 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// #docregion Component
import {Component} from '@angular/core';
@Component({
selector: 'example-app',
template: `
<form #f="ngForm">
<select name="state" ngModel>
<option value="" disabled>Choose a state</option>
<option *ngFor="let state of states" [ngValue]="state">
{{ state.abbrev }}
</option>
</select>
</form>
<p>Form value: {{ f.value | json }}</p>
<!-- example value: {state: {name: 'New York', abbrev: 'NY'} } -->
`,
})
export class SelectControlComp {
states = [
{name: 'Arizona', abbrev: 'AZ'},
{name: 'California', abbrev: 'CA'},
{name: 'Colorado', abbrev: 'CO'},
{name: 'New York', abbrev: 'NY'},
{name: 'Pennsylvania', abbrev: 'PA'},
];
}
// #enddocregion

View File

@ -0,0 +1,44 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ElementArrayFinder, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
describe('simpleForm example', () => {
afterEach(verifyNoBrowserErrors);
let inputs: ElementArrayFinder;
let paragraphs: ElementArrayFinder;
beforeEach(() => {
browser.get('/forms/ts/simpleForm/index.html');
inputs = element.all(by.css('input'));
paragraphs = element.all(by.css('p'));
});
it('should update the domain model as you type', () => {
inputs.get(0).click();
inputs.get(0).sendKeys('Nancy');
inputs.get(1).click();
inputs.get(1).sendKeys('Drew');
expect(paragraphs.get(0).getText()).toEqual('First name value: Nancy');
expect(paragraphs.get(2).getText()).toEqual('Form value: { "first": "Nancy", "last": "Drew" }');
});
it('should report the validity correctly', () => {
expect(paragraphs.get(1).getText()).toEqual('First name valid: false');
expect(paragraphs.get(3).getText()).toEqual('Form valid: false');
inputs.get(0).click();
inputs.get(0).sendKeys('a');
expect(paragraphs.get(1).getText()).toEqual('First name valid: true');
expect(paragraphs.get(3).getText()).toEqual('Form valid: true');
});
});

View File

@ -0,0 +1,20 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {BrowserModule} from '@angular/platform-browser';
import {SimpleFormComp} from './simple_form_example';
@NgModule({
imports: [BrowserModule, FormsModule],
declarations: [SimpleFormComp],
bootstrap: [SimpleFormComp]
})
export class AppModule {
}

View File

@ -0,0 +1,35 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/* tslint:disable:no-console */
// #docregion Component
import {Component} from '@angular/core';
import {NgForm} from '@angular/forms';
@Component({
selector: 'example-app',
template: `
<form #f="ngForm" (ngSubmit)="onSubmit(f)" novalidate>
<input name="first" ngModel required #first="ngModel">
<input name="last" ngModel>
<button>Submit</button>
</form>
<p>First name value: {{ first.value }}</p>
<p>First name valid: {{ first.valid }}</p>
<p>Form value: {{ f.value | json }}</p>
<p>Form valid: {{ f.valid }}</p>
`,
})
export class SimpleFormComp {
onSubmit(f: NgForm) {
console.log(f.value); // { first: '', last: '' }
console.log(f.valid); // false
}
}
// #enddocregion

View File

@ -0,0 +1,54 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ElementFinder, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
describe('simpleFormControl example', () => {
afterEach(verifyNoBrowserErrors);
describe('index view', () => {
let input: ElementFinder;
let valueP: ElementFinder;
let statusP: ElementFinder;
beforeEach(() => {
browser.get('/forms/ts/simpleFormControl/index.html');
input = element(by.css('input'));
valueP = element(by.css('p:first-of-type'));
statusP = element(by.css('p:last-of-type'));
});
it('should populate the form control value in the DOM', () => {
expect(input.getAttribute('value')).toEqual('value');
expect(valueP.getText()).toEqual('Value: value');
});
it('should update the value as user types', () => {
input.click();
input.sendKeys('s!');
expect(valueP.getText()).toEqual('Value: values!');
});
it('should show the correct validity state', () => {
expect(statusP.getText()).toEqual('Validation status: VALID');
input.click();
input.clear();
input.sendKeys('a');
expect(statusP.getText()).toEqual('Validation status: INVALID');
});
it('should set the value programmatically', () => {
element(by.css('button')).click();
expect(input.getAttribute('value')).toEqual('new value');
});
});
});

View File

@ -0,0 +1,20 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgModule} from '@angular/core';
import {ReactiveFormsModule} from '@angular/forms';
import {BrowserModule} from '@angular/platform-browser';
import {SimpleFormControl} from './simple_form_control_example';
@NgModule({
imports: [BrowserModule, ReactiveFormsModule],
declarations: [SimpleFormControl],
bootstrap: [SimpleFormControl]
})
export class AppModule {
}

View File

@ -0,0 +1,29 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// #docregion Component
import {Component} from '@angular/core';
import {FormControl, Validators} from '@angular/forms';
@Component({
selector: 'example-app',
template: `
<input [formControl]="control">
<p>Value: {{ control.value }}</p>
<p>Validation status: {{ control.status }}</p>
<button (click)="setValue()">Set value</button>
`,
})
export class SimpleFormControl {
control: FormControl = new FormControl('value', Validators.minLength(2));
setValue() { this.control.setValue('new value'); }
}
// #enddocregion

View File

@ -0,0 +1,45 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ElementFinder, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
describe('formControlName example', () => {
afterEach(verifyNoBrowserErrors);
describe('index view', () => {
let firstInput: ElementFinder;
let lastInput: ElementFinder;
beforeEach(() => {
browser.get('/forms/ts/simpleFormGroup/index.html');
firstInput = element(by.css('[formControlName="first"]'));
lastInput = element(by.css('[formControlName="last"]'));
});
it('should populate the form control values in the DOM', () => {
expect(firstInput.getAttribute('value')).toEqual('Nancy');
expect(lastInput.getAttribute('value')).toEqual('Drew');
});
it('should show the error when the form is invalid', () => {
firstInput.click();
firstInput.clear();
firstInput.sendKeys('a');
expect(element(by.css('div')).getText()).toEqual('Name is too short.');
});
it('should set the value programmatically', () => {
element(by.css('button:not([type="submit"])')).click();
expect(firstInput.getAttribute('value')).toEqual('Carson');
expect(lastInput.getAttribute('value')).toEqual('Drew');
});
});
});

View File

@ -0,0 +1,20 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgModule} from '@angular/core';
import {ReactiveFormsModule} from '@angular/forms';
import {BrowserModule} from '@angular/platform-browser';
import {SimpleFormGroup} from './simple_form_group_example';
@NgModule({
imports: [BrowserModule, ReactiveFormsModule],
declarations: [SimpleFormGroup],
bootstrap: [SimpleFormGroup]
})
export class AppModule {
}

View File

@ -0,0 +1,44 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/* tslint:disable:no-console */
// #docregion Component
import {Component} from '@angular/core';
import {FormControl, FormGroup, Validators} from '@angular/forms';
@Component({
selector: 'example-app',
template: `
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<div *ngIf="first.invalid"> Name is too short. </div>
<input formControlName="first" placeholder="First name">
<input formControlName="last" placeholder="Last name">
<button type="submit">Submit</button>
</form>
<button (click)="setValue()">Set preset value</button>
`,
})
export class SimpleFormGroup {
form = new FormGroup({
first: new FormControl('Nancy', Validators.minLength(2)),
last: new FormControl('Drew'),
});
get first(): any { return this.form.get('first'); }
onSubmit(): void {
console.log(this.form.value); // {first: 'Nancy', last: 'Drew'}
}
setValue() { this.form.setValue({first: 'Carson', last: 'Drew'}); }
}
// #enddocregion

View File

@ -0,0 +1,45 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ElementArrayFinder, ElementFinder, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
describe('simpleNgModel example', () => {
afterEach(verifyNoBrowserErrors);
let input: ElementFinder;
let paragraphs: ElementArrayFinder;
let button: ElementFinder;
beforeEach(() => {
browser.get('/forms/ts/simpleNgModel/index.html');
input = element(by.css('input'));
paragraphs = element.all(by.css('p'));
button = element(by.css('button'));
});
it('should update the domain model as you type', () => {
input.click();
input.sendKeys('Carson');
expect(paragraphs.get(0).getText()).toEqual('Value: Carson');
});
it('should report the validity correctly', () => {
expect(paragraphs.get(1).getText()).toEqual('Valid: false');
input.click();
input.sendKeys('a');
expect(paragraphs.get(1).getText()).toEqual('Valid: true');
});
it('should set the value by changing the domain model', () => {
button.click();
expect(input.getAttribute('value')).toEqual('Nancy');
});
});

View File

@ -0,0 +1,20 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {BrowserModule} from '@angular/platform-browser';
import {SimpleNgModelComp} from './simple_ng_model_example';
@NgModule({
imports: [BrowserModule, FormsModule],
declarations: [SimpleNgModelComp],
bootstrap: [SimpleNgModelComp]
})
export class AppModule {
}

View File

@ -0,0 +1,28 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// #docregion Component
import {Component} from '@angular/core';
@Component({
selector: 'example-app',
template: `
<input [(ngModel)]="name" #ctrl="ngModel" required>
<p>Value: {{ name }}</p>
<p>Valid: {{ ctrl.valid }}</p>
<button (click)="setValue()">Set value</button>
`,
})
export class SimpleNgModelComp {
name: string = '';
setValue() { this.name = 'Nancy'; }
}
// #enddocregion

View File

View File

@ -0,0 +1,25 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {DebugElement} from '@angular/core';
import {By} from '@angular/platform-browser';
let debugElement: DebugElement;
class MyDirective {}
// #docregion by_all
debugElement.query(By.all());
// #enddocregion
// #docregion by_css
debugElement.query(By.css('[attribute]'));
// #enddocregion
// #docregion by_directive
debugElement.query(By.directive(MyDirective));
// #enddocregion

View File

@ -0,0 +1,23 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Component, NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
@Component({selector: 'my-component'})
class MyAppComponent {
}
// #docregion providers
@NgModule({imports: [BrowserModule], bootstrap: [MyAppComponent]})
class AppModule {
}
platformBrowserDynamic().bootstrapModule(AppModule);
// #enddocregion

8
packages/examples/test.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/sh
cd `dirname $0`
./build.sh
gulp serve-examples &
(cd ../../../ && NODE_PATH=$NODE_PATH:dist/all $(npm bin)/protractor protractor-examples-e2e.conf.js --bundles=true)

View File

@ -0,0 +1,38 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {discardPeriodicTasks, fakeAsync, tick} from '@angular/core/testing';
// #docregion basic
describe('this test', () => {
it('looks async but is synchronous', <any>fakeAsync((): void => {
let flag = false;
setTimeout(() => { flag = true; }, 100);
expect(flag).toBe(false);
tick(50);
expect(flag).toBe(false);
tick(50);
expect(flag).toBe(true);
}));
});
// #enddocregion
// #docregion pending
describe('this test', () => {
it('aborts a periodic timer', <any>fakeAsync((): void => {
// This timer is scheduled but doesn't need to complete for the
// test to pass (maybe it's a timeout for some operation).
// Leaving it will cause the test to fail...
setInterval(() => {}, 100);
// Unless we clean it up first.
discardPeriodicTasks();
}));
});
// #enddocregion

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