angular入门知识盘点

836 阅读7分钟

angular常用指令一览

ng generate component example       // 生成组件带有模版(简写 ng g c example)
ng generate component example -it   // 生成内联模版(不会单独生成html文件)
ng generate module my-module        // 生成一个新模块
ng generate directive my-directive  // 生成一个新指令
ng generate pipe my-pipe            // 生成一个新管道
ng generate service my-service      // 生成一个新服务
ng generate route my-route          // 生成一个新路由
ng generate class my-class          // 生成一个简易的模型类

安装angular-cli 脚手架,如果npm比较慢,请使用淘宝镜像

npm install -g @angular/cli
npm i -g @angular/cli@1.1.0 //安装指定版本的angular脚手架
//更换npm源
npm config set registry https://registry.npm.taobao.org

//验证是否配置成功
npm config get registry

//npm官方源 https://registry.npmjs.org
//安装cnpm,更换npm的源或者使用cnpm,二选一
npm install -g cnpm --registry=https://registry.npm.taobao.org

//注意:低版本npm3.10.9安装低版本angular/cli@1.1.0 
//需要把镜像源改回registry.npmjs.org;这样才能安装,问题推测是淘宝镜像源里面没有这个版本

创建一个项目

ng new myAngular                // 创建一个项目,注意在git自带的brash 里面创建,可能不会显示引导步骤
ng new myAngular --skip-install // 不用安装依赖包
ng new myAngular -si            // 上一句的缩写
ng new myAngular --routing      // 创建一个带路由的
ng new myAngular --strict       // 使用更严格的 typescript 编译选项

组件结构

//从angular主模块中引入Component(组件装饰器或组件注解)
import { Component } from '@angular/core';

//装饰器中以json的形式声明元数据
@Component({
  //它指定了一个叫 <app-root> 的元素。 该元素是 index.html 文件里的一个占位符
  //为什么这个组件跟入口index建立了联系呢?因为入口main.ts中绑定了主模块为appModule
  selector: 'app-root',                //在模板中找对应的标签,找到后创建并插入该组件实例
  templateUrl: './app.component.html', //组件模板
  styleUrls: ['./app.component.css'],  //组件样式
  //这个属性(内联模板)和templateUrl(外联模板)二选一,template后面可以直接跟html字符串
  //注意在模板语法(反引号)中是使用插值表达式,不能使用${}插入值
  template: `<h1>{{title}}</h1>`   
})
//组件控制器,写逻辑代码的地方
export class AppComponent {
  title = 'myAngular';
  //构造函数可以用来进行属性的声明和初始化语句
  //在angular里面有个特别重要的点要记住:只能通过构造函数注入依赖
  constructor() {}
}

模块结构

import { BrowserModule } from '@angular/platform-browser';
//从主模块中引入NgModule(模块装饰器或模块注解)
import { NgModule } from '@angular/core';
//引入组件,因为模块为组件提供编译的上下文环境
import { AppComponent } from './app.component';

//装饰器以json的形式声明元数据
@NgModule({
   //组合模块的组件和管道
  declarations: [ AppComponent ],
  //模块依赖项
  imports: [ BrowserModule ],
  //模块提供的服务
  providers: [],
  //指定宿主组件,只在根模块中出现
  bootstrap: [AppComponent]
})
export class AppModule { }

创建组件命令

ng generate component 组件名 
ng g c 组件名                //上面命令的缩写
ng g c 组件名 -it             //生成内联模板,便于测试

基本绑定

//值绑定 {{}}   单向绑定 module => view
//里面可以放变量和表达式,一般放到html内容里,但是也可以用在属性中
msg:string = "文本";
bgClass:string = "bgRed";
<span>{{msg}}<span>
<div class="{{bgClass}}"></div>

//属性绑定 [] 和 {{}}
 public placeholder:string = "Dan"
 public index:number = 0
<input type="text" [value]="placeholder"></input>
<input type="text" value="{{placeholder}}"></input>
<input type="text" [value]="{{placeholder}}"></input>
<input type="text" bind-value="placeholder"></input>
<input type="text" [attr.data-index]="index"></input> //自定义属性绑定
//特例:表格的clospan怎么绑定(两种方式)
<tr><td [attr.colspan]="2">Attribute的方式绑定</td></tr>
<tr><td [colSpan]="2">Property的方式绑定</td></tr>
//绑定html内容,会自动屏蔽掉script标签里面的内容,避免安全隐患
htmlStr:string = "<h1>标题</h1><script>location.href="www.baidu.com"</script>"
<div [innerHtml]="htmlStr"></div>

//属性绑定中的class绑定
public successClass:string = "text-success ..."
public hasError:Boolean = true;
public messageClasses: Object = {
    "text-success": false,
    "text-danger": true
    }
public arrClass = ['aa','bb']
//自动将变量的class和原有的class相加
<h2 class="aa" [class]="successClass"> 标题</h2>        //绑定值是字符串
<h2 class="aa" class="{{successClass}}"> 标题</h2>      //绑定值是字符串
<h2 class="aa" [attr.class]="successClass"> 标题</h2>   //绑定值是字符串
<h2 [class.text-danger] = "hasError">标题</h2>          //绑定值是Boolean
<h2 [class]="messageClasses">标题</h2>                  //绑定值是对象
<h2 [class]="{text-danger: true}">标题</h2>             //绑定值是对象(本质是Boolean)
<h2 [ngClass]="{'text-danger': true}">标题</h2>         //绑定值是对象(本质是Boolean)
<h2 [class]="['aa','bb']">标题</h2>                     //绑定值是数组
<h2 [class]="arrClass">标题</h2>                        //绑定值是数组

//属性帮定中的style绑定
public styleStr:string = “background-colo:red;font-size:20px”;
public highlightColor:string = "orange";
public heightStr:string ="200px"
public heightNum:number = 200;
public titleStyles:Object = {
    color: "blue",
    fontStyle: "italic"
}
<h2 [style] = "styleStr">标题</h2>                       //绑定值是字符串
<h2 [ngStyle] = "styleStr">标题</h2>                     //绑定值是字符串
<h2 [style.height] = "heightStr">标题</h2>               //绑定值是字符串
<h2 [style.height.px] = "heightNum">标题</h2>            //绑定值是数字
<h2 [style.color] = "hasError ? 'red':'green'">标题</h2> //绑定值是Boolean
<h2 [style] = "titleStyles"> </h2>                       //绑定值是对象

//循环指令 *ngFor
arr:string[] = ['张三','李四','王五']; 
trackByItems(index: number, item: Item): number { return item.id; }
<div *ngFor="let item of arr; let i=index" (click)='choseThis(item,i)'>
   索引值:{{i}} -- 内容:{{item}}
</div>
//trackBy一般和长列表一起使用,减少dom替换次数,提升性能
<div *ngFor="let item of items; trackBy: trackByItems">
  ({{item.id}}) {{item.name}}
</div>

//条件渲染 *ngIf  style  class  ngSwitch
isShow: Boolean = true;
personState: number = 2;
<p *ngIf="isShow">命令模式</p>  //频繁切换不建议用,频繁加载和移除有较高的性能消耗
<p [style.display]="isShow?'block':'none'">style模式</p> //频繁切换建议用样式
<p [class.hidden]="isShow">class模式</p>
//匹配多种情况的条件渲染,跟vue的v-if/v-else-if/v-else类似
<div [ngSwitch] = 'personState'>
    <div *ngSwitchCase="1">工作</div>
    <div *ngSwitchCase="2">吃饭</div>
    <div *ngSwitchDefault>睡觉</div>
</div>

//事件绑定 ()
eventFunc(e){
  //得到事件对象,可以得到元素的宽高,设置元素的样式等
  console.log(e);
}
<button (click) = "eventFunc($event)">Greet</button> //推荐
<button on-click = "eventFunc($event)">Greet</button>
<input (blur)="eventFunc($event)">         //失去焦点事件
<input (keyup)="eventFunc($event)">        //监听键盘输入事件
<input (keyup.enter)="eventFunc($event)">  //监听键盘输入回车触发事件

//双向数据绑定 [(ngModel)]
//Angular不能直接识别ngModel,需要通过引入模块FormsModule来访问
import {FormsModule} from '@angular/forms';
imports: [FormsModule]
public name = "张三";
<input [(ngModel)] = "name" type="text"> //推荐
<input bindon-change="name" type="text"> //备选
//属性绑定+事件绑定 = ngModel
<input [value]="name" (input)="name=$event.target.value" >

//模板引用变量 #标识(通常是对模板中DOM元素的引用,可以直接通过标识获取dom元素值)
<input #phone placeholder="phone number" />   //推荐
<input ref-phone placeholder="phone number"/> //可以用'ref-'代替'#'
<button (click)="callPhone(phone.value)">Call</button>

指令(属性型指令和结构型指令)

//属性型指令
//作用:为元素提供统一的表现或行为
ng generate directive highlight //通过命令行创建指令类文件
//下面这个例子:在模板元素上加上appHighlight,元素的背景会变成黄色背景高亮显示
import { Directive, ElementRef } from '@angular/core';
//指令装饰器
@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
    constructor(el: ElementRef) {
       el.nativeElement.style.backgroundColor = 'yellow';
    }
}
<p appHighlight>Highlight me!</p>
//指令也能响应用户的事件,例如鼠标移动到元素上显示高亮背景,移除则不显示
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
  //可以在指令里面定义@input接收指令的传值
  @Input('appHighlight') highlightColor: string;
  constructor(private el: ElementRef) { }
  @HostListener('mouseenter') onMouseEnter() {
    this.highlight(this.highlightColor || 'yellow');
  }
  @HostListener('mouseleave') onMouseLeave() {
    this.highlight(null);
  }
  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }
  //也可以通过元素去监听事件
  this.el.nativeElement.addEventListener(click,() => {
      this.el.nativeElement.style.height = 100px;
  })
}
//通过属性绑定的方式给属性指令传值
<p [appHighlight]="'red'">Highlight me!</p>

//结构型指令
*ngFor *ngIf ngSwitch
//下面自定义一个结构型指令
//案例:实现一个*ngIf功能相反的指令*ngElse
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({ selector: '[ngElse]'})
export class UnlessDirective {
  private hasView = false;
  //TemplateRef取得 <ng-template> 的内容
  //ViewContainerRef来访问这个视图容器
  constructor(
    private templateRef: TemplateRef<any>,
    private viewContainer: ViewContainerRef) { }
  @Input() set ngElse(condition: boolean) {
    if (!condition && !this.hasView) {
      this.viewContainer.createEmbeddedView(this.templateRef);
      this.hasView = true;
    } else if (condition && this.hasView) {
      this.viewContainer.clear();
      this.hasView = false;
    }
  }
}
//使用自定义结构型指令
<p *ngIf="isShow">IF</p>
//可以优化,使用ngElse的元素可以不赋值,动态拿到它前面挨着元素ngIf的绑定属性和值
<p *ngElse="!isShow">ELSE</p>

路由系统

//路由配置项
//path: 路由路径,不带/,通配符 **
Routes:Array<json> 
[{path: '',component : HomeComponent},  //不带路由
{path: 'detail/:id',component : DetailComponent},  //路由传参
{path:'**',component:Page404Component}]  //找不到路由的情况,跳转到404页面,必须放到最后

//辅助路由,html占位符,类似于插槽和具名插槽
//辅助路由输出器"aux"
routes=[
    {path:'',component:center}, //主路由显示器显示
    {path:'auxVivwe',component:auxViwe,outlet:'aux'}//辅助路由显示
]
<router-outlet></router-outlet>
<router-outlet></router-oulet name='aux'>
//主路由为/,辅助路由跳转到auxVivwe
<a [routerLink]="['/',{outlet:'aux','auxVivwe'}]">1</a>

Router //路由器对象,可以通过调用其navigate()和navigateByUrl()方法来导航到一个指定的路由
Router.navigate(Array<string>)  //将路由跳转到指定路由['/']

//routerLink:string  将标签为路由导航的属性
<a router-link="home"></a>
<a [routerLink]="['home']"></a>

//当前路由激活的对象。当前路由地址,路由参数
ActivatedRouter
proto.queryParams[id] //get参数对象= ?id=10
proto.params[id]      //参数= a/:id
proto.data[index].id  //data数据 =  [path:'',component:com,data:[{id:abc}]]

//重定向路由 redirectTo 和 pathMatch
//redirectTo:string重定向到的路由地址
//pathMatch:full 是否开启精确模式
{path:'', redirectTo:'/home',pathMatch:'full'}

//路由守卫
CanActivate:fun //处理路由到某路由的情况调用,方法返回真允许路由进入,返回假阻止路由。
CanDeactivate:fun //处理当前路由离开的状况调用,
Resolve:fun //在路由激活数据之前调用
//对路由注入路由守卫
{path:'/home',component:a,CanActivate:[实例,]}
//路由数据获取函数
object.subscribe(backFuntion) //订阅数据,对象数据发生改变立即调用回调方法
object.snapshot.object  //获取数据快照

依赖注入,控制反转

//提供器
//提供器 providers 可以在@ngModuls()或@component中声明
providers:[{provider:'prod',useClass:prod}]  //直接实例

//useFactory:function 通过方法返回类或值,工厂方法只会在需要提供服务时候被调用一次所返回的值会常驻并且后续的使用直接返回此值
//deps:Array<class>  为工厂方法提供的中间提供器,它所提供的服务其他是从@ngModule或@component中查找过来的
providers:[{provider:'prod',useFactory:()=>{ return 1},deps:[loge]}]   //提供工厂方法值
providers:[{provider:'prod',useValue:false}]   //提供一个值

//注入器
constructor(prod:prod){}

管道(过滤器)

//在控制器中声明一个函数,将值过滤后返回,通vue写法一样
//可以多个过滤器一起使用
<div>{{value| myDate | json }}</div>
<div>The hero's birthday is {{ birthday | date:"MM/dd/yy" }} </div>
<div>T价格: {{ money | toMoney:'元'}} </div>

//内置管道
json              //会自动将对象转成json字符串,方便看输出结果
date:'yyyy-mm-dd' //转换成时间data格式 
uppercase         //转换成全部大写
lowercase         //转换成小写
number:'2.2-2'   //转换数字格式

//自定义管道需要声明在declarations中初始化
//第一步创建管道文件
ng g pipe /piper/mypiper
//第二步在管道文件中写自己的逻辑transform两个参数分别表示传入值,和参数
//注意:通过命令行生成的管道(过滤器),会自动在全局声明;管道传入的参数是在':'冒号后面表示

安全导航运算符(很实用)

//可以对在属性路径中出现 null 和 undefined 值进行保护,防止渲染失败
//所以在使用对象的属性的时候,良好的习惯是使用安全导航运算符
//多层嵌套的对象,应该这样使用a?.b?.c?.d
<p>产品: {{product?.name}}</p>
<p>产品: {{product?.name?.firstName}}</p>

通信

//父子组件通信

//父组件 => 子组件
@Input() a:string; //子组件在控制器中定义
@Input()
  set b(b: string) { //监听输入值的变化,判断输入值是否符合要求,不符合要求就提示用户
    this._b = (b && b.trim()) || '<no name set>';
  }
  get b(): string { return this._b; }
@Input() c: function;
func(){ console.log("我是父组件的方法") }
//父组件通过属性绑定的方式向下传值或者方法
<my-component  [a]='helloA' [b]='helloB' [c]="func"></my-component>

//子组件 => 父组件
//子组件,自定义事件并发射出去
<input type="button" (click)="vote(true)">
exprot class son{
  //通过EventEmitter实现自定义事件
  @Output() voted = new EventEmitter<boolean>();
  vote(agreed: boolean) {
    this.voted.emit(agreed); //发射
  }
}
//父组件,监听自定义事件
<div>
    <app-son  (voted)="onVoted($event)"> </app-son>
</div>
exprot class app{
    agreed = 0;
    onVoted(agreed: boolean) {
       agreed ? this.agreed++ : agreed--;
    }
}

//父组件主动获取子组件的数据或方法 viewChild
<childCompontent #child> </childComponent>
@ViewChild('child') child1:component
this.child1.属性或方法

//非父子组件之间通过服务来通讯,也可以用Localstorage/SessionStorage(h5新增的本地缓存技术)

组件生命周期钩子

钩子时机及用途
ngOnChanges()时机:当被绑定的输入属性的值发生变化时调用,首次调用一定会发生在 ngOnInit() 之前。用途:当 Angular 设置或重新设置数据绑定的输入属性时响应。 该方法接受当前和上一属性值的 SimpleChanges 对象注意:这发生的非常频繁,所以你在这里执行的任何操作都会显著影响性能。
ngOnInit()时机:在第一轮 ngOnChanges() 完成之后调用,只调用一次。用途:在 Angular 第一次显示数据绑定和设置指令/组件的输入属性之后,初始化指令/组件。组件获取初始数据的好地方
ngDoCheck()时机:紧跟在每次执行变更检测时的 ngOnChanges() 和 首次执行变更检测时的 ngOnInit() 后调用。用途:检测,并在发生 Angular 无法或不愿意自己检测的变化时作出反应。注意:它和ngOnChanges一样发生的很频繁
ngAfterContentInit()时机:第一次 ngDoCheck() 之后调用,只调用一次。用途:当 Angular 把外部内容投影进组件视图或指令所在的视图之后调用
ngAfterContentChecked()时机:ngAfterContentInit() 和每次 ngDoCheck() 之后调用。用途:每当 Angular 检查完被投影到组件或指令中的内容之后调用。
ngAfterViewInit()时机:第一次 ngAfterContentChecked() 之后调用,只调用一次。用途:初始化完组件视图及其子视图之后调用。
ngAfterViewChecked()时机:ngAfterViewInit() 和每次 ngAfterContentChecked() 之后调用。用途:每次做完组件视图和子视图的变更检测之后调用。
ngOnDestroy()时机:在 Angular 销毁指令/组件之前调用。用途:当 Angular 每次销毁指令/组件之前调用并清扫。 在这儿反订阅可观察对象和分离事件处理器,以防内存泄漏。取消订阅可观察对象;清除定时器;反注册该指令在全局或应用服务中注册过的所有回调。

表单处理ngFrom

//常用表单基础类
FormControl 实例用于追踪单个表单控件的值和验证状态。
FormGroup 用于追踪一个表单控件组的值和状态
FormArray 用于追踪表单控件数组的值和状态。
ControlValueAccessor 用于在 AngularFormControl 实例和原生 DOM 元素之间创建一个桥梁。

//建立响应式表单,自动绑定

//第一步:在app.module.ts中引入
 imports: [ ReactiveFormsModule ]
 
//第二步:在组件中引入FormControl
import { FormControl } from '@angular/forms';
export class FavoriteColorComponent {
  name = new FormControl('');
}

//第三步:在模板中使用
<label>Name:
   <input type="text" [formControl]="name">
</label>

//局部刷新表单控件的值
this.name.setValue('张三');

//创建控件组
import { FormGroup, FormControl } from '@angular/forms';
profileForm = new FormGroup({
    firstName: new FormControl(''),
    lastName: new FormControl(''),
});

<form [formGroup]="profileForm">
  <label>
    First Name:
    <input type="text" formControlName="firstName">
  </label>
  <label>
    Last Name:
    <input type="text" formControlName="lastName">
  </label>
</form>

//嵌套表单和上面差不多

//也使用 patchValue() 方法用对象中所定义的任何属性为表单模型进行替换。(局部刷新)
this.profileForm.patchValue({
    firstName: 'Nancy',
    address: {
      street: '123 Drew Street'
    }
});

//建立模板驱动表单,手动双向绑定
import { Component } from '@angular/core';
@Component({
  selector: 'app-template-favorite-color',
  template: `color: <input type="text" [(ngModel)]="favoriteColor">`
})
export class FavoriteColorComponent {
  favoriteColor = '';
}

//使用 FormBuilder服务生成控件
//第一步:导入 FormBuilder 类。
import { FormBuilder } from '@angular/forms';
//第二步:注入这个 FormBuilder 服务。
constructor(private fb: FormBuilder) { }
//第三步:生成表单内容。
export class ProfileEditorComponent {
  //每个控件名对应的值都是一个数组,这个数组中的第一项是其初始值。第二项同步校验,第三项是异步校验
  profileForm = this.fb.group({
    firstName: ['',,Validators.required],,
    lastName: [''],
    address: this.fb.group({
      street: [''],
      city: [''],
      state: [''],
      zip: ['']
    }),
  });
}

通过http访问服务器

//对http请求进行封装 
//参考文档 https://www.jb51.net/article/152056.htm

//第一步:在根模块中引入HttpClientModule
import { HttpClientModule } from '@angular/common/http';
imports: [HttpClientModule]

//第二步:在服务中引入HttpClient,通过构造器注入
import { HttpClient } from '@angular/common/http';
//注意:HttpClient 服务为所有工作都使用了可观察对象。你必须导入示例代码片段中出现的 RxJS 可观察对象和操作符
import { Observable, throwError } from 'rxjs';
import { catchError, retry } from 'rxjs/operators';

constructor(private http: HttpClient) { }
getData() {
  //调用get方法,返回一个 Observable,返回的类型取决于你调用时传入的 observe 和 responseType 参数
  return this.http.get('请求地址');
}

//请求选项
options: {
    headers?: HttpHeaders | {[header: string]: string | string[]},
    //选项用于指定要返回的响应内容。
    observe?: 'body' | 'events' | 'response', 
    params?: HttpParams|{[param: string]: string | string[]},
    reportProgress?: boolean,
    //选项指定返回数据的格式
    responseType?: 'arraybuffer'|'blob'|'json'|'text',
    withCredentials?: boolean,
}

//异步请求
//当我们把一个函数的返回值用Observable类包装后,调用函数的使用方就可以订阅该函数,然后在得到通知后处理后续的事情,也就是异步的调用过程而不是同步等待
import { Observable } from 'rxjs/rx';
import { of } from 'rxjs/observable/of';
getHeroes(): Observable<Hero[]> {
    this.messageService.add('HeroService: fetched heroes');
    return of(HEROES);
}
//上面的函数用Observable封装, of强转后就是一个异步的函数, 这样外部在函数完成后调用后续的处理,比如refresh
this.heroService.getHeroes() 
.subscribe(heroes => this.refreshTree(heroes));

//拦截请求和响应
//检查请求字符串(检查是否为空)
private checkRequestUrl(requestUrl: string): boolean {
    if (!requestUrl || '' == requestUrl.trim() || null == requestUrl) {
      console.error('请求url为空,请检查');
      return false;
    }
    return true;
}
//检查post请求参数(检查是否为空)
 private checkParams(data: any): boolean {
    // 常规空检查
    if (!postData || null == postData) {
      console.error('请求数据为空,请检查');
      return false;
    }
    return true;
}

//如何获取当前路由地址
//Location和PlatformLocation都可以获取当前路由地址
 private planform: PlatformLocation,
 private location: Location,
 this.profileMenu['href'] = '/profile/setting';
// 去掉路由前面的'/'
const refreshUrl = this.planform.pathname.split('/', 2)[1];
this.displayChange = refreshUrl === 'page-index';

启动angular

npm start
ng serve --open //启动项目并打开浏览器
ng serve -o     //上一句命令的缩写
ng serve --port //指定程序运行端口