Merge remote-tracking branch 'remotes/angular.io/master'
# Conflicts: # public/docs/ts/latest/_data.json # public/docs/ts/latest/cheatsheet.jade # public/news.jade
|
@ -378,7 +378,7 @@ gulp.task('help', taskListing.withFilters(function(taskName) {
|
|||
// requires admin access because it adds symlinks
|
||||
gulp.task('add-example-boilerplate', function() {
|
||||
var realPath = path.join(EXAMPLES_PATH, '/node_modules');
|
||||
var nodeModulesPaths = getNodeModulesPaths(EXAMPLES_PATH);
|
||||
var nodeModulesPaths = excludeDartPaths(getNodeModulesPaths(EXAMPLES_PATH));
|
||||
|
||||
nodeModulesPaths.forEach(function(linkPath) {
|
||||
gutil.log("symlinking " + linkPath + ' -> ' + realPath)
|
||||
|
@ -670,7 +670,10 @@ gulp.task('lint', function() {
|
|||
'!./public/docs/_examples/**/node_modules/**/*',
|
||||
'!./public/docs/_examples/_protractor/**/*',
|
||||
'!./public/docs/_examples/**/typings/**/*',
|
||||
'!./public/docs/_examples/**/typings-ng1/**/*'
|
||||
'!./public/docs/_examples/**/typings-ng1/**/*',
|
||||
// temporary until codelyzer is fixed mgechev/codelyzer#60
|
||||
'!./public/docs/_examples/animations/ts/app/hero.service.ts'
|
||||
|
||||
])
|
||||
.pipe(tslint({
|
||||
rulesDirectory: ['node_modules/codelyzer'],
|
||||
|
|
|
@ -0,0 +1,296 @@
|
|||
/// <reference path='../_protractor/e2e.d.ts' />
|
||||
/**
|
||||
* The tests here basically just checking that the end styles
|
||||
* of each animation are in effect.
|
||||
*
|
||||
* Relies on the Angular 2 testability only becoming stable once
|
||||
* animation(s) have finished.
|
||||
*
|
||||
* Ideally we'd use https://developer.mozilla.org/en-US/docs/Web/API/Document/getAnimations
|
||||
* but they're not supported in Chrome at the moment. The upcoming nganimate polyfill
|
||||
* may also add some introspection support.
|
||||
*/
|
||||
describe('Animation Tests', () => {
|
||||
|
||||
const INACTIVE_COLOR = 'rgba(238, 238, 238, 1)';
|
||||
const ACTIVE_COLOR = 'rgba(207, 216, 220, 1)';
|
||||
const NO_TRANSFORM_MATRIX_REGEX = /matrix\(1,\s*0,\s*0,\s*1,\s*0,\s*0\)/;
|
||||
|
||||
beforeEach(() => {
|
||||
browser.get('');
|
||||
});
|
||||
|
||||
describe('basic states', () => {
|
||||
|
||||
let host: protractor.ElementFinder;
|
||||
|
||||
beforeEach(() => {
|
||||
host = element(by.css('hero-list-basic'));
|
||||
});
|
||||
|
||||
it('animates between active and inactive', () => {
|
||||
addHero();
|
||||
|
||||
let li = host.element(by.css('li'));
|
||||
|
||||
expect(getScaleX(li)).toBe(1.0);
|
||||
expect(li.getCssValue('backgroundColor')).toBe(INACTIVE_COLOR);
|
||||
|
||||
li.click();
|
||||
browser.driver.sleep(300);
|
||||
expect(getScaleX(li)).toBe(1.1);
|
||||
expect(li.getCssValue('backgroundColor')).toBe(ACTIVE_COLOR);
|
||||
|
||||
li.click();
|
||||
browser.driver.sleep(300);
|
||||
expect(getScaleX(li)).toBe(1.0);
|
||||
expect(li.getCssValue('backgroundColor')).toBe(INACTIVE_COLOR);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('styles inline in transitions', () => {
|
||||
|
||||
let host: protractor.ElementFinder;
|
||||
|
||||
beforeEach(function() {
|
||||
host = element(by.css('hero-list-inline-styles'));
|
||||
});
|
||||
|
||||
it('are not kept after animation', () => {
|
||||
addHero();
|
||||
|
||||
let li = host.element(by.css('li'));
|
||||
|
||||
li.click();
|
||||
browser.driver.sleep(300);
|
||||
expect(getScaleX(li)).toBe(1.0);
|
||||
expect(li.getCssValue('backgroundColor')).toBe(INACTIVE_COLOR);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('combined transition syntax', () => {
|
||||
|
||||
let host: protractor.ElementFinder;
|
||||
|
||||
beforeEach(() => {
|
||||
host = element(by.css('hero-list-combined-transitions'));
|
||||
});
|
||||
|
||||
it('animates between active and inactive', () => {
|
||||
addHero();
|
||||
|
||||
let li = host.element(by.css('li'));
|
||||
|
||||
expect(getScaleX(li)).toBe(1.0);
|
||||
expect(li.getCssValue('backgroundColor')).toBe(INACTIVE_COLOR);
|
||||
|
||||
li.click();
|
||||
browser.driver.sleep(300);
|
||||
expect(getScaleX(li)).toBe(1.1);
|
||||
expect(li.getCssValue('backgroundColor')).toBe(ACTIVE_COLOR);
|
||||
|
||||
li.click();
|
||||
browser.driver.sleep(300);
|
||||
expect(getScaleX(li)).toBe(1.0);
|
||||
expect(li.getCssValue('backgroundColor')).toBe(INACTIVE_COLOR);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('two-way transition syntax', () => {
|
||||
|
||||
let host: protractor.ElementFinder;
|
||||
|
||||
beforeEach(() => {
|
||||
host = element(by.css('hero-list-twoway'));
|
||||
});
|
||||
|
||||
it('animates between active and inactive', () => {
|
||||
addHero();
|
||||
|
||||
let li = host.element(by.css('li'));
|
||||
|
||||
expect(getScaleX(li)).toBe(1.0);
|
||||
expect(li.getCssValue('backgroundColor')).toBe(INACTIVE_COLOR);
|
||||
|
||||
li.click();
|
||||
browser.driver.sleep(300);
|
||||
expect(getScaleX(li)).toBe(1.1);
|
||||
expect(li.getCssValue('backgroundColor')).toBe(ACTIVE_COLOR);
|
||||
|
||||
li.click();
|
||||
browser.driver.sleep(300);
|
||||
expect(getScaleX(li)).toBe(1.0);
|
||||
expect(li.getCssValue('backgroundColor')).toBe(INACTIVE_COLOR);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('enter & leave', () => {
|
||||
|
||||
let host: protractor.ElementFinder;
|
||||
|
||||
beforeEach(() => {
|
||||
host = element(by.css('hero-list-enter-leave'));
|
||||
});
|
||||
|
||||
it('adds and removes element', () => {
|
||||
addHero();
|
||||
|
||||
let li = host.element(by.css('li'));
|
||||
expect(li.getCssValue('transform')).toMatch(NO_TRANSFORM_MATRIX_REGEX);
|
||||
|
||||
removeHero();
|
||||
expect(li.isPresent()).toBe(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('enter & leave & states', () => {
|
||||
|
||||
let host: protractor.ElementFinder;
|
||||
|
||||
beforeEach(function() {
|
||||
host = element(by.css('hero-list-enter-leave-states'));
|
||||
});
|
||||
|
||||
it('adds and removes and animates between active and inactive', () => {
|
||||
addHero();
|
||||
|
||||
let li = host.element(by.css('li'));
|
||||
|
||||
expect(li.getCssValue('transform')).toMatch(NO_TRANSFORM_MATRIX_REGEX);
|
||||
|
||||
li.click();
|
||||
browser.driver.sleep(300);
|
||||
expect(getScaleX(li)).toBe(1.1);
|
||||
|
||||
li.click();
|
||||
browser.driver.sleep(300);
|
||||
expect(li.getCssValue('transform')).toMatch(NO_TRANSFORM_MATRIX_REGEX);
|
||||
|
||||
removeHero();
|
||||
expect(li.isPresent()).toBe(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('auto style calc', () => {
|
||||
|
||||
let host: protractor.ElementFinder;
|
||||
|
||||
beforeEach(function() {
|
||||
host = element(by.css('hero-list-auto'));
|
||||
});
|
||||
|
||||
it('adds and removes element', () => {
|
||||
addHero();
|
||||
|
||||
let li = host.element(by.css('li'));
|
||||
expect(li.getCssValue('height')).toBe('50px');
|
||||
|
||||
removeHero();
|
||||
expect(li.isPresent()).toBe(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('different timings', () => {
|
||||
|
||||
let host: protractor.ElementFinder;
|
||||
|
||||
beforeEach(() => {
|
||||
host = element(by.css('hero-list-timings'));
|
||||
});
|
||||
|
||||
it('adds and removes element', () => {
|
||||
addHero();
|
||||
|
||||
let li = host.element(by.css('li'));
|
||||
expect(li.getCssValue('transform')).toMatch(NO_TRANSFORM_MATRIX_REGEX);
|
||||
expect(li.getCssValue('opacity')).toMatch('1');
|
||||
|
||||
removeHero();
|
||||
expect(li.isPresent()).toBe(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('multiple keyframes', () => {
|
||||
|
||||
let host: protractor.ElementFinder;
|
||||
|
||||
beforeEach(() => {
|
||||
host = element(by.css('hero-list-multistep'));
|
||||
});
|
||||
|
||||
it('adds and removes element', () => {
|
||||
addHero();
|
||||
|
||||
let li = host.element(by.css('li'));
|
||||
expect(li.getCssValue('transform')).toMatch(NO_TRANSFORM_MATRIX_REGEX);
|
||||
expect(li.getCssValue('opacity')).toMatch('1');
|
||||
|
||||
removeHero();
|
||||
expect(li.isPresent()).toBe(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('parallel groups', () => {
|
||||
|
||||
let host: protractor.ElementFinder;
|
||||
|
||||
beforeEach(() => {
|
||||
host = element(by.css('hero-list-groups'));
|
||||
});
|
||||
|
||||
it('adds and removes element', () => {
|
||||
addHero();
|
||||
|
||||
let li = host.element(by.css('li'));
|
||||
expect(li.getCssValue('transform')).toMatch(NO_TRANSFORM_MATRIX_REGEX);
|
||||
expect(li.getCssValue('opacity')).toMatch('1');
|
||||
|
||||
removeHero();
|
||||
expect(li.isPresent()).toBe(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function addHero() {
|
||||
element(by.buttonText('Add hero')).click();
|
||||
browser.driver.sleep(500);
|
||||
}
|
||||
|
||||
function removeHero() {
|
||||
element(by.buttonText('Remove hero')).click();
|
||||
browser.driver.sleep(500);
|
||||
}
|
||||
|
||||
function getScaleX(el: protractor.ElementFinder) {
|
||||
return protractor.promise.all([
|
||||
getBoundingClientWidth(el),
|
||||
getOffsetWidth(el)
|
||||
]).then(function([clientWidth, offsetWidth]) {
|
||||
return clientWidth / offsetWidth;
|
||||
});
|
||||
}
|
||||
|
||||
function getBoundingClientWidth(el: protractor.ElementFinder): protractor.promise.Promise<number> {
|
||||
return browser.executeScript(
|
||||
'return arguments[0].getBoundingClientRect().width',
|
||||
el.getWebElement()
|
||||
);
|
||||
}
|
||||
|
||||
function getOffsetWidth(el: protractor.ElementFinder): protractor.promise.Promise<number> {
|
||||
return browser.executeScript(
|
||||
'return arguments[0].offsetWidth',
|
||||
el.getWebElement()
|
||||
);
|
||||
}
|
||||
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
**/*.js
|
|
@ -0,0 +1,46 @@
|
|||
import {
|
||||
Component,
|
||||
Input,
|
||||
trigger,
|
||||
state,
|
||||
style,
|
||||
animate,
|
||||
transition
|
||||
} from '@angular/core';
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'hero-list-auto',
|
||||
// #docregion template
|
||||
template: `
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes"
|
||||
@shrinkOut="'in'">
|
||||
{{hero.name}}
|
||||
</li>
|
||||
</ul>
|
||||
`,
|
||||
// #enddocregion template
|
||||
styleUrls: ['hero-list.component.css'],
|
||||
|
||||
/* When the element leaves (transition "in => void" occurs),
|
||||
* get the element's current computed height and animate
|
||||
* it down to 0.
|
||||
*/
|
||||
// #docregion animationdef
|
||||
animations: [
|
||||
trigger('shrinkOut', [
|
||||
state('in', style({height: '*'})),
|
||||
transition('* => void', [
|
||||
style({height: '*'}),
|
||||
animate(250, style({height: 0}))
|
||||
])
|
||||
])
|
||||
]
|
||||
// #enddocregion animationdef
|
||||
})
|
||||
export class HeroListAutoComponent {
|
||||
@Input() heroes: Heroes;
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
// #docplaster
|
||||
// #docregion
|
||||
// #docregion imports
|
||||
import {
|
||||
Component,
|
||||
Input,
|
||||
trigger,
|
||||
state,
|
||||
style,
|
||||
transition,
|
||||
animate
|
||||
} from '@angular/core';
|
||||
// #enddocregion imports
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'hero-list-basic',
|
||||
// #enddocregion
|
||||
/* The click event calls hero.toggleState(), which
|
||||
* causes the state of that hero to switch from
|
||||
* active to inactive or vice versa.
|
||||
*/
|
||||
// #docregion
|
||||
// #docregion template
|
||||
template: `
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes"
|
||||
@heroState="hero.state"
|
||||
(click)="hero.toggleState()">
|
||||
{{hero.name}}
|
||||
</li>
|
||||
</ul>
|
||||
`,
|
||||
// #enddocregion template
|
||||
styleUrls: ['hero-list.component.css'],
|
||||
// #enddocregion
|
||||
/**
|
||||
* Define two states, "inactive" and "active", and the end
|
||||
* styles that apply whenever the element is in those states.
|
||||
* Then define animations for transitioning between the states,
|
||||
* one in each direction
|
||||
*/
|
||||
// #docregion
|
||||
// #docregion animationdef
|
||||
animations: [
|
||||
trigger('heroState', [
|
||||
// #docregion states
|
||||
state('inactive', style({
|
||||
backgroundColor: '#eee',
|
||||
transform: 'scale(1)'
|
||||
})),
|
||||
state('active', style({
|
||||
backgroundColor: '#cfd8dc',
|
||||
transform: 'scale(1.1)'
|
||||
})),
|
||||
// #enddocregion states
|
||||
// #docregion transitions
|
||||
transition('inactive => active', animate('100ms ease-in')),
|
||||
transition('active => inactive', animate('100ms ease-out'))
|
||||
// #enddocregion transitions
|
||||
])
|
||||
]
|
||||
// #enddocregion animationdef
|
||||
})
|
||||
export class HeroListBasicComponent {
|
||||
@Input() heroes: Heroes;
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
// #docregion
|
||||
// #docregion imports
|
||||
import {
|
||||
Component,
|
||||
Input,
|
||||
trigger,
|
||||
state,
|
||||
style,
|
||||
transition,
|
||||
animate
|
||||
} from '@angular/core';
|
||||
// #enddocregion imports
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'hero-list-combined-transitions',
|
||||
// #docregion template
|
||||
template: `
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes"
|
||||
@heroState="hero.state"
|
||||
(click)="hero.toggleState()">
|
||||
{{hero.name}}
|
||||
</li>
|
||||
</ul>
|
||||
`,
|
||||
// #enddocregion template
|
||||
styleUrls: ['hero-list.component.css'],
|
||||
/*
|
||||
* Define two states, "inactive" and "active", and the end
|
||||
* styles that apply whenever the element is in those states.
|
||||
* Then define an animated transition between these two
|
||||
* states, in *both* directions.
|
||||
*/
|
||||
// #docregion animationdef
|
||||
animations: [
|
||||
trigger('heroState', [
|
||||
state('inactive', style({
|
||||
backgroundColor: '#eee',
|
||||
transform: 'scale(1)'
|
||||
})),
|
||||
state('active', style({
|
||||
backgroundColor: '#cfd8dc',
|
||||
transform: 'scale(1.1)'
|
||||
})),
|
||||
// #docregion transitions
|
||||
transition('inactive => active, active => inactive',
|
||||
animate('100ms ease-out'))
|
||||
// #enddocregion transitions
|
||||
])
|
||||
]
|
||||
// #enddocregion animationdef
|
||||
})
|
||||
export class HeroListCombinedTransitionsComponent {
|
||||
@Input() heroes: Heroes;
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
import {
|
||||
Component,
|
||||
Input,
|
||||
trigger,
|
||||
state,
|
||||
style,
|
||||
animate,
|
||||
transition
|
||||
} from '@angular/core';
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'hero-list-enter-leave-states',
|
||||
// #docregion template
|
||||
template: `
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes"
|
||||
(click)="hero.toggleState()"
|
||||
@heroState="hero.state">
|
||||
{{hero.name}}
|
||||
</li>
|
||||
</ul>
|
||||
`,
|
||||
// #enddocregion template
|
||||
styleUrls: ['hero-list.component.css'],
|
||||
/* The elements here have two possible states based
|
||||
* on the hero state, "active", or "inactive". We animate
|
||||
* six transitions: Between the two states in both directions,
|
||||
* and between each state and void. With this we can animate
|
||||
* the enter and leave of elements differently based on which
|
||||
* state they are in when they are added and removed.
|
||||
*/
|
||||
// #docregion animationdef
|
||||
animations: [
|
||||
trigger('heroState', [
|
||||
state('inactive', style({transform: 'translateX(0) scale(1)'})),
|
||||
state('active', style({transform: 'translateX(0) scale(1.1)'})),
|
||||
transition('inactive => active', animate('100ms ease-in')),
|
||||
transition('active => inactive', animate('100ms ease-out')),
|
||||
transition('void => inactive', [
|
||||
style({transform: 'translateX(-100%) scale(1)'}),
|
||||
animate(100)
|
||||
]),
|
||||
transition('inactive => void', [
|
||||
animate(100, style({transform: 'translateX(100%) scale(1)'}))
|
||||
]),
|
||||
transition('void => active', [
|
||||
style({transform: 'translateX(0) scale(0)'}),
|
||||
animate(200)
|
||||
]),
|
||||
transition('active => void', [
|
||||
animate(200, style({transform: 'translateX(0) scale(0)'}))
|
||||
])
|
||||
])
|
||||
]
|
||||
// #enddocregion animationdef
|
||||
})
|
||||
export class HeroListEnterLeaveStatesComponent {
|
||||
@Input() heroes: Heroes;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
import {
|
||||
Component,
|
||||
Input,
|
||||
trigger,
|
||||
state,
|
||||
style,
|
||||
animate,
|
||||
transition
|
||||
} from '@angular/core';
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'hero-list-enter-leave',
|
||||
// #docregion template
|
||||
template: `
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes"
|
||||
@flyInOut="'in'">
|
||||
{{hero.name}}
|
||||
</li>
|
||||
</ul>
|
||||
`,
|
||||
// #enddocregion template
|
||||
styleUrls: ['hero-list.component.css'],
|
||||
/* The element here always has the state "in" when it
|
||||
* is present. We animate two transitions: From void
|
||||
* to in and from in to void, to achieve an animated
|
||||
* enter and leave transition. The element enters from
|
||||
* the left and leaves to the right using translateX.
|
||||
*/
|
||||
// #docregion animationdef
|
||||
animations: [
|
||||
trigger('flyInOut', [
|
||||
state('in', style({transform: 'translateX(0)'})),
|
||||
transition('void => *', [
|
||||
style({transform: 'translateX(-100%)'}),
|
||||
animate(100)
|
||||
]),
|
||||
transition('* => void', [
|
||||
animate(100, style({transform: 'translateX(100%)'}))
|
||||
])
|
||||
])
|
||||
]
|
||||
// #enddocregion animationdef
|
||||
})
|
||||
export class HeroListEnterLeaveComponent {
|
||||
@Input() heroes: Heroes;
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
import {
|
||||
Component,
|
||||
Input,
|
||||
trigger,
|
||||
state,
|
||||
style,
|
||||
animate,
|
||||
transition,
|
||||
group
|
||||
} from '@angular/core';
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'hero-list-groups',
|
||||
template: `
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes"
|
||||
@flyInOut="'in'">
|
||||
{{hero.name}}
|
||||
</li>
|
||||
</ul>
|
||||
`,
|
||||
styleUrls: ['hero-list.component.css'],
|
||||
styles: [`
|
||||
li {
|
||||
padding: 0 !important;
|
||||
text-align: center;
|
||||
}
|
||||
`],
|
||||
/* The element here always has the state "in" when it
|
||||
* is present. We animate two transitions: From void
|
||||
* to in and from in to void, to achieve an animated
|
||||
* enter and leave transition.
|
||||
*
|
||||
* The transitions have *parallel group* that allow
|
||||
* animating several properties at the same time but
|
||||
* with different timing configurations. On enter
|
||||
* (void => *) we start the opacity animation 0.1s
|
||||
* earlier than the translation/width animation.
|
||||
* On leave (* => void) we do the opposite -
|
||||
* the translation/width animation begins immediately
|
||||
* and the opacity animation 0.1s later.
|
||||
*/
|
||||
// #docregion animationdef
|
||||
animations: [
|
||||
trigger('flyInOut', [
|
||||
state('in', style({width: 120, transform: 'translateX(0)', opacity: 1})),
|
||||
transition('void => *', [
|
||||
style({width: 10, transform: 'translateX(50px)', opacity: 0}),
|
||||
group([
|
||||
animate('0.3s 0.1s ease', style({
|
||||
transform: 'translateX(0)',
|
||||
width: 120
|
||||
})),
|
||||
animate('0.3s ease', style({
|
||||
opacity: 1
|
||||
}))
|
||||
])
|
||||
]),
|
||||
transition('* => void', [
|
||||
group([
|
||||
animate('0.3s ease', style({
|
||||
transform: 'translateX(50px)',
|
||||
width: 10
|
||||
})),
|
||||
animate('0.3s 0.2s ease', style({
|
||||
opacity: 0
|
||||
}))
|
||||
])
|
||||
])
|
||||
])
|
||||
]
|
||||
// #enddocregion animationdef
|
||||
})
|
||||
export class HeroListGroupsComponent {
|
||||
@Input() heroes: Heroes;
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
// #docregion
|
||||
// #docregion imports
|
||||
import {
|
||||
Component,
|
||||
Input,
|
||||
trigger,
|
||||
style,
|
||||
transition,
|
||||
animate
|
||||
} from '@angular/core';
|
||||
// #enddocregion imports
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'hero-list-inline-styles',
|
||||
// #docregion template
|
||||
template: `
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes"
|
||||
@heroState="hero.state"
|
||||
(click)="hero.toggleState()">
|
||||
{{hero.name}}
|
||||
</li>
|
||||
</ul>
|
||||
`,
|
||||
// #enddocregion template
|
||||
styleUrls: ['hero-list.component.css'],
|
||||
/**
|
||||
* Define two states, "inactive" and "active", and the end
|
||||
* styles that apply whenever the element is in those states.
|
||||
* Then define an animation for the inactive => active transition.
|
||||
* This animation has no end styles, but only styles that are
|
||||
* defined inline inside the transition and thus are only kept
|
||||
* as long as the animation is running.
|
||||
*/
|
||||
// #docregion animationdef
|
||||
animations: [
|
||||
trigger('heroState', [
|
||||
// #docregion transitions
|
||||
transition('inactive => active', [
|
||||
style({
|
||||
backgroundColor: '#cfd8dc',
|
||||
transform: 'scale(1.3)'
|
||||
}),
|
||||
animate('80ms ease-in', style({
|
||||
backgroundColor: '#eee',
|
||||
transform: 'scale(1)'
|
||||
}))
|
||||
]),
|
||||
// #enddocregion transitions
|
||||
])
|
||||
]
|
||||
// #enddocregion animationdef
|
||||
})
|
||||
export class HeroListInlineStylesComponent {
|
||||
@Input() heroes: Heroes;
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
import {
|
||||
Component,
|
||||
Input,
|
||||
trigger,
|
||||
state,
|
||||
style,
|
||||
animate,
|
||||
transition,
|
||||
keyframes
|
||||
} from '@angular/core';
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'hero-list-multistep',
|
||||
template: `
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes"
|
||||
@flyInOut="'in'">
|
||||
{{hero.name}}
|
||||
</li>
|
||||
</ul>
|
||||
`,
|
||||
styleUrls: ['hero-list.component.css'],
|
||||
/* The element here always has the state "in" when it
|
||||
* is present. We animate two transitions: From void
|
||||
* to in and from in to void, to achieve an animated
|
||||
* enter and leave transition. Each transition is
|
||||
* defined in terms of multiple keyframes, to give it
|
||||
* a bounce effect.
|
||||
*/
|
||||
// #docregion animationdef
|
||||
animations: [
|
||||
trigger('flyInOut', [
|
||||
state('in', style({transform: 'translateX(0)'})),
|
||||
transition('void => *', [
|
||||
animate(300, keyframes([
|
||||
style({opacity: 0, transform: 'translateX(-100%)', offset: 0}),
|
||||
style({opacity: 1, transform: 'translateX(15px)', offset: 0.3}),
|
||||
style({opacity: 1, transform: 'translateX(0)', offset: 1.0})
|
||||
]))
|
||||
]),
|
||||
transition('* => void', [
|
||||
animate(300, keyframes([
|
||||
style({opacity: 1, transform: 'translateX(0)', offset: 0}),
|
||||
style({opacity: 1, transform: 'translateX(-15px)', offset: 0.7}),
|
||||
style({opacity: 0, transform: 'translateX(100%)', offset: 1.0})
|
||||
]))
|
||||
])
|
||||
])
|
||||
]
|
||||
// #enddocregion animationdef
|
||||
})
|
||||
export class HeroListMultistepComponent {
|
||||
@Input() heroes: Heroes;
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
import {
|
||||
Component,
|
||||
Input,
|
||||
trigger,
|
||||
state,
|
||||
style,
|
||||
animate,
|
||||
transition
|
||||
} from '@angular/core';
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'hero-list-timings',
|
||||
template: `
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes"
|
||||
@flyInOut="'in'"
|
||||
(click)="hero.toggleState()">
|
||||
{{hero.name}}
|
||||
</li>
|
||||
</ul>
|
||||
`,
|
||||
styleUrls: ['hero-list.component.css'],
|
||||
/* The element here always has the state "in" when it
|
||||
* is present. We animate two transitions: From void
|
||||
* to in and from in to void, to achieve an animated
|
||||
* enter and leave transition. The element enters from
|
||||
* the left and leaves to the right using translateX,
|
||||
* and fades in/out using opacity. We use different easings
|
||||
* for enter and leave.
|
||||
*/
|
||||
// #docregion animationdef
|
||||
animations: [
|
||||
trigger('flyInOut', [
|
||||
state('in', style({opacity: 1, transform: 'translateX(0)'})),
|
||||
transition('void => *', [
|
||||
style({
|
||||
opacity: 0,
|
||||
transform: 'translateX(-100%)'
|
||||
}),
|
||||
animate('0.2s ease-in')
|
||||
]),
|
||||
transition('* => void', [
|
||||
animate('0.2s 10 ease-out', style({
|
||||
opacity: 0,
|
||||
transform: 'translateX(100%)'
|
||||
}))
|
||||
])
|
||||
])
|
||||
]
|
||||
// #enddocregion animationdef
|
||||
})
|
||||
export class HeroListTimingsComponent {
|
||||
@Input() heroes: Heroes;
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
// #docregion
|
||||
// #docregion imports
|
||||
import {
|
||||
Component,
|
||||
Input,
|
||||
trigger,
|
||||
state,
|
||||
style,
|
||||
transition,
|
||||
animate
|
||||
} from '@angular/core';
|
||||
// #enddocregion imports
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'hero-list-twoway',
|
||||
// #docregion template
|
||||
template: `
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes"
|
||||
@heroState="hero.state"
|
||||
(click)="hero.toggleState()">
|
||||
{{hero.name}}
|
||||
</li>
|
||||
</ul>
|
||||
`,
|
||||
// #enddocregion template
|
||||
styleUrls: ['hero-list.component.css'],
|
||||
/*
|
||||
* Define two states, "inactive" and "active", and the end
|
||||
* styles that apply whenever the element is in those states.
|
||||
* Then define an animated transition between these two
|
||||
* states, in *both* directions.
|
||||
*/
|
||||
// #docregion animationdef
|
||||
animations: [
|
||||
trigger('heroState', [
|
||||
state('inactive', style({
|
||||
backgroundColor: '#eee',
|
||||
transform: 'scale(1)'
|
||||
})),
|
||||
state('active', style({
|
||||
backgroundColor: '#cfd8dc',
|
||||
transform: 'scale(1.1)'
|
||||
})),
|
||||
// #docregion transitions
|
||||
transition('inactive <=> active', animate('100ms ease-out'))
|
||||
// #enddocregion transitions
|
||||
])
|
||||
]
|
||||
// #enddocregion animationdef
|
||||
})
|
||||
export class HeroListTwowayComponent {
|
||||
@Input() heroes: Heroes;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
display: block;
|
||||
width: 120px;
|
||||
line-height: 50px;
|
||||
padding: 0 10px;
|
||||
box-sizing: border-box;
|
||||
background-color: #eee;
|
||||
border-radius: 4px;
|
||||
margin: 10px;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.active {
|
||||
background-color: #cfd8dc;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.inactive {
|
||||
background-color: #eee;
|
||||
transform: scale(1);
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
import { HeroListBasicComponent } from './hero-list-basic.component';
|
||||
import { HeroListInlineStylesComponent } from './hero-list-inline-styles.component';
|
||||
import { HeroListEnterLeaveComponent } from './hero-list-enter-leave.component';
|
||||
import { HeroListEnterLeaveStatesComponent } from './hero-list-enter-leave-states.component';
|
||||
import { HeroListCombinedTransitionsComponent } from './hero-list-combined-transitions.component';
|
||||
import { HeroListTwowayComponent } from './hero-list-twoway.component';
|
||||
import { HeroListAutoComponent } from './hero-list-auto.component';
|
||||
import { HeroListGroupsComponent } from './hero-list-groups.component';
|
||||
import { HeroListMultistepComponent } from './hero-list-multistep.component';
|
||||
import { HeroListTimingsComponent } from './hero-list-timings.component';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-team-builder',
|
||||
template: `
|
||||
<div class="buttons">
|
||||
<button [disabled]="!heroes.canAdd()" (click)="heroes.add()">Add hero</button>
|
||||
<button [disabled]="!heroes.canRemove()" (click)="heroes.remove()">Remove hero</button>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<h4>Basic State</h4>
|
||||
<p>Switch between active/inactive on click.</p>
|
||||
<hero-list-basic [heroes]=heroes></hero-list-basic>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h4>Styles inline in transitions</h4>
|
||||
<p>Animated effect on click, no persistend end styles.</p>
|
||||
<hero-list-inline-styles [heroes]=heroes></hero-list-inline-styles>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h4>Combined transition syntax</h4>
|
||||
<p>Switch between active/inactive on click. Define just one transition used in both directions.</p>
|
||||
<hero-list-combined-transitions [heroes]=heroes></hero-list-combined-transitions>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h4>Two-way transition syntax</h4>
|
||||
<p>Switch between active/inactive on click. Define just one transition used in both directions using the <=> syntax.</p>
|
||||
<hero-list-twoway [heroes]=heroes></hero-list-twoway>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h4>Enter & Leave</h4>
|
||||
<p>Enter and leave animations using the void state.</p>
|
||||
<hero-list-enter-leave [heroes]=heroes></hero-list-enter-leave>
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<h4>Enter & Leave & States</h4>
|
||||
<p>
|
||||
Enter and leave animations combined with active/inactive state animations.
|
||||
Different enter and leave transitions depending on state.
|
||||
</p>
|
||||
<hero-list-enter-leave-states [heroes]=heroes></hero-list-enter-leave-states>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h4>Auto Style Calc</h4>
|
||||
<p>Leave animation from the current computed height using the auto-style value *.</p>
|
||||
<hero-list-auto [heroes]=heroes></hero-list-auto>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h4>Different Timings</h4>
|
||||
<p>Enter and leave animations with different easings, ease-in for enter, ease-out for leave.</p>
|
||||
<hero-list-timings [heroes]=heroes></hero-list-timings>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h4>Multiple Keyframes</h4>
|
||||
<p>Enter and leave animations with three keyframes in each, to give the transition some bounce.</p>
|
||||
<hero-list-multistep [heroes]=heroes></hero-list-multistep>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h4>Parallel Groups</h4>
|
||||
<p>Enter and leave animations with multiple properties animated in parallel with different timings.</p>
|
||||
<hero-list-groups [heroes]=heroes></hero-list-groups>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
styles: [`
|
||||
.buttons {
|
||||
text-align: center;
|
||||
}
|
||||
button {
|
||||
padding: 1.5em 3em;
|
||||
}
|
||||
.columns {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.column {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
}
|
||||
.column p {
|
||||
min-height: 6em;
|
||||
}
|
||||
`],
|
||||
directives: [
|
||||
HeroListBasicComponent,
|
||||
HeroListInlineStylesComponent,
|
||||
HeroListCombinedTransitionsComponent,
|
||||
HeroListTwowayComponent,
|
||||
HeroListEnterLeaveComponent,
|
||||
HeroListEnterLeaveStatesComponent,
|
||||
HeroListAutoComponent,
|
||||
HeroListTimingsComponent,
|
||||
HeroListMultistepComponent,
|
||||
HeroListGroupsComponent
|
||||
],
|
||||
providers: [Heroes]
|
||||
})
|
||||
export class HeroTeamBuilderComponent {
|
||||
constructor(private heroes: Heroes) { }
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
|
||||
class Hero {
|
||||
constructor(public name: string,
|
||||
public state = 'inactive') {
|
||||
}
|
||||
|
||||
toggleState() {
|
||||
this.state = (this.state === 'active' ? 'inactive' : 'active');
|
||||
}
|
||||
}
|
||||
|
||||
let ALL_HEROES = [
|
||||
'Wolverine',
|
||||
'Magneto',
|
||||
'Emma Frost',
|
||||
'Thing',
|
||||
'Kitty Pryde',
|
||||
'Nightcrawler',
|
||||
'Juggernaut',
|
||||
'Beast',
|
||||
'Captain America',
|
||||
'Spider-Man',
|
||||
'Puck',
|
||||
'Alex Wilder',
|
||||
'Doctor Strange'
|
||||
].map(name => new Hero(name));
|
||||
|
||||
@Injectable()
|
||||
export class Heroes implements Iterable<Hero> {
|
||||
|
||||
currentHeroes: Hero[] = [];
|
||||
|
||||
[Symbol.iterator]() {
|
||||
return this.currentHeroes.values();
|
||||
}
|
||||
|
||||
canAdd() {
|
||||
return this.currentHeroes.length < ALL_HEROES.length;
|
||||
}
|
||||
|
||||
canRemove() {
|
||||
return this.currentHeroes.length > 0;
|
||||
}
|
||||
|
||||
add() {
|
||||
this.currentHeroes.push(ALL_HEROES[this.currentHeroes.length]);
|
||||
}
|
||||
|
||||
remove() {
|
||||
this.currentHeroes.splice(this.currentHeroes.length - 1, 1);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
import { bootstrap } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { HeroTeamBuilderComponent } from './hero-team-builder.component';
|
||||
|
||||
bootstrap(HeroTeamBuilderComponent);
|
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Animations</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
||||
<!-- Polyfill for Web Animations -->
|
||||
<script src="https://npmcdn.com/web-animations-js@2.2.1"></script>
|
||||
|
||||
<!-- Polyfill(s) for older browsers -->
|
||||
<script src="node_modules/core-js/client/shim.min.js"></script>
|
||||
|
||||
<script src="node_modules/zone.js/dist/zone.js"></script>
|
||||
<script src="node_modules/reflect-metadata/Reflect.js"></script>
|
||||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
|
||||
<script src="systemjs.config.js"></script>
|
||||
<script>
|
||||
System.import('app').catch(function(err){ console.error(err); });
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1 style="visibility: hidden;">External H1 Title for E2E test</h1>
|
||||
<hero-team-builder></hero-team-builder>
|
||||
<button style="visibility: hidden;">External button for E2E test</button>
|
||||
<ul style="visibility: hidden;">
|
||||
<li>External list for E2E test</li>
|
||||
</ul>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"description": "Angular 2 Animations",
|
||||
"files":[
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js"
|
||||
]
|
||||
}
|
|
@ -10,6 +10,9 @@ dependencies:
|
|||
dart_to_js_script_rewriter: ^1.0.1
|
||||
transformers:
|
||||
- angular2:
|
||||
platform_directives: 'package:angular2/common.dart#CORE_DIRECTIVES'
|
||||
platform_directives:
|
||||
- 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
platform_pipes:
|
||||
- 'package:angular2/common.dart#COMMON_PIPES'
|
||||
entry_points: web/main.dart
|
||||
- dart_to_js_script_rewriter
|
||||
|
|
|
@ -10,6 +10,9 @@ dependencies:
|
|||
dart_to_js_script_rewriter: ^1.0.1
|
||||
transformers:
|
||||
- angular2:
|
||||
platform_directives: 'package:angular2/common.dart#CORE_DIRECTIVES'
|
||||
platform_directives:
|
||||
- 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
platform_pipes:
|
||||
- 'package:angular2/common.dart#COMMON_PIPES'
|
||||
entry_points: web/main.dart
|
||||
- dart_to_js_script_rewriter
|
||||
|
|
|
@ -10,6 +10,9 @@ dependencies:
|
|||
dart_to_js_script_rewriter: ^1.0.1
|
||||
transformers:
|
||||
- angular2:
|
||||
platform_directives: 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
platform_directives:
|
||||
- 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
platform_pipes:
|
||||
- 'package:angular2/common.dart#COMMON_PIPES'
|
||||
entry_points: web/main.dart
|
||||
- dart_to_js_script_rewriter
|
||||
|
|
|
@ -10,8 +10,10 @@ dependencies:
|
|||
dart_to_js_script_rewriter: ^1.0.1
|
||||
transformers:
|
||||
- angular2:
|
||||
platform_directives: 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
platform_pipes: 'package:angular2/common.dart#COMMON_PIPES'
|
||||
platform_directives:
|
||||
- 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
platform_pipes:
|
||||
- 'package:angular2/common.dart#COMMON_PIPES'
|
||||
entry_points: web/main.dart
|
||||
- dart_to_js_script_rewriter
|
||||
dev_dependencies:
|
||||
|
|
|
@ -10,6 +10,9 @@ dependencies:
|
|||
dart_to_js_script_rewriter: ^1.0.1
|
||||
transformers:
|
||||
- angular2:
|
||||
platform_directives: 'package:angular2/common.dart#CORE_DIRECTIVES'
|
||||
platform_directives:
|
||||
- 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
platform_pipes:
|
||||
- 'package:angular2/common.dart#COMMON_PIPES'
|
||||
entry_points: web/main.dart
|
||||
- dart_to_js_script_rewriter
|
||||
|
|
|
@ -11,7 +11,9 @@ dependencies:
|
|||
transformers:
|
||||
- angular2:
|
||||
platform_directives:
|
||||
- 'package:angular2/common.dart#CORE_DIRECTIVES'
|
||||
- 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
- 'package:angular2/common.dart#FORM_DIRECTIVES'
|
||||
platform_pipes:
|
||||
- 'package:angular2/common.dart#COMMON_PIPES'
|
||||
entry_points: web/main.dart
|
||||
- dart_to_js_script_rewriter
|
||||
|
|
|
@ -10,6 +10,9 @@ dependencies:
|
|||
dart_to_js_script_rewriter: ^1.0.1
|
||||
transformers:
|
||||
- angular2:
|
||||
platform_directives: 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
platform_directives:
|
||||
- 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
platform_pipes:
|
||||
- 'package:angular2/common.dart#COMMON_PIPES'
|
||||
entry_points: web/main.dart
|
||||
- dart_to_js_script_rewriter
|
||||
|
|
|
@ -11,7 +11,8 @@ dependencies:
|
|||
transformers:
|
||||
- angular2:
|
||||
platform_directives:
|
||||
- 'package:angular2/common.dart#CORE_DIRECTIVES'
|
||||
- 'package:angular2/common.dart#FORM_DIRECTIVES'
|
||||
- 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
platform_pipes:
|
||||
- 'package:angular2/common.dart#COMMON_PIPES'
|
||||
entry_points: web/main.dart
|
||||
- dart_to_js_script_rewriter
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
"@angular/router": "2.0.0-rc.2",
|
||||
"@angular/router-deprecated": "2.0.0-rc.2",
|
||||
"@angular/upgrade": "2.0.0-rc.2",
|
||||
"angular2-in-memory-web-api": "0.0.11",
|
||||
"angular2-in-memory-web-api": "0.0.12",
|
||||
"bootstrap": "^3.3.6",
|
||||
"core-js": "^2.4.0",
|
||||
"reflect-metadata": "^0.1.3",
|
||||
|
|
|
@ -10,7 +10,9 @@ dependencies:
|
|||
dart_to_js_script_rewriter: ^1.0.1
|
||||
transformers:
|
||||
- angular2:
|
||||
platform_directives: 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
platform_pipes: 'package:angular2/common.dart#COMMON_PIPES'
|
||||
platform_directives:
|
||||
- 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
platform_pipes:
|
||||
- 'package:angular2/common.dart#COMMON_PIPES'
|
||||
entry_points: web/main.dart
|
||||
- dart_to_js_script_rewriter
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
# #docplaster
|
||||
# #docregion
|
||||
# #docregion no-rewriter
|
||||
name: angular2_getting_started
|
||||
description: QuickStart
|
||||
version: 0.0.1
|
||||
|
@ -9,11 +7,12 @@ environment:
|
|||
dependencies:
|
||||
angular2: 2.0.0-beta.17
|
||||
browser: ^0.10.0
|
||||
# #enddocregion no-rewriter
|
||||
dart_to_js_script_rewriter: ^1.0.1
|
||||
# #docregion no-rewriter
|
||||
transformers:
|
||||
- angular2:
|
||||
platform_directives:
|
||||
- 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
platform_pipes:
|
||||
- 'package:angular2/common.dart#COMMON_PIPES'
|
||||
entry_points: web/main.dart
|
||||
# #enddocregion no-rewriter
|
||||
- dart_to_js_script_rewriter
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
"rxjs": "5.0.0-beta.6",
|
||||
"zone.js": "0.6.12",
|
||||
|
||||
"angular2-in-memory-web-api": "0.0.11",
|
||||
"angular2-in-memory-web-api": "0.0.12",
|
||||
"bootstrap": "^3.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
"rxjs": "5.0.0-beta.6",
|
||||
"zone.js": "^0.6.12",
|
||||
|
||||
"angular2-in-memory-web-api": "0.0.11",
|
||||
"angular2-in-memory-web-api": "0.0.12",
|
||||
"bootstrap": "^3.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
// Bundled (~40 requests):
|
||||
function packUmd(pkgName) {
|
||||
packages['@angular/'+pkgName] = { main: pkgName + '.umd.js', defaultExtension: 'js' };
|
||||
packages['@angular/'+pkgName] = { main: '/bundles/' + pkgName + '.umd.js', defaultExtension: 'js' };
|
||||
}
|
||||
|
||||
// Most environments should use UMD; some (Karma) need the individual index files
|
||||
|
|
|
@ -16,10 +16,10 @@ dependencies:
|
|||
transformers:
|
||||
- angular2:
|
||||
platform_directives:
|
||||
- 'package:angular2/common.dart#CORE_DIRECTIVES'
|
||||
- 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
platform_pipes:
|
||||
- 'package:angular2/common.dart#COMMON_PIPES'
|
||||
entry_points: 'web/main.dart'
|
||||
entry_points: web/main.dart
|
||||
resolved_identifiers:
|
||||
BrowserClient: 'package:http/browser_client.dart'
|
||||
- dart_to_js_script_rewriter
|
||||
|
|
|
@ -10,6 +10,9 @@ dependencies:
|
|||
dart_to_js_script_rewriter: ^1.0.1
|
||||
transformers:
|
||||
- angular2:
|
||||
platform_directives: 'package:angular2/common.dart#CORE_DIRECTIVES'
|
||||
platform_directives:
|
||||
- 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
platform_pipes:
|
||||
- 'package:angular2/common.dart#COMMON_PIPES'
|
||||
entry_points: web/main.dart
|
||||
- dart_to_js_script_rewriter
|
||||
|
|
|
@ -10,9 +10,10 @@ dependencies:
|
|||
dart_to_js_script_rewriter: ^1.0.1
|
||||
transformers:
|
||||
- angular2:
|
||||
platform_pipes: 'package:angular2/common.dart#COMMON_PIPES'
|
||||
platform_directives:
|
||||
- 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
- 'package:angular2/common.dart#FORM_DIRECTIVES'
|
||||
platform_pipes:
|
||||
- 'package:angular2/common.dart#COMMON_PIPES'
|
||||
entry_points: web/main.dart
|
||||
- dart_to_js_script_rewriter
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# #docregion
|
||||
name: angular2_tour_of_heroes
|
||||
description: Tour of Heroes
|
||||
version: 0.0.1
|
||||
environment:
|
||||
sdk: '>=1.13.0 <2.0.0'
|
||||
|
@ -10,7 +11,8 @@ dependencies:
|
|||
transformers:
|
||||
- angular2:
|
||||
platform_directives:
|
||||
- package:angular2/common.dart#COMMON_DIRECTIVES
|
||||
- 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
platform_pipes:
|
||||
- package:angular2/common.dart#COMMON_PIPES
|
||||
- 'package:angular2/common.dart#COMMON_PIPES'
|
||||
entry_points: web/main.dart
|
||||
- dart_to_js_script_rewriter
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# #docregion
|
||||
name: angular2_tour_of_heroes
|
||||
description: Tour of Heroes
|
||||
version: 0.0.1
|
||||
environment:
|
||||
sdk: '>=1.13.0 <2.0.0'
|
||||
|
@ -9,7 +11,8 @@ dependencies:
|
|||
transformers:
|
||||
- angular2:
|
||||
platform_directives:
|
||||
- package:angular2/common.dart#COMMON_DIRECTIVES
|
||||
- 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
platform_pipes:
|
||||
- package:angular2/common.dart#COMMON_PIPES
|
||||
- 'package:angular2/common.dart#COMMON_PIPES'
|
||||
entry_points: web/main.dart
|
||||
- dart_to_js_script_rewriter
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# #docregion
|
||||
name: angular2_tour_of_heroes
|
||||
description: Tour of Heroes
|
||||
version: 0.0.1
|
||||
environment:
|
||||
sdk: '>=1.13.0 <2.0.0'
|
||||
|
@ -9,7 +11,8 @@ dependencies:
|
|||
transformers:
|
||||
- angular2:
|
||||
platform_directives:
|
||||
- package:angular2/common.dart#COMMON_DIRECTIVES
|
||||
- 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
platform_pipes:
|
||||
- package:angular2/common.dart#COMMON_PIPES
|
||||
- 'package:angular2/common.dart#COMMON_PIPES'
|
||||
entry_points: web/main.dart
|
||||
- dart_to_js_script_rewriter
|
||||
|
|
|
@ -8,7 +8,6 @@ import 'hero.dart';
|
|||
import 'hero_detail_component.dart';
|
||||
// #docregion hero-service-import
|
||||
import 'hero_service.dart';
|
||||
|
||||
// #enddocregion hero-service-import
|
||||
|
||||
@Component(
|
||||
|
@ -92,17 +91,17 @@ class AppComponent implements OnInit {
|
|||
|
||||
AppComponent(this._heroService);
|
||||
|
||||
// #docregion get-heroes
|
||||
getHeroes() async {
|
||||
// #docregion get-heroes
|
||||
Future<Null> getHeroes() async {
|
||||
heroes = await _heroService.getHeroes();
|
||||
}
|
||||
// #enddocregion get-heroes
|
||||
// #enddocregion get-heroes
|
||||
|
||||
ngOnInit() {
|
||||
void ngOnInit() {
|
||||
getHeroes();
|
||||
}
|
||||
|
||||
onSelect(Hero hero) {
|
||||
void onSelect(Hero hero) {
|
||||
selectedHero = hero;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,9 +5,7 @@ import 'package:angular2/core.dart';
|
|||
// #enddocregion on-init
|
||||
import 'hero.dart';
|
||||
import 'hero_detail_component.dart';
|
||||
// #docregion hero-service-import
|
||||
import 'hero_service_1.dart';
|
||||
// #enddocregion hero-service-import
|
||||
|
||||
// Testable but never shown
|
||||
@Component(
|
||||
|
@ -21,7 +19,7 @@ import 'hero_service_1.dart';
|
|||
directives: const [HeroDetailComponent],
|
||||
// #docregion providers
|
||||
providers: const [HeroService])
|
||||
// #enddocregion providers
|
||||
// #enddocregion providers
|
||||
// #docregion on-init
|
||||
class AppComponent implements OnInit {
|
||||
// #enddocregion on-init
|
||||
|
@ -38,27 +36,25 @@ class AppComponent implements OnInit {
|
|||
final HeroService _heroService;
|
||||
AppComponent(this._heroService);
|
||||
// #enddocregion ctor
|
||||
|
||||
// #docregion getHeroes
|
||||
getHeroes() {
|
||||
//#docregion get-heroes
|
||||
void getHeroes() {
|
||||
// #docregion get-heroes
|
||||
heroes = _heroService.getHeroes();
|
||||
// #enddocregion get-heroes
|
||||
}
|
||||
// #enddocregion getHeroes
|
||||
|
||||
// #docregion ng-on-init
|
||||
// #docregion on-init
|
||||
ngOnInit() {
|
||||
// #docregion ng-on-init, on-init
|
||||
void ngOnInit() {
|
||||
// #enddocregion on-init
|
||||
getHeroes();
|
||||
// #docregion on-init
|
||||
}
|
||||
// #enddocregion on-init
|
||||
// #enddocregion ng-on-init
|
||||
// #enddocregion ng-on-init, on-init
|
||||
|
||||
onSelect(Hero hero) {
|
||||
void onSelect(Hero hero) {
|
||||
selectedHero = hero;
|
||||
}
|
||||
// #docregion on-init
|
||||
}
|
||||
// #enddocregion on-init
|
||||
|
|
|
@ -14,8 +14,8 @@ import 'hero.dart';
|
|||
<input [(ngModel)]="hero.name" placeholder="name"/>
|
||||
</div>
|
||||
</div>
|
||||
''',
|
||||
inputs: const ['hero'])
|
||||
''')
|
||||
class HeroDetailComponent {
|
||||
@Input()
|
||||
Hero hero;
|
||||
}
|
||||
|
|
|
@ -12,15 +12,15 @@ import 'mock_heroes.dart';
|
|||
class HeroService {
|
||||
// #docregion get-heroes
|
||||
Future<List<Hero>> getHeroes() async => mockHeroes;
|
||||
// #enddocregion get-heroes
|
||||
// #enddocregion just-get-heroes
|
||||
// #enddocregion get-heroes, just-get-heroes
|
||||
// #enddocregion
|
||||
// See the "Take it slow" appendix
|
||||
// #docregion get-heroes-slowly
|
||||
Future<List<Hero>> getHeroesSlowly() {
|
||||
return new Future.delayed(const Duration(seconds: 2), () => mockHeroes);
|
||||
}
|
||||
// #enddocregion get-heroes-slowly
|
||||
// #docregion
|
||||
// #docregion just-get-heroes
|
||||
}
|
||||
// #enddocregion just-get-heroes
|
||||
// #enddocregion
|
||||
|
||||
|
|
|
@ -10,19 +10,13 @@ import 'mock_heroes.dart';
|
|||
// #docregion getHeroes-stub
|
||||
@Injectable()
|
||||
class HeroService {
|
||||
// #enddocregion getHeroes-stub
|
||||
// #enddocregion empty-class
|
||||
// #enddocregion final
|
||||
// #enddocregion getHeroes-stub, empty-class, final
|
||||
/*
|
||||
// #docregion getHeroes-stub
|
||||
// #docregion getHeroes-stub
|
||||
List<Hero> getHeroes() {}
|
||||
// #enddocregion getHeroes-stub
|
||||
*/
|
||||
// #docregion final
|
||||
// #enddocregion getHeroes-stub
|
||||
*/
|
||||
// #docregion final
|
||||
List<Hero> getHeroes() => mockHeroes;
|
||||
// #docregion empty-class
|
||||
// #docregion getHeroes-stub
|
||||
// #docregion empty-class, getHeroes-stub
|
||||
}
|
||||
// #enddocregion getHeroes-stub
|
||||
// #enddocregion empty-class
|
||||
// #enddocregion final
|
||||
|
|
|
@ -13,4 +13,3 @@ final List<Hero> mockHeroes = [
|
|||
new Hero(19, 'Magma'),
|
||||
new Hero(20, 'Tornado')
|
||||
];
|
||||
// #enddocregion
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# #docregion
|
||||
name: angular2_tour_of_heroes
|
||||
description: Tour of Heroes
|
||||
version: 0.0.1
|
||||
environment:
|
||||
sdk: '>=1.13.0 <2.0.0'
|
||||
|
@ -9,7 +11,8 @@ dependencies:
|
|||
transformers:
|
||||
- angular2:
|
||||
platform_directives:
|
||||
- package:angular2/common.dart#COMMON_DIRECTIVES
|
||||
- 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
platform_pipes:
|
||||
- package:angular2/common.dart#COMMON_PIPES
|
||||
- 'package:angular2/common.dart#COMMON_PIPES'
|
||||
entry_points: web/main.dart
|
||||
- dart_to_js_script_rewriter
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
// #docregion pt1
|
||||
import 'package:angular2/platform/browser.dart';
|
||||
|
||||
import 'package:angular2_tour_of_heroes/app_component.dart';
|
||||
|
||||
main() {
|
||||
void main() {
|
||||
bootstrap(AppComponent);
|
||||
}
|
||||
// #enddocregion pt1
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
// #docregion pt1
|
||||
import 'package:angular2/platform/browser.dart';
|
||||
|
||||
import 'package:angular2_tour_of_heroes/app_component_1.dart';
|
||||
|
||||
main() {
|
||||
void main() {
|
||||
bootstrap(AppComponent);
|
||||
}
|
||||
// #enddocregion pt1
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# #docregion
|
||||
name: angular2_tour_of_heroes
|
||||
description: Tour of Heroes 5
|
||||
description: Tour of Heroes
|
||||
version: 0.0.1
|
||||
environment:
|
||||
sdk: '>=1.13.0 <2.0.0'
|
||||
|
@ -11,8 +11,8 @@ dependencies:
|
|||
transformers:
|
||||
- angular2:
|
||||
platform_directives:
|
||||
- package:angular2/common.dart#COMMON_DIRECTIVES
|
||||
- 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
platform_pipes:
|
||||
- package:angular2/common.dart#COMMON_PIPES
|
||||
- 'package:angular2/common.dart#COMMON_PIPES'
|
||||
entry_points: web/main.dart
|
||||
- dart_to_js_script_rewriter
|
||||
|
|
|
@ -10,6 +10,9 @@ dependencies:
|
|||
dart_to_js_script_rewriter: ^1.0.1
|
||||
transformers:
|
||||
- angular2:
|
||||
platform_directives: 'package:angular2/common.dart#CORE_DIRECTIVES'
|
||||
platform_directives:
|
||||
- 'package:angular2/common.dart#COMMON_DIRECTIVES'
|
||||
platform_pipes:
|
||||
- 'package:angular2/common.dart#COMMON_PIPES'
|
||||
entry_points: web/main.dart
|
||||
- dart_to_js_script_rewriter
|
||||
|
|
|
@ -10,13 +10,13 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@angular/common": "2.0.0-rc.1",
|
||||
"@angular/compiler": "2.0.0-rc.1",
|
||||
"@angular/core": "2.0.0-rc.1",
|
||||
"@angular/http": "2.0.0-rc.1",
|
||||
"@angular/platform-browser": "2.0.0-rc.1",
|
||||
"@angular/platform-browser-dynamic": "2.0.0-rc.1",
|
||||
"@angular/router-deprecated": "2.0.0-rc.1",
|
||||
"@angular/common": "2.0.0-rc.2",
|
||||
"@angular/compiler": "2.0.0-rc.2",
|
||||
"@angular/core": "2.0.0-rc.2",
|
||||
"@angular/http": "2.0.0-rc.2",
|
||||
"@angular/platform-browser": "2.0.0-rc.2",
|
||||
"@angular/platform-browser-dynamic": "2.0.0-rc.2",
|
||||
"@angular/router-deprecated": "2.0.0-rc.2",
|
||||
"core-js": "^2.4.0",
|
||||
"reflect-metadata": "0.1.2",
|
||||
"rxjs": "5.0.0-beta.6",
|
||||
|
@ -40,10 +40,10 @@
|
|||
"rimraf": "^2.5.2",
|
||||
"style-loader": "^0.13.1",
|
||||
"ts-loader": "^0.8.1",
|
||||
"typescript": "^1.8.9",
|
||||
"typescript": "^1.8.10",
|
||||
"typings": "^1.0.4",
|
||||
"webpack": "^1.12.14",
|
||||
"webpack": "^1.13.0",
|
||||
"webpack-dev-server": "^1.14.1",
|
||||
"webpack-merge": "^0.9.0"
|
||||
"webpack-merge": "^0.14.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,10 +9,5 @@
|
|||
"removeComments": false,
|
||||
"noImplicitAny": true,
|
||||
"suppressImplicitAnyIndexErrors": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"typings/main",
|
||||
"typings/main.d.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,13 +15,13 @@
|
|||
"tutorial": {
|
||||
"icon": "list",
|
||||
"title": "Tutorial",
|
||||
"banner": "Angular 2 is currently in Beta."
|
||||
"banner": "Angular 2 is currently in Release Candidate."
|
||||
},
|
||||
|
||||
"guide": {
|
||||
"icon": "list",
|
||||
"title": "Developer Guides",
|
||||
"banner": "Angular 2 is currently in Beta."
|
||||
"banner": "Angular 2 is currently in Release Candidate."
|
||||
},
|
||||
|
||||
"cookbook": {
|
||||
|
@ -51,7 +51,7 @@
|
|||
"resources": {
|
||||
"icon": "play-circle-fill",
|
||||
"title": "Angular Resources",
|
||||
"banner": "Angular 2 is currently in Beta.",
|
||||
"banner": "Angular 2 is currently in Release Candidate.",
|
||||
"resources": true
|
||||
},
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- var base = current.path[4] ? '.' : './guide';
|
||||
.banner.grid-fluid
|
||||
p.text-body.c10 This cheat sheet is provisional and may change. Angular 2 is currently in Beta.
|
||||
p.text-body.c10 This cheat sheet is provisional and may change. Angular 2 is currently in Release Candidate.
|
||||
|
||||
article(class="l-content-small grid-fluid docs-content")
|
||||
.cheatsheet
|
||||
|
|
|
@ -13,7 +13,7 @@ include ../_util-fns
|
|||
It also makes it easier to unit test the component with a mock service.
|
||||
|
||||
Because data services are invariably asynchronous,
|
||||
we'll finish the chapter with a promise-based version of the data service.
|
||||
we'll finish the chapter with a **!{_Promise}**-based version of the data service.
|
||||
|
||||
p Run the #[+liveExampleLink2('', 'toh-4')] for this part.
|
||||
|
||||
|
@ -35,6 +35,7 @@ p Run the #[+liveExampleLink2('', 'toh-4')] for this part.
|
|||
.children
|
||||
.file index.html
|
||||
.file main.dart
|
||||
.file styles.css
|
||||
.file pubspec.yaml
|
||||
:marked
|
||||
### Keep the app compiling and running
|
||||
|
@ -59,7 +60,7 @@ code-example(language="bash").
|
|||
First, defining heroes is not the component's job.
|
||||
Second, we can't easily share that list of heroes with other components and views.
|
||||
|
||||
We can refactor this hero data acquisition business to a single service that provides heroes and
|
||||
We can refactor this hero data acquisition business to a single service that provides heroes, and
|
||||
share that service with all components that need heroes.
|
||||
|
||||
### Create the HeroService
|
||||
|
@ -67,12 +68,12 @@ code-example(language="bash").
|
|||
.l-sub-section
|
||||
:marked
|
||||
We've adopted a convention in which we spell the name of a service in lowercase followed by `_service`.
|
||||
If the service name were multi-word, we'd spell the base filename with lower underscore case (AKA "snake_case").
|
||||
If the service name were multi-word, we'd spell the base filename in lower underscore case (also called [snake_case](../guide/glossary.html#!#snake_case)).
|
||||
The `SpecialSuperHeroService` would be defined in the `special_super_hero_service.dart` file.
|
||||
:marked
|
||||
We name the class `HeroService`.
|
||||
|
||||
+makeExample('toh-4/dart/lib/hero_service_1.dart', 'empty-class', 'hero_service.dart (class)')(format=".")
|
||||
+makeExample('toh-4/dart/lib/hero_service_1.dart', 'empty-class', 'lib/hero_service.dart (starting point)')(format=".")
|
||||
|
||||
:marked
|
||||
### Injectable Services
|
||||
|
@ -91,7 +92,7 @@ code-example(language="bash").
|
|||
:marked
|
||||
### Getting Heroes
|
||||
Add a `getHeroes` method stub.
|
||||
+makeExample('toh-4/dart/lib/hero_service_1.dart', 'getHeroes-stub', 'hero_service.dart (getHeroes stub)')(format=".")
|
||||
+makeExample('toh-4/dart/lib/hero_service_1.dart', 'getHeroes-stub', 'lib/hero_service.dart (getHeroes stub)')(format=".")
|
||||
:marked
|
||||
We're holding back on the implementation for a moment to make an important point.
|
||||
|
||||
|
@ -109,31 +110,31 @@ code-example(language="bash").
|
|||
We already have mock `Hero` data sitting in the `AppComponent`. It doesn't belong there. It doesn't belong *here* either.
|
||||
We'll move the mock data to its own file.
|
||||
|
||||
Cut the the `mockHeroes` list from `app_component.dart` and paste it to a new file in the `lib` folder named `mock_heroes.dart`.
|
||||
Cut the `mockHeroes` list from `app_component.dart` and paste it to a new file in the `lib` folder named `mock_heroes.dart`.
|
||||
We copy the `import 'hero.dart'` statement as well because the heroes list uses the `Hero` class.
|
||||
|
||||
+makeExample('toh-4/dart/lib/mock_heroes.dart', null, 'mock_heroes.dart (Heroes list)')
|
||||
+makeExample('toh-4/dart/lib/mock_heroes.dart', null, 'lib/mock_heroes.dart')
|
||||
:marked
|
||||
Meanwhile, back in `app_component.dart` where we cut away the `mockHeroes` list,
|
||||
we leave behind an uninitialized `heroes` property:
|
||||
+makeExample('toh-4/dart/lib/app_component_1.dart', 'heroes-prop', 'app_component.dart (heroes property)')(format=".")
|
||||
+makeExample('toh-4/dart/lib/app_component_1.dart', 'heroes-prop', 'lib/app_component.dart (heroes property)')(format=".")
|
||||
:marked
|
||||
### Return Mocked Heroes
|
||||
Back in the `HeroService` we import the mock `mockHeroes` and return it from the `getHeroes` method.
|
||||
Our `HeroService` looks like this:
|
||||
+makeExample('toh-4/dart/lib/hero_service_1.dart', 'final', 'hero_service.dart')(format=".")
|
||||
+makeExample('toh-4/dart/lib/hero_service_1.dart', 'final', 'lib/hero_service.dart')(format=".")
|
||||
:marked
|
||||
### Use the Hero Service
|
||||
We're ready to use the `HeroService` in other components starting with our `AppComponent`.
|
||||
|
||||
We begin, as usual, by importing the thing we want to use, the `HeroService`.
|
||||
+makeExample('toh-4/dart/lib/app_component.dart', 'hero-service-import', 'app_component.dart (import HeroService)')(format=".")
|
||||
+makeExcerpt('toh-4/dart/lib/app_component.dart', 'hero-service-import')
|
||||
:marked
|
||||
Importing the service allows us to *reference* it in our code.
|
||||
How should the `AppComponent` acquire a runtime concrete `HeroService` instance?
|
||||
|
||||
### Do we *new* the *HeroService*? No way!
|
||||
We could create a new instance of the `HeroService` with "new" like this:
|
||||
We could create a new instance of the `HeroService` with `new` like this:
|
||||
+makeExample('toh-4/dart/lib/app_component_1.dart', 'new-service')(format=".")
|
||||
:marked
|
||||
That's a bad idea for several reasons including
|
||||
|
@ -143,7 +144,7 @@ code-example(language="bash").
|
|||
we'll have to find every place we create the service and fix it.
|
||||
Running around patching code is error prone and adds to the test burden.
|
||||
|
||||
* We create a new service each time we use "new".
|
||||
* We create a new service each time we use `new`.
|
||||
What if the service should cache heroes and share that cache with others?
|
||||
We couldn't do that.
|
||||
|
||||
|
@ -166,7 +167,7 @@ code-example(language="bash").
|
|||
1. We add to the component's `providers` metadata.
|
||||
|
||||
Here are the property and the constructor:
|
||||
+makeExample('toh-4/dart/lib/app_component_1.dart', 'ctor', 'app_component.dart (constructor)')(format='.')
|
||||
+makeExample('toh-4/dart/lib/app_component_1.dart', 'ctor', 'lib/app_component.dart (constructor)')(format='.')
|
||||
:marked
|
||||
The constructor does nothing except set the `_heroService`
|
||||
property. The `HeroService` type of `_heroService`
|
||||
|
@ -186,14 +187,14 @@ code-example(language="bash").
|
|||
:marked
|
||||
The *injector* does not know yet how to create a `HeroService`.
|
||||
If we ran our code now, Angular would fail with an error:
|
||||
code-example(format="." language="html").
|
||||
code-example(format="nocode").
|
||||
EXCEPTION: No provider for HeroService! (AppComponent -> HeroService)
|
||||
:marked
|
||||
We have to teach the *injector* how to make a `HeroService` by registering a `HeroService` **provider**.
|
||||
Do that by adding the following `providers` parameter to the bottom of the component metadata
|
||||
in the `@Component` annotation.
|
||||
|
||||
+makeExample('toh-4/dart/lib/app_component_1.dart', 'providers', 'app_component.dart (providing HeroService)')(format=".")
|
||||
+makeExcerpt('toh-4/dart/lib/app_component_1.dart', 'providers')
|
||||
:marked
|
||||
The `providers` parameter tells Angular to create a fresh instance of the `HeroService` when it creates a new `AppComponent`.
|
||||
The `AppComponent` can use that service to get heroes and so can every child component of its component tree.
|
||||
|
@ -207,7 +208,7 @@ code-example(format="." language="html").
|
|||
|
||||
If the `HeroDetailComponent` needed its parent component's `HeroService`,
|
||||
it would ask Angular to inject the service into its constructor which would look just like the one for `AppComponent`:
|
||||
+makeExample('toh-4/dart/lib/app_component_1.dart', 'ctor', 'hero_detail_component.dart (constructor)')(format=".")
|
||||
+makeExample('toh-4/dart/lib/app_component_1.dart', 'ctor', 'lib/hero_detail_component.dart (constructor)')(format=".")
|
||||
:marked
|
||||
The `HeroDetailComponent` must *not* repeat its parent's `providers` list! Guess [why](#shadow-provider).
|
||||
|
||||
|
@ -221,7 +222,7 @@ code-example(format="." language="html").
|
|||
+makeExample('toh-4/dart/lib/app_component_1.dart', 'get-heroes')(format=".")
|
||||
:marked
|
||||
We don't really need a dedicated method to wrap one line. We write it anyway:
|
||||
+makeExample('toh-4/dart/lib/app_component_1.dart', 'getHeroes', 'app_component.dart (getHeroes)')(format=".")
|
||||
+makeExcerpt('toh-4/dart/lib/app_component_1.dart', 'getHeroes')
|
||||
|
||||
<a id="oninit"></a>
|
||||
:marked
|
||||
|
@ -248,18 +249,19 @@ code-example(format="." language="html").
|
|||
Learn more about lifecycle hooks in the [Lifecycle Hooks](../guide/lifecycle-hooks.html) chapter.
|
||||
:marked
|
||||
Here's the essential outline for the `OnInit` interface:
|
||||
+makeExample('toh-4/dart/lib/app_component_1.dart', 'on-init', 'app_component.dart (OnInit protocol)')(format=".")
|
||||
+makeExample('toh-4/dart/lib/app_component_1.dart', 'on-init', 'lib/app_component.dart (ngOnInit stub)')(format=".")
|
||||
:marked
|
||||
We write an `ngOnInit` method with our initialization logic inside and leave it to Angular to call it
|
||||
at the right time. In our case, we initialize by calling `getHeroes`.
|
||||
+makeExample('toh-4/dart/lib/app_component_1.dart', 'ng-on-init', 'app_component.dart (OnInit protocol)')(format=".")
|
||||
+makeExcerpt('toh-4/dart/lib/app_component_1.dart', 'ng-on-init')
|
||||
:marked
|
||||
Our application should be running as expected, showing a list of heroes and a hero detail view
|
||||
when we click on a hero name.
|
||||
|
||||
We're getting closer. But something isn't quite right.
|
||||
|
||||
## Async Services and Futures
|
||||
<a id="async"></a>
|
||||
## Async Services and !{_Promise}
|
||||
Our `HeroService` returns a list of mock heroes immediately.
|
||||
Its `getHeroes` signature is synchronous
|
||||
+makeExample('toh-4/dart/lib/app_component_1.dart', 'get-heroes')(format=".")
|
||||
|
@ -273,42 +275,42 @@ code-example(format="." language="html").
|
|||
|
||||
We'll have to use some kind of asynchronous technique and that will change the signature of our `getHeroes` method.
|
||||
|
||||
We'll use *futures*.
|
||||
We'll use *!{_Promise}s*.
|
||||
|
||||
### The Hero Service returns a future
|
||||
### The Hero Service returns a !{_Promise}
|
||||
|
||||
We ask an asynchronous service to do some work and give us the result in the future.
|
||||
The service does that work (somewhere) and eventually it updates the future with the results of the work or an error.
|
||||
We ask an asynchronous service to do some work and give us the result in the !{_Promise}.
|
||||
The service does that work (somewhere) and eventually it updates the !{_Promise} with the results of the work or an error.
|
||||
.l-sub-section
|
||||
:marked
|
||||
We are simplifying. Learn about Futures in the tutorial
|
||||
We are simplifying. Learn about !{_Promise}s in the tutorial
|
||||
[Asynchronous Programming: Futures](https://www.dartlang.org/docs/tutorials/futures/).
|
||||
:marked
|
||||
Update the `HeroService` with this future-returning `getHeroes` method:
|
||||
+makeExample('toh-4/dart/lib/hero_service.dart', 'get-heroes', 'hero_service.dart (getHeroes)')(format=".")
|
||||
Update the `HeroService` with this !{_Promise}-returning `getHeroes` method:
|
||||
+makeExample('toh-4/dart/lib/hero_service.dart', 'get-heroes', 'lib/hero_service.dart (excerpt)')(format=".")
|
||||
:marked
|
||||
We're still mocking the data. We're simulating the behavior of an ultra-fast, zero-latency server,
|
||||
by returning a future that will quickly resolve with our mock heroes as the result.
|
||||
by returning a !{_Promise} that will quickly resolve with our mock heroes as the result.
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
Marking the method's body with `async` makes the method immediately return a `Future` object.
|
||||
That future later completes with the method's return value.
|
||||
That !{_Promise} later completes with the method's return value.
|
||||
For more information on async functions, see
|
||||
[Declaring async functions](https://www.dartlang.org/docs/dart-up-and-running/ch02.html#async) in the Dart language tour.
|
||||
|
||||
:marked
|
||||
### Act on the Futures
|
||||
### Act on the !{_Promise}
|
||||
Returning to the `AppComponent` and its `getHeroes` method, we see that it still looks like this:
|
||||
+makeExample('toh-4/dart/lib/app_component_1.dart', 'getHeroes', 'app_component.dart (getHeroes - old)')(format=".")
|
||||
+makeExample('toh-4/dart/lib/app_component_1.dart', 'getHeroes', 'lib/app_component.dart (getHeroes - old)')(format=".")
|
||||
:marked
|
||||
As a result of our change to `HeroService`, we're now setting `heroes` to a future rather than a list of heroes.
|
||||
As a result of our change to `HeroService`, we're now setting `heroes` to a !{_Promise} rather than a list of heroes.
|
||||
|
||||
We have to change our implementation to *act on the future when it resolves*.
|
||||
We can *await* the future to resolve, and then display the heroes:
|
||||
+makeExample('toh-4/dart/lib/app_component.dart', 'get-heroes', 'app_component.dart (getHeroes - revised)')(format=".")
|
||||
We have to change our implementation to *act on the !{_Promise} when it resolves*.
|
||||
We can *await* for the !{_Promise} to resolve, and then display the heroes:
|
||||
+makeExample('toh-4/dart/lib/app_component.dart', 'get-heroes', 'lib/app_component.dart (getHeroes - revised)')(format=".")
|
||||
:marked
|
||||
Our code waits until the future completes, and then
|
||||
Our code waits until the !{_Promise} completes, and then
|
||||
sets the component's `heroes` property to the list of heroes returned by the service. That's all there is to it!
|
||||
|
||||
Our app should still be running, still showing a list of heroes, and still
|
||||
|
@ -334,6 +336,7 @@ code-example(format="." language="html").
|
|||
.children
|
||||
.file index.html
|
||||
.file main.dart
|
||||
.file styles.css
|
||||
.file pubspec.yaml
|
||||
:marked
|
||||
Here are the code files we discussed in this chapter.
|
||||
|
@ -351,11 +354,11 @@ code-example(format="." language="html").
|
|||
## The Road We’ve Travelled
|
||||
Let’s take stock of what we’ve built.
|
||||
|
||||
* We created a service class that can be shared by many components
|
||||
* We used the `ngOnInit` Lifecycle Hook to get our heroes when our `AppComponent` activates
|
||||
* We defined our `HeroService` as a provider for our `AppComponent`
|
||||
* We created mock hero data and imported them into our service
|
||||
* We designed our service to return a future and our component to get our data from the future
|
||||
* We created a service class that can be shared by many components.
|
||||
* We used the `ngOnInit` Lifecycle Hook to get our heroes when our `AppComponent` activates.
|
||||
* We defined our `HeroService` as a provider for our `AppComponent`.
|
||||
* We created mock hero data and imported them into our service.
|
||||
* We designed our service to return a !{_Promise} and our component to get our data from the !{_Promise}.
|
||||
|
||||
|
||||
### The Road Ahead
|
||||
|
@ -373,10 +376,10 @@ code-example(format="." language="html").
|
|||
We can simulate a slow connection.
|
||||
|
||||
Add the following `getHeroesSlowly` method to the `HeroService`:
|
||||
+makeExample('toh-4/dart/lib/hero_service.dart', 'get-heroes-slowly', 'hero_service.dart (getHeroesSlowly)')(format=".")
|
||||
+makeExample('toh-4/dart/lib/hero_service.dart', 'get-heroes-slowly', 'lib/hero_service.dart (getHeroesSlowly)')(format=".")
|
||||
:marked
|
||||
Like `getHeroes`, it also returns a future.
|
||||
But this future waits 2 seconds before resolving the future with mock heroes.
|
||||
Like `getHeroes`, it also returns a !{_Promise}.
|
||||
But this !{_Promise} waits 2 seconds before resolving the !{_Promise} with mock heroes.
|
||||
|
||||
Back in the `AppComponent`, replace
|
||||
`_heroService.getHeroes` with `_heroService.getHeroesSlowly`
|
||||
|
|
|
@ -15,13 +15,13 @@
|
|||
"tutorial": {
|
||||
"icon": "list",
|
||||
"title": "Tutorial",
|
||||
"banner": "Angular 2 is currently in Beta."
|
||||
"banner": "Angular 2 is currently in Release Candidate."
|
||||
},
|
||||
|
||||
"guide": {
|
||||
"icon": "list",
|
||||
"title": "Developer Guides",
|
||||
"banner": "Angular 2 is currently in Beta."
|
||||
"banner": "Angular 2 is currently in Release Candidate."
|
||||
},
|
||||
|
||||
"cookbook": {
|
||||
|
@ -51,7 +51,7 @@
|
|||
"resources": {
|
||||
"icon": "play-circle-fill",
|
||||
"title": "Angular Resources",
|
||||
"banner": "Angular 2 is currently in Beta.",
|
||||
"banner": "Angular 2 is currently in Release Candidate.",
|
||||
"resources": true
|
||||
},
|
||||
|
||||
|
|
|
@ -22,13 +22,13 @@
|
|||
"tutorial": {
|
||||
"icon": "list",
|
||||
"title": "教程",
|
||||
"banner": "Angular 2正处于Beta阶段。"
|
||||
"banner": "Angular 2正处于候选版本阶段。"
|
||||
},
|
||||
|
||||
"guide": {
|
||||
"icon": "list",
|
||||
"title": "开发指南",
|
||||
"banner": "Angular 2正处于Beta阶段。"
|
||||
"banner": "Angular 2正处于候选版本阶段。"
|
||||
},
|
||||
|
||||
"cookbook": {
|
||||
|
@ -58,7 +58,7 @@
|
|||
"resources": {
|
||||
"icon": "play-circle-fill",
|
||||
"title": "Angular资源",
|
||||
"banner": "Angular 2正处于Beta阶段",
|
||||
"banner": "Angular 2正处于候选版本阶段",
|
||||
"resources": true
|
||||
},
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
- var base = current.path[4] ? '.' : './guide';
|
||||
.banner
|
||||
p.text-body This cheat sheet is provisional and may change. Angular 2 is currently in Beta.
|
||||
p.text-body 本小抄是临时的,将来可能改变。Angular 2目前还是Beta状态。
|
||||
p.text-body 本小抄是临时的,将来可能改变。Angular 2目前在候选版本阶段。
|
||||
|
||||
article(class="l-content-small grid-fluid docs-content")
|
||||
.cheatsheet
|
||||
|
|
|
@ -63,6 +63,11 @@
|
|||
"basics": true
|
||||
},
|
||||
|
||||
"animations": {
|
||||
"title": "Animations",
|
||||
"intro": "A guide to Angular's animation system."
|
||||
},
|
||||
|
||||
"attribute-directives": {
|
||||
"title": "属性型指令",
|
||||
"intro": "属性型指令把行为添加到现有元素上。"
|
||||
|
|
|
@ -0,0 +1,339 @@
|
|||
include ../_util-fns
|
||||
|
||||
:marked
|
||||
Motion is an important aspect in the design of modern web applications. We want our
|
||||
user interfaces to have smooth transitions between states, and engaging animations
|
||||
that call attention where it's needed. Well-designed animations can make a UI not only
|
||||
more fun but also easier to use.
|
||||
|
||||
Angular's animation system gives us what we need to make the kinds of animations we want.
|
||||
We can build animations that run with the same kind of native performance that we're used
|
||||
to with pure CSS animations. But we can also have our animation logic tightly integrated
|
||||
with the rest of our application code, where they can be easily triggered and controlled.
|
||||
|
||||
.alert.is-helpful
|
||||
:marked
|
||||
Angular animations are built on top of the standard [Web Animations API](https://w3c.github.io/web-animations/)
|
||||
and they run natively on [browsers that support it](http://caniuse.com/#feat=web-animation).
|
||||
|
||||
For other browsers, a polyfill is required. Grab
|
||||
[`web-animations.min.js` from here](https://github.com/web-animations/web-animations-js) and
|
||||
add it to your page.
|
||||
|
||||
A more lightweight polyfill maintained by the Angular team is coming soon.
|
||||
|
||||
:marked
|
||||
# Table of Contents
|
||||
|
||||
* [Quickstart Example: Transitioning Between Two States](#example-transitioning-between-states)
|
||||
* [States and Transitions](#states-and-transitions)
|
||||
* [Example: Entering and Leaving](#example-entering-and-leaving)
|
||||
* [Example: Entering and Leaving from Different States](#example-entering-and-leaving-from-different-states)
|
||||
* [Animatable Properties and Units](#animatable-properties-and-units)
|
||||
* [Automatic Property Calculation](#automatic-property-calculation)
|
||||
* [Animation Timing](#animation-timing)
|
||||
* [Multi-Step Animations with Keyframes](#multi-step-animations-with-keyframes)
|
||||
* [Parallel Animation Groups](#parallel-animation-groups)
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
The examples referenced in this chapter are available as a [live example](/resources/live-examples/animations/ts/plnkr.html).
|
||||
|
||||
a(id="example-transitioning-between-states")
|
||||
.l-main-section
|
||||
:marked
|
||||
## Quickstart Example: Transitioning Between Two States
|
||||
figure
|
||||
img(src="/resources/images/devguide/animations/animation_basic_click.gif" alt="A simple transition animation" align="right" style="width:220px;margin-left:20px" )
|
||||
:marked
|
||||
Let's build a simple animation that transitions an element between two states
|
||||
driven by a model attribute.
|
||||
|
||||
Animations are defined inside `@Component` metadata. Before we can add some, we need
|
||||
to import a few animation-specific functions:
|
||||
|
||||
+makeExample('animations/ts/app/hero-list-basic.component.ts', 'imports')(format=".")
|
||||
|
||||
:marked
|
||||
With these we can now define an *animation trigger* called `heroState` in the component
|
||||
metadata. It has animated transitions between two states: `active` and `inactive`. When a
|
||||
hero is active, we display a the element in slightly larger size and lighter color.
|
||||
|
||||
+makeExample('animations/ts/app/hero-list-basic.component.ts', 'animationdef')(format=".")
|
||||
|
||||
.alert.is-helpful
|
||||
:marked
|
||||
In this example we are defining animation styles (color and transform) inline in the
|
||||
animation metadata. In an upcoming release of Angular, support will be added for pulling
|
||||
the styles in from the component CSS stylesheet instead.
|
||||
|
||||
:marked
|
||||
We now have an animation defined but it is not yet used anywhere. We can change that by
|
||||
attaching it to one or more elements in the component's template using the "`@triggerName`"
|
||||
syntax:
|
||||
|
||||
+makeExample('animations/ts/app/hero-list-basic.component.ts', 'template')(format=".")
|
||||
|
||||
:marked
|
||||
Here we've applied the animation trigger to every element repeated by an `ngFor`. Each of
|
||||
the repeated elements will animate independently. We're binding the value of the
|
||||
attribute to the expression `hero.state`. We expect it to always be either `inactive`
|
||||
or `active`, since that's what we have defined animation states for.
|
||||
|
||||
With this setup, an animated transition is shown whenever a hero object changes state!
|
||||
Here's the full component implementation:
|
||||
|
||||
+makeExample('animations/ts/app/hero-list-basic.component.ts')
|
||||
|
||||
:marked
|
||||
## States and Transitions
|
||||
|
||||
Angular animations are defined in terms of logical **states** and **transitions**
|
||||
between states.
|
||||
|
||||
An animation state is a string value that we define in our application code. In the example
|
||||
above we used the states `'active'` and `'inactive'` based on the logical state of
|
||||
hero objects. The source of the state can be a simple object attribute as it was in this case,
|
||||
or it can be a value computed in a method. The important thing is that we can read it into the
|
||||
component's template.
|
||||
|
||||
We can define *styles* for each animation state:
|
||||
|
||||
+makeExample('animations/ts/app/hero-list-basic.component.ts', 'states')(format=".")
|
||||
|
||||
:marked
|
||||
These `state` definitions specify the *end styles* of each state.
|
||||
They are applied to the element once it has transitioned to that state, and will stay
|
||||
*as long as it remains in that state*. In that sense, we are defining more than just
|
||||
animations here. We're actually defining what styles the element has in different states.
|
||||
|
||||
Once we have states, we can define *transitions* between the states. Each transition
|
||||
controls the timing of switching between one set of styles and the next:
|
||||
|
||||
+makeExample('animations/ts/app/hero-list-basic.component.ts', 'transitions')(format=".")
|
||||
|
||||
figure.image-display
|
||||
img(src="/resources/images/devguide/animations/ng_animate_transitions_inactive_active.png" alt="In Angular animations we defines states and transitions between states" width="400")
|
||||
|
||||
:marked
|
||||
If we have the same timing configuration for several transitions, we can combine
|
||||
them into the same `transition` definition:
|
||||
|
||||
+makeExample('animations/ts/app/hero-list-combined-transitions.component.ts', 'transitions')(format=".")
|
||||
|
||||
:marked
|
||||
When we have the same timing for both directions of a transition, as we do in the previous
|
||||
example, we can use the `<=>` shorthand syntax:
|
||||
|
||||
+makeExample('animations/ts/app/hero-list-twoway.component.ts', 'transitions')(format=".")
|
||||
|
||||
:marked
|
||||
Sometimes we have styles that we want to apply during an animation but not keep around
|
||||
after it finishes. We can define such styles inline in the `transition`. In this example,
|
||||
the element receives one set of styles immediately and is then animated to the next.
|
||||
When the transition finishes, none of these styles will be kept because they're not
|
||||
defined in a `state`.
|
||||
|
||||
+makeExample('animations/ts/app/hero-list-inline-styles.component.ts', 'transitions')(format=".")
|
||||
|
||||
:marked
|
||||
### The wildcard state `*`
|
||||
|
||||
The `*` ("wildcard") state matches *any* animation state. This is useful for defining styles and
|
||||
transitions that should apply regardless of which state the animation is in. For example:
|
||||
|
||||
* The `active => *` transition applies when the element's state changes from `active` to anything else.
|
||||
* The `* => *` transition applies when *any* change between two states takes place.
|
||||
|
||||
figure.image-display
|
||||
img(src="/resources/images/devguide/animations/ng_animate_transitions_inactive_active_wildcards.png" alt="The wildcard state can be used to match many different transitions at once" width="400")
|
||||
|
||||
|
||||
:marked
|
||||
### The `void` state
|
||||
|
||||
There's one special state called `void` that may apply to any animation. It applies
|
||||
when the element is *not* attached to a view. This may be because it has not yet been
|
||||
added or because it has been removed. The `void` state is useful for defining "enter" and
|
||||
"leave" animations.
|
||||
|
||||
For example the `* => void` transition applies when the element leaves the view,
|
||||
regardless of what state it was in before it left.
|
||||
|
||||
figure.image-display
|
||||
img(src="/resources/images/devguide/animations/ng_animate_transitions_void_in.png" alt="The void state can be used for enter and leave transitions" width="400")
|
||||
|
||||
:marked
|
||||
The wildcard state `*` also matches `void`.
|
||||
|
||||
## Example: Entering and Leaving
|
||||
figure
|
||||
img(src="/resources/images/devguide/animations/animation_enter_leave.gif" alt="Enter and leave animations" align="right" style="width:250px;" )
|
||||
:marked
|
||||
Using the `void` and `*` states we can define transitions that animate the
|
||||
entering and leaving of elements:
|
||||
|
||||
* Enter: `void => *`
|
||||
* Leave: `* => void`
|
||||
|
||||
+makeExample('animations/ts/app/hero-list-enter-leave.component.ts', 'animationdef')(format=".")
|
||||
|
||||
:marked
|
||||
Note that in this case we have the styles applied to the void state directly in the
|
||||
transition definitions, and not in a separate `state(void)` definition. We do this because
|
||||
we want the transforms to be different on enter and leave: The element enters from the left
|
||||
and leaves to the right.
|
||||
|
||||
## Example: Entering and Leaving from Different States
|
||||
figure
|
||||
img(src="/resources/images/devguide/animations/animation_enter_leave_states.gif" alt="Enter and leave animations combined with state animations" align="right" style="width:200px" )
|
||||
:marked
|
||||
We can also combine this animation with the earlier state transition animation by
|
||||
using the hero state as the animation state. What this will let us do is configure
|
||||
different transitions for entering and leaving based on what the state of the hero
|
||||
is:
|
||||
|
||||
* Inactive hero enter: `void => inactive`
|
||||
* Active hero enter: `void => active`
|
||||
* Inactive hero leave: `active => void`
|
||||
* Active hero leave: `inactive => void`
|
||||
|
||||
We now have fine-grained control over each transition:
|
||||
|
||||
figure.image-display
|
||||
img(src="/resources/images/devguide/animations/ng_animate_transitions_inactive_active_void.png" alt="This example transitions between active, inactive, and void states" width="400")
|
||||
|
||||
|
||||
+makeExample('animations/ts/app/hero-list-enter-leave-states.component.ts', 'animationdef')(format=".")
|
||||
|
||||
:marked
|
||||
## Animatable Properties and Units
|
||||
|
||||
Since Angular's animation support builds on top of Web Animations, we can animate any property
|
||||
that the browser considers *animatable*. This includes positions, sizes, transforms, colors,
|
||||
borders and many others. The W3C maintains
|
||||
[a list of animatable properties](https://www.w3.org/TR/css3-transitions/#animatable-properties).
|
||||
|
||||
For positional properties that have a numeric value, we can define a unit by providing
|
||||
the value as a string with the appropriate suffix:
|
||||
|
||||
* `'50px'`
|
||||
* `'3em'`
|
||||
* `'100%'`
|
||||
|
||||
For most dimensinal properties we can also just define a number which is then assumed to be
|
||||
in pixels:
|
||||
|
||||
* `50` is the same as saying `'50px'`
|
||||
|
||||
## Automatic Property Calculation
|
||||
figure
|
||||
img(src="/resources/images/devguide/animations/animation_auto.gif" alt="Animation with automated height calculation" align="right" style="width:220px;margin-left:20px" )
|
||||
:marked
|
||||
Sometimes the value of a dimensional style property that we want to
|
||||
animate is not known until at runtime. For example, it is quite common for elements
|
||||
to have widths and heights that depend on their content and the screen size. These
|
||||
properties are often tricky to animate with CSS.
|
||||
|
||||
With Angular we can use a special `*` property value in these cases. What it means
|
||||
is that the value of this property will be computed at runtime and then plugged into
|
||||
the animation.
|
||||
|
||||
The "leave" animation in this example takes whatever height the element has before it
|
||||
leaves and animates from that height to zero:
|
||||
|
||||
+makeExample('animations/ts/app/hero-list-auto.component.ts', 'animationdef')(format=".")
|
||||
|
||||
:marked
|
||||
## Animation Timing
|
||||
|
||||
There are three timing properties we can tune for every animated transition:
|
||||
The duration, the delay, and the easing function. They are all combined into
|
||||
a single transition *timing string*.
|
||||
|
||||
### Duration
|
||||
|
||||
The duration controls how long the animation takes to run from start to finish.
|
||||
We can define a duration in three ways:
|
||||
|
||||
* As a plain number, in milliseconds: `100`
|
||||
* In a string, as milliseconds: `'100ms'`
|
||||
* In a string, as seconds: `'0.1s'`
|
||||
|
||||
### Delay
|
||||
|
||||
The delay controls how long to wait after an animation triggers before the
|
||||
transition actually begins. We can define one by adding it in the same string
|
||||
following the duration. It also has the same format options as the duration:
|
||||
|
||||
* Wait for 100ms and then run for 200ms: `'0.2s 100ms'`
|
||||
|
||||
### Easing
|
||||
|
||||
The [easing function](http://easings.net/) controls how the animation accelerates
|
||||
and decelerates during its runtime. For example, using an `ease-in` function means
|
||||
the animation begins relatively slowly but then picks up speed as it progresses. We
|
||||
can control the easing by adding it as a *third* value in the string after the duration
|
||||
and the delay (or as the *second* value when there is no delay):
|
||||
|
||||
* Wait for 100ms and then run for 200ms, with easing: `'0.2s 100ms ease-out'`
|
||||
* Run for 200ms, with easing: `'0.2s ease-in-out'`
|
||||
|
||||
figure
|
||||
img(src="/resources/images/devguide/animations/animation_timings.gif" alt="Animations with specific timings" align="right" style="width:220px;margin-left:20px" )
|
||||
:marked
|
||||
### Example
|
||||
|
||||
Here are a couple of custom timings in action. Both "enter" and "leave" last for
|
||||
200 milliseconds but they have different easings. The leave begins after a
|
||||
slight delay:
|
||||
|
||||
+makeExample('animations/ts/app/hero-list-timings.component.ts', 'animationdef')(format=".")
|
||||
|
||||
:marked
|
||||
## Multi-Step Animations with Keyframes
|
||||
figure
|
||||
img(src="/resources/images/devguide/animations/animation_multistep.gif" alt="Animations with some bounce implemented with keyframes" align="right" style="width:220px;margin-left:20px" )
|
||||
:marked
|
||||
With animation *keyframes* we can go beyond a simple transition between two
|
||||
sets of styles to a more intricate animation that goes through one or more
|
||||
intermediate styles in between.
|
||||
|
||||
For each keyframe, we can specify an *offset* that defines at which point
|
||||
in the animation that keyframe applies. The offset is a number between zero,
|
||||
which marks the beginning of the animation, and one, which marks the end.
|
||||
|
||||
In this example we add some "bounce" to our enter and leave animations with
|
||||
keyframes:
|
||||
|
||||
+makeExample('animations/ts/app/hero-list-multistep.component.ts', 'animationdef')(format=".")
|
||||
|
||||
:marked
|
||||
Note that the offsets are *not* defined in terms of absolute time. They are relative
|
||||
measures from 0 to 1. The final timeline of the animation will based on the combination
|
||||
of keyframe offsets, duration, delay, and easing.
|
||||
|
||||
Defining offsets for keyframes is optional. If we omit them, offsets with even
|
||||
spacing are automatically assigned. For example, three keyframes without predefined
|
||||
offsets will receive offsets `0`, `0.5`, and `1`.
|
||||
|
||||
:marked
|
||||
## Parallel Animation Groups
|
||||
figure
|
||||
img(src="/resources/images/devguide/animations/animation_groups.gif" alt="Parallel animations with different timings, implemented with groups" align="right" style="width:220px;margin-left:20px" )
|
||||
:marked
|
||||
We've already seen how we can animate multiple style properties at the same time:
|
||||
Just put all of them into the same `style()` definition!
|
||||
|
||||
But we may also want to configure different *timings* for animations that happen
|
||||
in parallel. For example, we may want to animate two CSS properties but use a
|
||||
different easing function for each one.
|
||||
|
||||
For this we can use animation *groups*. In this example we use groups both on
|
||||
enter and leave so that we can use two different timing configurations. Both
|
||||
are applied to the same element in parallel, but run independent of each other:
|
||||
|
||||
+makeExample('animations/ts/app/hero-list-groups.component.ts', 'animationdef')(format=".")
|
||||
|
||||
:marked
|
||||
One group animates the element transform and width. The other animates the opacity.
|
|
@ -210,6 +210,13 @@ block install-packages
|
|||
code-example(language="sh").
|
||||
npm install
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
The `typings` folder could not show up after `npm install`. If so, please install them manually.
|
||||
|
||||
code-example(language="sh").
|
||||
npm run typings install
|
||||
|
||||
.alert.is-important
|
||||
:marked
|
||||
Scary <span style="color:red; font-weight: bold">error messages in red</span> may appear **during** install.
|
||||
|
|
|
@ -1,103 +1,92 @@
|
|||
.grid-fluid.l-space-bottom-2
|
||||
.c12.text-center
|
||||
h3.text-headline.text-uppercase Core Team
|
||||
h3.text-headline.text-uppercase 核心开发组
|
||||
.clear
|
||||
|
||||
.grid-fluid
|
||||
.c6
|
||||
.article-card
|
||||
.date April 19, 2016
|
||||
.date 2016-04-19
|
||||
.date June 15, 2016
|
||||
.title
|
||||
a(
|
||||
target="_blank"
|
||||
href="https://angularjs.blogspot.com/2016/04/angular-2-react-native.html"
|
||||
) Angular 2 + React Native
|
||||
p The Angular 2 and React Native story started last summer [...] we are now pleased to share a library to build your own React Native applications with Angular 2 for iOS and Android...
|
||||
p Angular 2和React Native之间的故事开始于去年夏天 [...] 我们很高兴现在能共享同一个库,以便使用Angular 2来构建你自己的React原生应用 —— 为iOS和Android ……
|
||||
href="http://angularjs.blogspot.com/2016/06/rc2-now-available.html"
|
||||
) RC2 Now Available
|
||||
p Today we’re happy to announce that we are shipping Angular 2.0.0-rc2. This release includes 1. Animation Framework 2. Improvements to Forms 3. More than 100 community contributions...
|
||||
.author
|
||||
img(src="/resources/images/bios/marclaval.jpg")
|
||||
.posted Guest posted by <b>Marc Laval</b>
|
||||
img(src="/resources/images/bios/stephenfluin.jpg")
|
||||
.posted Posted by <b>Stephen Fluin</b>
|
||||
|
||||
.c6
|
||||
.article-card
|
||||
.date March 30, 2016
|
||||
.date 2016-03-30
|
||||
.date June 9, 2016
|
||||
.title
|
||||
a(
|
||||
target="_blank"
|
||||
href="http://angularjs.blogspot.com/2016/03/code-reuse-in-angular-2-native-mobile.html"
|
||||
) Code Reuse in Angular 2 Native Mobile Apps with NativeScript
|
||||
a(
|
||||
target="_blank"
|
||||
href="http://angularjs.blogspot.com/2016/03/code-reuse-in-angular-2-native-mobile.html"
|
||||
) 利用NativeScript在Angular 2原生移动应用中复用代码
|
||||
p In this article, I'm going to show you how to create a single application with Angular 2 that can be rendered on the web, or rendered in a native mobile application with NativeScript. Here's what you can expect to learn...
|
||||
p 在本文档中,我们将展示如何利用Angular 2创建一个单一应用,让它可以利用NativeScript在Web上或者在原生移动应用中渲染。这可能正是你想学的。
|
||||
href="http://angularjs.blogspot.com/2016/06/improvements-coming-for-routing-in.html"
|
||||
) Improvements Coming for Routing in Angular
|
||||
p A little more than a month ago, we introduced a new router at ng-conf. We’re grateful to have heard from many folks at ng-conf about flaws in this new design, so we are announcing...
|
||||
|
||||
.author
|
||||
img(src="/resources/images/bios/shield-bio-placeholder.png")
|
||||
.posted Guest posted by <b>Nathan Walker</b>
|
||||
|
||||
img(src="/resources/images/bios/stephenfluin.jpg")
|
||||
.posted Posted by <b>Stephen Fluin</b>
|
||||
|
||||
.grid-fluid.l-space-bottom-2.l-space-top-4
|
||||
.c12.text-center
|
||||
h3.text-headline.text-uppercase Developer Community
|
||||
h3.text-headline.text-uppercase 开发者社区
|
||||
.clear
|
||||
.grid-fluid
|
||||
.c6
|
||||
.article-card
|
||||
.date May 13, 2016
|
||||
.date June 14, 2016
|
||||
.title
|
||||
a(
|
||||
target="_blank"
|
||||
href="http://blog.thoughtram.io/angular/2016/05/13/angular-2-providers-using-map-literals.html"
|
||||
) Angular 2 Providers using Map Literals
|
||||
p Defining providers in Angular 2 is pretty easy and straight forward. Another, shorter syntax makes creating providers even more enjoyable. In this article we take a look at how to create providers using map literals...
|
||||
href="http://blog.thoughtram.io/angular/2016/06/14/routing-in-angular-2-revisited.html"
|
||||
) Routing in Angular 2 Revisited
|
||||
p Just recently, the Angular team announced yet another version of the new router. Take a first look at the new and better APIs, touching on the most common scenarios...
|
||||
.author
|
||||
img(src="/resources/images/bios/pascalprecht.jpg")
|
||||
.posted Posted by <b>Pascal Precht</b>
|
||||
|
||||
.c6
|
||||
.article-card
|
||||
.date May 10, 2016
|
||||
.date June 13, 2016
|
||||
.title
|
||||
a(
|
||||
target="_blank"
|
||||
href="https://medium.com/@richavyas/aha-moments-from-ngconf-2016-part-1-angular-2-0-compile-cycle-6f462f68632e#.ch4tad4ou"
|
||||
) Aha Moments from ngconf 2016 Part 1: Angular 2.0 Compile Cycle
|
||||
p WebStorm can be a great playground for your new Angular 2 applications. In this blog post we’d like to share with you some tips and tricks that we hope you’ll find useful ...
|
||||
p WebStorm可以成为新的Angular 2应用的上佳游乐场。在这篇blog中,我们很高兴共享一些提示和技巧 —— 但愿能给你一点帮助。
|
||||
href="https://toddmotto.com/rewriting-angular-styleguide-angular-2"
|
||||
) A new Angular 1.x ES2015 styleguide, the path to Angular 2
|
||||
p Angular 1.x has changed vastly since the original styleguide, and ES6/ES2015 has now become a defacto standard. The new styleguide focuses on using ES2015, offering recommendations on tooling to use it today...
|
||||
.author
|
||||
img(src="/resources/images/bios/shield-bio-placeholder.png")
|
||||
.posted Posted by <b>Richa Vyas</b>
|
||||
.posted Posted by <b>Todd Motto</b>
|
||||
.grid-fluid
|
||||
.c6
|
||||
.article-card
|
||||
.date May 9, 2016
|
||||
.date June 9, 2016
|
||||
.title
|
||||
a(
|
||||
target="_blank"
|
||||
href="https://scotch.io/bar-talk/the-best-news-from-angulars-ng-conf-2016"
|
||||
) The Best News from Angular’s ng-conf 2016
|
||||
p ng-conf 2016 in Salt Lake City brought a lot of incredible announcements. Just like last year, let's recap the announcements and takeaways from this year's ng-conf...
|
||||
href="https://medium.com/@urish/building-simon-with-angular2-iot-fceb78bb18e5"
|
||||
) Building Simon with Angular2-IoT
|
||||
p I thought it would be fun to see if I could make the same [Angular 2] control/logic code for both a web application and a physical, “Internet of Things” device....
|
||||
.author
|
||||
img(src="/resources/images/bios/shield-bio-placeholder.png")
|
||||
.posted Posted by <b>Chris Sevilleja</b>
|
||||
img(src="/resources/images/bios/angular-gde-bio-placeholder.png")
|
||||
.posted Posted by <b>Uri Shaked</b>
|
||||
|
||||
.c6
|
||||
.article-card
|
||||
.date May 6, 2016
|
||||
.date June 8, 2016
|
||||
.title
|
||||
a(
|
||||
target="_blank"
|
||||
href="https://www.joaogarin.com/blog/building-desktop-apps-with-angular2-and-electron"
|
||||
) Building Desktop Apps with Angular2 and Electron
|
||||
p Electron is a great open source tool from Github that allows you to leverage your Javascript skills to build Desktop apps for MacOS, Windows and Linux systems...
|
||||
href="http://blog.thoughtram.io/angular/2016/06/08/component-relative-paths-in-angular-2.html"
|
||||
) Component-Relative Paths in Angular 2
|
||||
p Component-based development is Angular 2’s most-loved feature. By now you should be familiar with using the @Component decorators to create components.\...
|
||||
.author
|
||||
img(src="/resources/images/bios/shield-bio-placeholder.png")
|
||||
.posted Posted by <b>Joao Garin</b>
|
||||
img(src="/resources/images/bios/thomas.jpg")
|
||||
.posted Posted by <b>Thomas Burleson</b>
|
||||
|
||||
.grid-fluid.l-space-bottom-2.l-space-top-4
|
||||
.c12.text-center
|
||||
|
@ -116,11 +105,6 @@
|
|||
class="twitter-follow-button"
|
||||
data-show-count="false"
|
||||
) Follow @angularjs
|
||||
a(
|
||||
href="http://twitter.com/angularjs"
|
||||
class="twitter-follow-button"
|
||||
data-show-count="false"
|
||||
) 追随 @angularjs
|
||||
p.
|
||||
<a class="twitter-timeline" data-chrome="nofooter noborders noheader"
|
||||
href="http://twitter.com/angularjs" data-widget-id="700150278465523713"></a>
|
||||
|
|
After Width: | Height: | Size: 224 KiB |
After Width: | Height: | Size: 164 KiB |
After Width: | Height: | Size: 221 KiB |
After Width: | Height: | Size: 805 KiB |
After Width: | Height: | Size: 242 KiB |
After Width: | Height: | Size: 304 KiB |
After Width: | Height: | Size: 225 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 64 KiB |
After Width: | Height: | Size: 70 KiB |
After Width: | Height: | Size: 50 KiB |