1.1angular模块
每个Angular应用至少有一个根模块,一般命名为app.module,跟模块用于声明和引入一些模块,如果不声明,就会报错。
模块的基本语法如下:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
@NgModule({
imports: [ BrowserModule ], //这里一般导入angular的内部模块 BrowserModule注册了一些关键的应用服务提供商。 它还包括了一些通用的指令,例如NgIf和NgFor,所以这些指令在该模块的任何组件模板中都是可用的。
providers: [ Logger ], //提供服务
declarations: [ AppComponent ], //声明应用程序创建的模块
exports: [ AppComponent ], //导出模块。一般在根模块中不用 可用于其它模块的组件模板。
bootstrap: [ AppComponent ] //主视图,就是决定根目录显示什么内容 只有根模块才能设置bootstrap属性。
})
export class AppModule { }
通常在main.ts文件中引导根模块,如下
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
1.2angular模块库
下面是一些常用到的模块库(持续更新中……),导入语法:import { Component } from ‘@angular/core’;
- import { Component } from ‘@angular/core’; 用于声明模块的类
- import { BrowserModule } from ‘@angular/platform-browser’; BrowserModule注册了一些关键的应用服务提供商。 它还包括了一些通用的指令,例如NgIf和NgFor,所以这些指令在该模块的任何组件模板中都是可用的。
- import { Input} from ‘@angular/core’; 用于模块间参数传递 eg:
@Input() hero: Hero;
- import { NgModule } from ‘@angular/core’; 用于视图数据绑定
- import { OnInit} from ‘@angular/core’; 用于初始化事件调用
export class LoginComponent implements OnInit {}
- import { ViewChild,ElementRef} from ‘@angular/core’; 用于获取html元素,定义:
@ViewChild('chart') private chartContainer: ElementRef;
使用:let element = this.chartContainer.nativeElement;
- import { RouterModule, Routes } from ‘@angular/router’; 用于路由
- import { Route } from ‘@angular/router’; 用于路由跳转this.router.navigate([‘/url’,传参数]);
- import { ActivatedRoute, ParamMap } from ‘@angular/router’; 和上面搭配。用于获取传递的参数。eg:
this.route.paramMap
.switchMap((params : ParamMap) => this.heroService.getHero(+params.get('id')))
.subscribe(hero => this.hero = hero)
- import { Location } from ‘@angular/common’; 用于浏览器跳转 eg:
this.location.back();
- import {FormsModule} from ‘@angular/forms’;
- import {HttpClientModule} from ‘@angular/common/http’; 用于和后端浏览器通讯
@Component(...)
export class MyComponent implements OnInit {
results: string[];
// Inject HttpClient into your component or service.
constructor(private http: HttpClient) {}
ngOnInit(): void {
// Make the HTTP request:
this.http.get('/api/items').subscribe(data => {
// Read the result field from the JSON response.
this.results = data['results'];
});
}
}
1.3模块的定义
(1)模块定义
模块定义基本语法如下:
import { Component } from '@angular/core';
@Component({
selector: 'app-root', //选择器
template:'<h1></h1>', //可以直接写语句也可以如下应用模板
templateUrl: './app.component.html', //模板html的地址
styleUrls: ['./app.component.css'] //css的地址
providers: [ HeroService ] //注入的类
})
export class AppComponent implements OnInit {
constructor() {
}
ngOnInit() {
}
}
<app-root></app-root> //使用
(2)生命周期
- ngOnChanges() 当被绑定的输入属性的值发生变化时调用,首次调用一定会发生在ngOnInit()之前
- ngOnInit() 在Angular第一次显示数据绑定和设置指令/组件的输入属性之后,初始化指令/组件。在第一轮ngOnChanges()完成之后调用,只调用一次。用途:在构造函数之后马上执行复杂的初始化逻辑。
- ngDoCheck() 检测,并在发生Angular无法或不愿意自己检测的变化时作出反应。
- ngAfterContentInit() 当把内容投影进组件之后调用 只调用一次。只适用于组件。
- ngAfterContentChecked() 每次完成被投影组件内容的变更检测之后调用。只适合组件。
- ngAfterViewInit() 初始化完组件视图及其子视图之后调用。只调用一次。只适合组件。
- ngAfterViewChecked() 每次做完组件视图和子视图的变更检测之后调用。只适合组件。
- ngOnDestroy 销毁指令/组件之前调用并清扫 。进行反订阅可观察对象和分离事件处理器。
1.4模板
用angular模板语法写的html代码,一般和component结合,链接地址放置在templateUrl
一些常用的模板语法(更新中……)
- {{….}} 插入值(表达式)
<p>My current hero is {{currentHero.name}}</p>
<img src="{{heroImageUrl}}" style="height:30px">
<p>The sum of 1 + 1 is not {{1 + 1 + getVal()}}</p>
- 属性绑定 [属性名] :方括号告诉 Angular 要计算模板表达式。 如果忘了加方括号,Angular 会把这个表达式当做字符串常量看待,并用该字符串来初始化目标属性。
<img [src]="heroImageUrl"> //元素 等同于<img src="{{heroImageUrl}}" >
<button [disabled]="isUnchanged">Cancel is disabled</button> //组件
<app-hero-detail [hero]="currentHero"></app-hero-detail> //组件
<div [ngClass]="{'special': isSpecial}"></div> //指令
- 事件绑定
<button (click)="onSave()">Save</button>
<div (myClick)="clickMessage=$event" clickable>click with myClick</div> //指令
<input [value]="currentHero.name"
(input)="currentHero.name=$event.target.value" >
//在处理父指令到子指令传递event事件时:
//子指令
<button (click)="delete()">Delete</button> //html中
@Output() deleteRequest = new EventEmitter<Hero>(); //ts中
delete() {
this.deleteRequest.emit(this.hero);
}
//父指令
<app-hero-detail (deleteRequest)="deleteHero($event)" [hero]="currentHero"></app-hero-detail>
- 双向数据绑定 [(…)] ngModule
<input [(ngModel)]="currentHero.name">
//在使用ngModel指令进行双向数据绑定之前,我们必须导入FormsModule并把它添加到Angular模块的imports列表中。
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms'; //
@NgModule({
imports: [
BrowserModule,
FormsModule // <--- import into the NgModule
],
/* Other module metadata */
})
export class AppModule { }
- NgClass 指令 添加或移除一组CSS类
<div [ngClass]="currentClasses">This div is initially saveable, unchanged, and special</div>
- NgStyle 添加或移除一组CSS样式
<div [ngStyle]="currentStyles">
This div is initially italic, normal weight, and extra large (24px).
</div>
- *ngIf 类似js的if
<div *ngIf="currentHero">Hello, {{currentHero.name}}</div>
- *ngSwitch 类似js的switch
<div [ngSwitch]="currentHero.emotion">
<app-happy-hero *ngSwitchCase="'happy'" [hero]="currentHero"></app-happy-hero>
<app-sad-hero *ngSwitchCase="'sad'" [hero]="currentHero"></app-sad-hero>
<app-confused-hero *ngSwitchCase="'confused'" [hero]="currentHero"></app-confused-hero>
<app-unknown-hero *ngSwitchDefault [hero]="currentHero"></app-unknown-hero>
</div>
- *ngForOf 类似js的for of
<div *ngFor="let hero of heroes">{{hero.name}}</div>
<app-hero-detail *ngFor="let hero of heroes" [hero]="hero"></app-hero-detail>
<div *ngFor="let hero of heroes; let i=index">{{i + 1}} - {{hero.name}}</div>
- 带trackBy的*ngFor
trackByHeroes(index: number, hero: Hero): number { return hero.id; }
<div *ngFor="let hero of heroes; trackBy: trackByHeroes">
({{hero.id}}) {{hero.name}}
</div>
<ng-template>
是一个 Angular 元素,用来渲染HTML。<ng-container>
是一个分组元素,但它不会污染样式或元素布局,因为 Angular 压根不会把它放进 DOM 中
<p>
I turned the corner
<ng-container *ngIf="hero">
and saw {{hero.name}}. I waved
</ng-container>
and continued on my way.
</p>
- #var 引入变量 类似react的ref
<input ref-fax placeholder="fax number">
<button (click)="callFax(fax.value)">Fax</button>
- @input @output :在子组件引入父组件的元素时,@Input往往是值,@Output是指事件
@Input() hero: Hero;
@Output() deleteRequest = new EventEmitter<Hero>();
- 管道指令
<div>Title through uppercase pipe: {{title | uppercase}}</div> //大写
<div>
Title through a pipe chain:
{{title | uppercase | lowercase}} //小写,可串联
</div>
<div>Birthdate: {{currentHero?.birthdate | date:'longDate'}}</div> //日期
<div>{{currentHero | json}}</div> //转为json格式
- 安全操作符 ?. !非空断言 :表示存在就执行后面的操作
The current hero's name is {{currentHero?.name}}
<div *ngIf="hero">
The hero's name is {{hero!.name}}
</div>
- ElementRef :可以封装不同平台下视图层中的 native 元素,在constructo注入,然后通过this.elementRef.nativeElement获取dom元素
constructor(private elementRef: ElementRef) {
}
const rootMenu = this.elementRef.nativeElement.querySelectorAll('.ant-menu-submenu.ant-menu-submenu-horizontal');
1.5服务
服务一般是封装了一堆方法的类
一般在根组件中注册 providers: [DataTableService]
然后在使用的组件的constuctor中注入
constructor(private store: Store<State>,
private dataTable: DataTableService,
private dataTask: DataTaskService,
private _message: NzMessageService) {}
//使用时 this.store.select()
(1)依赖注入: 就是在 class的构造函数中注入外部类,而不必亲自创建它。
(2)为什么要用 @Injectable()?
@Injectable() 标识一个类可以被注入器实例化。 通常,在试图实例化没有被标识为@Injectable()的类时,注入器会报错。
import { Injectable } from '@angular/core';
import { HEROES } from './mock-heroes';
import { Logger } from '../logger.service';
@Injectable()
export class HeroService {
constructor(private logger: Logger) { }
getHeroes() {
this.logger.log('Getting heroes ...');
return HEROES;
}
}
(3)
1.6交互
1.@input 和 @output 绑定把数据从父组件传到子组件
(1)父组件
import { Component } from '@angular/core';
import { HEROES } from './hero';
@Component({
selector: 'app-hero-parent',
template: `
<h2>{{master}} controls {{heroes.length}} heroes</h2>
<app-hero-child *ngFor="let hero of heroes"
[hero]="hero"
[master]="master">
</app-hero-child>
`
})
export class HeroParentComponent {
heroes = HEROES;
master = 'Master';
}
(2)子组件
import { Component, Input } from '@angular/core';
import { Hero } from './hero';
@Component({
selector: 'app-hero-child',
template: `
<h3>{{hero.name}} says:</h3>
<p>I, {{hero.name}}, am at your service, {{masterName}}.</p>
`
})
export class HeroChildComponent {
//直接不处理
@Input() hero: Hero;
@Input('master') masterName: string; //区别名 不采用
//或者采用setter处理
private _masterName = '';
@Input()
set masterName(masterName: string) {
this._masterName = (masterName&& masterName.trim()) || '<no name set>';
}
get masterName(): string { return this._masterName; }
}
2.ngOnChanges
ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
let log: string[] = [];
for (let propName in changes) {
let changedProp = changes[propName];
let to = JSON.stringify(changedProp.currentValue); //当前值
if (changedProp.isFirstChange()) {
log.push(`Initial value of ${propName} set to ${to}`); //propName 改变的变量名
} else {
let from = JSON.stringify(changedProp.previousValue); //以前的值
log.push(`${propName} changed from ${from} to ${to}`);
}
}
this.changeLog.push(log.join(', '));
}
3.父组件监听子组件的事件(见上面的1.4的事件绑定)
(1)父组件 : 父组件绑定到这个事件属性,并在事件发生时作出回应。
(2)子组件 :子组件暴露一个EventEmitter属性,当事件发生时,子组件利用该属性emits(向上弹射)事件。
4.父组件调用子组件的方法 变量 : 通过父组件定义一个变量#var来代表子组件,父组件-子组件的连接必须全部在父组件的模板中进行,例如:
import { Component } from '@angular/core';
import { CountdownTimerComponent } from './countdown-timer.component';
@Component({
selector: 'app-countdown-parent-lv',
template: `
<h3>Countdown to Liftoff (via local variable)</h3>
<button (click)="timer.start()">Start</button>
<button (click)="timer.stop()">Stop</button>
<div class="seconds">{{timer.seconds}}</div>
<app-countdown-timer #timer></app-countdown-timer>
`,
styleUrls: ['../assets/demo.css']
})
export class CountdownLocalVarParentComponent { }
//这里的#timer代表子组件,然后父组件可以调用子组件的start() stop()方法了
5.父组件的类需要读取子组件的属性值或调用子组件的方法:子组件作为ViewChild,注入到父组件里面
import { AfterViewInit, ViewChild } from '@angular/core';
import { Component } from '@angular/core';
import { CountdownTimerComponent } from './countdown-timer.component';
@Component({
selector: 'app-countdown-parent-vc',
template: `
<h3>Countdown to Liftoff (via ViewChild)</h3>
<button (click)="start()">Start</button>
<button (click)="stop()">Stop</button>
<div class="seconds">{{ seconds() }}</div>
<app-countdown-timer></app-countdown-timer>
`,
styleUrls: ['../assets/demo.css']
})
export class CountdownViewChildParentComponent implements AfterViewInit {
@ViewChild(CountdownTimerComponent)
private timerComponent: CountdownTimerComponent;
seconds() { return 0; } //被注入的计时器组件只有在Angular显示了父组件视图之后才能访问,所以我们先把秒数显示为0.
ngAfterViewInit() {
setTimeout(() => this.seconds = () => this.timerComponent.seconds, 0);
}
//然后Angular会调用ngAfterViewInit生命周期钩子,但这时候再更新父组件视图的倒计时就已经太晚了。Angular的单向数据流规则会阻止在同一个周期内更新父组件视图。我们在显示秒数之前会被迫再等一轮。使用setTimeout()来等下一轮,然后改写seconds()方法,这样它接下来就会从注入的这个计时器组件里获取秒数的值。
start() { this.timerComponent.start(); }
stop() { this.timerComponent.stop(); }
}
6.父组件和子组件通过服务通信
例如rxjs在服务中创建observable 在子组件和父组件中订阅它
1.7样式
1.:host 获取宿主元素
:host(.active) {
border-width: 3px;
}
2.:host-context 它在当前组件宿主元素的祖先节点中查找 CSS 类, 直到文档的根节点为止。在与其它选择器组合使用时,它非常有用。
:host-context(.theme-light) h2 {
background-color: #eef;
}
3.::ng-deep 它不但作用于组件的子视图,也会作用于组件的内容。
1.8自定义属性指令
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(private el: ElementRef) { }
@Input('appHighlight') highlightColor: string; //判断值
@HostListener('mouseenter') onMouseEnter() { //事件
this.highlight(this.highlightColor || 'red');
}
@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);
}
private highlight(color: string) {
this.el.nativeElement.style.backgroundColor = color;
}
}
使用: <p [appHighlight]="color">Highlight me!</p>
1.9自定义管道
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({name: 'exponentialStrength'})
export class ExponentialStrengthPipe implements PipeTransform {
transform(value: number, exponent: string): number {
let exp = parseFloat(exponent);
return Math.pow(value, isNaN(exp) ? 1 : exp);
}
}
//使用 <p>Super power boost: {{2 | exponentialStrength: 10}}</p>
2.0非纯管道
AsyncPipe接受一个Promise或Observable作为输入,并且自动订阅这个输入,最终返回它们给出的值。使用方式时加上async
<p>Message: {{ message$ | async }}</p>
2.1动画
<li *ngFor="let hero of heroes"
[@heroState]="hero.state"
(click)="hero.toggleState()">
{{hero.name}}
</li>
//active转变
animations: [
trigger('heroState', [
state('inactive', style({
backgroundColor: '#eee',
transform: 'scale(1)'
})),
state('active', style({
backgroundColor: '#cfd8dc',
transform: 'scale(1.1)'
})),
transition('inactive => active', animate('100ms ease-in')),
transition('active => inactive', animate('100ms ease-out'))
//可以写成 transition('inactive => active, active => inactive',
animate('100ms ease-out'))
//或者 transition('inactive <=> active', animate('100ms ease-out'))
])
]
//进场 离场
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%)'}))
])
])
]
//自动计算高度 *
animations: [
trigger('shrinkOut', [
state('in', style({height: '*'})),
transition('* => void', [
style({height: '*'}),
animate(250, style({height: 0}))
])
])
]
//并行动画
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
}))
])
])
])
]
2.1路由
//appRoutes 加入AppModule的imports数组中
const appRoutes: Routes = [
{ path: 'crisis-center', component: CrisisListComponent }, //路由不能以“/”开头
{ path: 'hero/:id', component: HeroDetailComponent },
//路由中的:id是一个路由参数的令牌(Token)。比如/hero/42这个URL中,“42”就是id参数的值。
{
path: 'heroes',
component: HeroListComponent,
data: { title: 'Heroes List' }
},
{ path: '',
redirectTo: '/heroes',
pathMatch: 'full'
},
//空路径('')表示应用的默认路径。
{ path: '**', component: PageNotFoundComponent }
//路由中的**路径是一个通配符。当所请求的URL不匹配前面定义的路由表中的任何路径时,路由器就会选择此路由。 这个特性可用于显示“404 - Not Found”页,或自动重定向到其它路由。
];
或者和<router-outlet></router-outlet>
配合嵌套子页面
import { AuthGuard } from '../auth-guard.service';
const adminRoutes: Routes = [
{
path: 'admin',
component: AdminComponent, //父页面
canActivate: [AuthGuard], //这个做权限管理,没有登陆就返回登陆页面
children: [
{
path: '',
children: [
{ path: 'crises', component: ManageCrisesComponent }, //子页面
{ path: 'heroes', component: ManageHeroesComponent },
{ path: '', component: AdminDashboardComponent }
],
}
]
}
];
@NgModule({
imports: [
RouterModule.forChild(adminRoutes)
],
exports: [
RouterModule
]
})
export class AdminRoutingModule {}