André Werlang (@awerlang)
https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html
A command line interface for Angular projects
npm install --save @angular/material
src/app/app.module.ts
import { MaterialModule } from '@angular/material';
@NgModule({
imports: [MaterialModule.forRoot()],
...
})
export class PizzaPartyAppModule { }
style.css
@import '~@angular/material/core/theming/prebuilt/deeppurple-amber.css';
It's interactive! Click on any element!
const inputChanged = Observable.fromEvent(inputElement, 'input')
.map(ev => ev.target.value)
.filter(text => text.length > 2)
.debounceTime(500)
.distinctUntilChanged();
const newSuggestionsArrived = inputChanged
.switchMap(q => Observable.ajax.getJSON('search?term=' + q));
newSuggestionsArrived.subscribe(data => {
suggestionsElement.innerHTML = data.map(it => `${it} `)
.join('');
});
<div>{{newSuggestionsArrived | async}}</div>
They augment:
@frozen class Foo {
@configurable(false) @enumerable(true) method() {}
}
https://tc39.github.io/proposal-decorators/ https://www.typescriptlang.org/docs/handbook/decorators.html
Provides a context for data and events,
supports template, styling,
can have services injected
and is change detected
* A directive is a component with no template.
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
topics = ['commits', 'branches', 'remotes'];
actions = ['list', 'create', 'delete'];
onGoClick() {
// TODO
}
}
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'my-component',
templateUrl: './my.component.html',
styleUrls: ['./my.component.css']
})
export class MyComponent {
@Input() input: DataType;
@Output() event = new EventEmitter<DataType>(false);
onEvent(value) {
this.event.emit(value);
}
}
Composes the view with an HTML-like syntax,
interpolates text and data, binds events,
and includes other components.
<md-chip-list>
<md-chip *ngFor="let topic of topics">{{topic}}</md-chip>
</md-chip-list>
<button md-button color="primary" (click)="onGoClick()">Go!</button>
<input #myInput [value]="input" (change)="myInput.value">
<input [(ngModel)]="field">
Styles a component view, supports view encapsulation.
:host {
display: block;
max-width: 500px;
font-family: Roboto,"Helvetica Neue",sans-serif;
}
.go {
margin-top: 8px;
float: right;
}
md-divider {
clear: both;
}
Declarative transitions and animations,
with state triggers and synchornization support.
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%)'}))
])
])
]
<md-chip *ngFor="let topic of topics" [@flyInOut]="'in'">...</md-chip>
Encapsulates business logic
and shares data among components
import { Injectable } from '@angular/core';
@Injectable()
export class TipsService {
match(topic, action) {
// TODO
}
}
import { TipsService } from './tips.service';
export class AppComponent {
constructor(private tips: TipsService) { }
onGoClick() {
const matchingTips = this.tips.match(this.topic, this.action);
}
}
Organizes elements and creates reusable modules.
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { MaterialModule } from '@angular/material';
import { AppComponent } from './app.component';
import { TipsService } from './tips.service';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
MaterialModule.forRoot(),
],
providers: [TipsService],
bootstrap: [AppComponent]
})
export class AppModule { }
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
MaterialModule.forRoot(),
],
declarations: [
AppComponent
],
providers: [{ provide: TipsService, useValue: tipsService }],
});
TestBed.compileComponents();
});
it('should render results', async(() => {
const topicChip: DebugElement = fixture.debugElement
.query(By.css('.topics')).query(By.directive(MdChip));
const actionChip: DebugElement = fixture.debugElement
.query(By.css('.actions')).query(By.directive(MdChip));
topicChip.triggerEventHandler('click', null);
actionChip.triggerEventHandler('click', null);
goBtn.triggerEventHandler('click', null);
fixture.detectChanges();
expect(el.querySelector('h4').textContent).toEqual('One tip found');
}));
let service = new TipsService();
beforeEach(() => {
service = new TipsService();
});
describe('getTopics', () => {
it('should work', () => {
expect(service.getTopics())
.toEqual(['changes', 'branches', 'commits', 'remotes']);
});
});