Angular学习笔记

188 阅读10分钟

一、父子组件传值篇

@Input() 和 @Output() 为子组件提供了一种与其父组件通信的方法。 @Input() 允许父组件更新子组件中的数据。相反,@Output() 允许子组件向父组件发送数据。

父组件向子组件传值—@Input

子组件或指令中的 @Input() 装饰器表示该属性可以从其父组件中获取值。

父组件 app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'my-app';
  msg = '你好,子组件';
}

父组件 app.component.html

<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
  <h1>
    Welcome to {{ title }}!
  </h1>
</div>
<app-child [item]="msg"></app-child>

<router-outlet></router-outlet>

子组件 child.component.ts

import { Component, OnInit, Input } from '@angular/core';

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.scss']
})
export class ChildComponent implements OnInit {
  // 子组件或指令中的 @Input() 装饰器表示该属性可以从其父组件中获取值。
  @Input() item: string;
  
  constructor() {
  }

  ngOnInit() {
  }
  
}

子组件 child.component.html

<p>父组件传给子组件的值:{{item}}</p>

子组件向父组件传值—@Output、EventEmitter

子组件或指令中的 @Output() 装饰器允许数据从子组件传给父组件。子组件使用 @Output() 属性来引发事件,以通知父组件这一变化。为了引发事件,@Output()必须是EventEmitter类型,它是@angular/core中用来发出自定义事件的类。

子组件 child.component.ts

import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.scss']
})
export class ChildComponent implements OnInit {
  // 子组件或指令中的 @Input() 装饰器表示该属性可以从其父组件中获取值。
  @Input() item: string;
  // 子组件或指令中的 @Output() 装饰器允许数据从子组件传给父组件。
  @Output() newItemEvent = new EventEmitter<string>();

  constructor() {
  }

  ngOnInit() {
  }

  addNewItem(value: string) {
    console.log(value);
    this.newItemEvent.emit(value);
  }

}

子组件 child.component.html

<p>父组件传给子组件的值:{{item}}</p>
<label>Add an item: <input #newItem></label>
<button (click)="addNewItem(newItem.value)">子组件向父组件中添加item</button>

父组件 app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'my-app';
  msg = '你好,子组件';
  items = ['item1','item2', 'item3', 'item4'];
  addItem(newItem: string) {
    this.items.push(newItem);
  }
}

父组件 app.component.html

<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
  <h1>
    Welcome to {{ title }}!
  </h1>
</div>
<ul>
  <li *ngFor="let item of items">{{item}}</li>
</ul>
<app-child [item]="msg" (newItemEvent)="addItem($event)"></app-child>

<router-outlet></router-outlet>

二、路由篇

项目中使用路由

要想在项目中使用路由,首先要创建一个带路由的应用项目。通过指令ng new routing-app --routing可以创建一个附带路由的项目。

1、为路由添加组件

为了使用 Angular 的路由器,应用至少要有两个组件才能从一个导航到另一个。要使用 CLI 创建组件,请在命令行输入以下内容,其中 first 是组件的名称:

ng generate component first

为第二个组件重复这个步骤,但给它一个不同的名字。这里的新名字是 second。

ng generate component second

CLI 会自动添加 Component 后缀,所以如果在编写 first-component,那么其组件名就是 FirstComponentComponent

2、导入这些新组件

要使用这些新组件,请把它们导入到该文件顶部的 AppRoutingModule 中,具体如下:

// 在app-routing.module.ts 文件中
import { FirstComponent } from './first/first.component';
import { SecondComponent } from './second/second.component';

3、定义一个基本路由

创建路由有三个基本的构建块。 把 AppRoutingModule 导入 AppModule 并把它添加到 imports 数组中。 Angular CLI 会为你执行这一步骤。但是,如果要手动创建应用或使用现存的非 CLI 应用,请验证导入和配置是否正确。

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module'; // CLI imports AppRoutingModule
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule // CLI adds AppRoutingModule to the AppModule's imports array
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

1、把 RouterModuleRoutes 导入到你的路由模块中。 Angular CLI 会自动执行这一步骤。CLI 还为你的路由设置了Routes数组,并为@NgModule()配置了importsexports数组。

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router'; // CLI imports router

const routes: Routes = []; // sets up routes constant where you define your routes

// configures NgModule imports and exports
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

2、在Routes数组中定义你的路由。 这个数组中的每个路由都是一个包含两个属性的 JavaScript 对象。第一个属性path定义了该路由的URL路径。第二个属性component定义了要让 Angular 用作相应路径的组件。

const routes: Routes = [
  { path: 'first-component', component: FirstComponent },
  { path: 'second-component', component: SecondComponent },
];

3、最后把这些路由添加到你的应用中。 现在你已经定义了路由,可以把它们添加到应用中了。首先,添加到这两个组件的链接。把要添加路由的链接赋值给routerLink属性。将属性的值设置为该组件,以便在用户点击各个链接时显示这个值。接下来,修改组件模板以包含<router-outlet>标签。该元素会通知 Angular,你可以用所选路由的组件更新应用的视图。

<!--app.component.html-->

<h1>Angular Router App</h1>
<!-- This nav gives you links to click, which tells the router which route to use (defined in the routes constant in  AppRoutingModule) -->
<nav>
  <ul>
    <li><a routerLink="/first-component" routerLinkActive="active">First Component</a></li>
    <li><a routerLink="/second-component" routerLinkActive="active">Second Component</a></li>
  </ul>
</nav>
<!-- The routed views render in the <router-outlet>-->
<router-outlet></router-outlet>

路由顺序

路由的顺序很重要,因为Router在匹配路由时使用“先到先得”策略,所以应该在不那么具体的路由前面放置更具体的路由。首先列出静态路径的路由,然后是一个与默认路由匹配的空路径路由。通配符路由是最后一个,因为它匹配每一个URL,只有当其它路由都没有匹配时,Router才会选择它。

设置通配符路由

当用户试图导航到那些不存在的应用部件时,在正常的应用中应该能得到很好的处理。要在应用中添加此功能,需要设置通配符路由。当所请求的URL与任何路由器路径都不匹配时,Angular路由器就会选择这个路由。

要设置通配符路由,请在routes定义中添加以下代码。

{ path: '**', component: PageNotFoundComponent }

设置重定向

要设置重定向,请使用重定向源的path、要重定向目标的component和一个pathMatch值来配置路由,以告诉路由器该如何匹配URL

const routes: Routes = [
  { path: 'first-component', component: FirstComponent },
  { path: 'second-component', component: SecondComponent },
  { path: '',   redirectTo: '/first-component', pathMatch: 'full' }, // redirect to `first-component`
  { path: '**', component: PageNotFoundComponent },  // Wildcard route for a 404 page
];

路由传参

1、动态路由

1、首先在app-routing.module.ts文件中的routes数组中配置动态路由

{
    path: 'first-component/:id',
    component: FirstComponent,
}

2、在html页面中跳转传值

<!--动态路由传值-->
<a routerLink="/first-component/3" routerLinkActive="active">First Component</a>
<!--或者-->
<a [routerLink]="['/first-component/',3]">First Component</a>

2、query形式传参

在html页面中的routerLinka标签中直接传参

<a routerLink="/second-component" [queryParams]="{name: '小笑残虹'}" routerLinkActive="active">
  Second Component
</a>

获取路由参数信息

1、获取动态路由的值

假如我要在FirstComponent组件中获取动态路由传递的值,第一步:在first.component.ts文件中引入ActivatedRoute模块,作为route注入到构造器函数中。

import {Component, OnInit} from '@angular/core';

import {ActivatedRoute} from '@angular/router';

@Component({
  selector: 'app-first',
  templateUrl: './first.component.html',
  styleUrls: ['./first.component.scss']
})
export class FirstComponent implements OnInit {
  constructor(private route: ActivatedRoute) {
  }
}

然后就可以通过route.params.subscribe()方法获取到传递的值了。

ngOnInit() {
    // 获取动态路由传值
    console.log(this.route.params);
    this.route.params.subscribe(data => {
      console.log(data);
      this.id = data.id
    })
  }

2、获取query形式传递过来的参数

假如我要在SecondComponent组件中获取query形式传递过来的值,第一步:在second.component.ts文件中引入ActivatedRoute模块,作为route注入到构造器函数中。

import {Component, OnInit} from '@angular/core';
import {ActivatedRoute} from '@angular/router';

@Component({
  selector: 'app-second',
  templateUrl: './second.component.html',
  styleUrls: ['./second.component.scss']
})
export class SecondComponent implements OnInit {
  constructor(
    private route: ActivatedRoute,
  ) {
  }
}

然后就可以通过route.queryParams.subscribe()方法获取到传递的值了。

ngOnInit() {
    // 获取params传值
    this.route.queryParams.subscribe(params => {
      console.log(params);
      this.name = params['name'];
    });
  }

js跳转路由(编程式导航)

我要在AppComponent组件中演示编程式导航,所以首先要在app.component.ts中引入Router模块,并且在constructor函数中进行初始化,引入NavigationExtras模块是为了进行query形式传参,NavigationExtras模块不需要在constructor构造器函数中初始化。

import { Router, NavigationExtras } from '@angular/router'

constructor(private router: Router) {}

1、js跳转普通路由

首先在页面上定义一个按钮用来模拟跳转

<button (click)="goHome()">js跳转路由</button>

通过router.navigate()方法进行跳转

// js跳转普通路由
goHome() {
  this.router.navigate(['/home'])
}

2、js跳转动态路由

<!-- js动态路由跳转 -->
<button (click)="goToPath()">js动态路由跳转</button>

通过router.navigate()方法进行跳转

// js跳转动态路由
goToPath() {
  // 路由跳转 适合普通路由和动态路由
  this.router.navigate(['/first-component/', '123'])
}

3、js跳转路由query形式传参

注意:需要引入NavigationExtras模块

<button (click)="queryRoute()">js跳转路由query传参</button>

通过router.navigate()方法进行跳转

// js跳转路由query传参 (get传值)
queryRoute() {
    let queryParams: NavigationExtras = {
      queryParams: {
        name: '王者荣耀'
      }
  }

  this.router.navigate(['/second-component'], queryParams);
}

嵌套路由

随着你的应用变得越来越复杂,你可能要创建一些根组件之外的相对路由。这些嵌套路由类型称为子路由。这意味着你要为你的应用添加第二 ,因为它是AppComponent之外的另一个<router-outlet>

在这个例子中,还有两个子组件,child-achild-b。这里的FirstComponent有它自己的<nav>AppComponent之外的第二<router-outlet>

<h2>First Component</h2>

<nav>
  <ul>
    <li><a routerLink="child-a">Child A</a></li>
    <li><a routerLink="child-b">Child B</a></li>
  </ul>
</nav>

<router-outlet></router-outlet>

子路由和其它路由一样,同时需要 path 和 component。唯一的区别是你要把子路由放在父路由的 children 数组中。

const routes: Routes = [
  {
    path: 'first-component',
    component: FirstComponent, // this is the component with the <router-outlet> in the template
    children: [
      {
        path: 'child-a', // child route path
        component: ChildAComponent, // child route component that the router renders
      },
      {
        path: 'child-b',
        component: ChildBComponent, // another child route component that the router renders
      },
    ],
  },
];

三、HTTP客户端篇

使用HTTP与后端服务进行通信。大多数前端应用都要通过HTTP协议与服务器通讯,才能下载或上传数据并访问其它后端服务。Angular 给应用提供了一个简化的HTTP客户端 API,也就是@angular/common/http中的HttpClient服务类。

HTTP客户端服务提供了以下主要功能。

  • 请求类型化响应对象的能力。

  • 简化的错误处理。

  • 各种特性的可测试性。

  • 请求和响应的拦截机制。

服务器通讯的准备工作

1、要想使用HttpClient,就要先在app.module.ts中导入AngularHttpClientModule。大多数应用都会在根模块AppModule中导入它。

// app/app.module.ts
  
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  imports: [
    BrowserModule,
    // import HttpClientModule after BrowserModule.
    HttpClientModule,
  ],
  declarations: [
    AppComponent,
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule {}

2、在用到的地方引入HttpClient并在构造器函数中声明。(我将要在NewsComponent组件中使用,所以在news.component.ts中引入HttpClient并在构造器函数中声明)

// news.component.ts
  
import {HttpClient} from "@angular/common/http";
  
constructor(public http:HttpClient) { }

get 请求数据

1、在news.component.html文件中定义一个按钮,来实现get方法请求服务器数据。

<button (click)="getData()">get请求</button>

2、在news.component.ts文件中实现getData()方法,get请求接口可以使用开源的https:httpbin.org/get

// get请求
  getData() {
    // https:httpbin.org/get
    // https://httpbin.org/post
    let api = 'http://a.itying.com/api/productlist';
    this.http.get(api).subscribe((res: any) => {
      console.log(res);
      this.list = res.result;
    })
  }

post 提交数据

Angular5.x 以后getpost和服务器交互使用的是HttpClientModule模块。

1、在app.module.ts中引入HttpClientModule并注入

import {HttpClientModule} from '@angular/common/http';
  
imports: [ BrowserModule, HttpClientModule ]

2、在用到的地方引入HttpClientHttpHeaders模块并在构造器函数中声明HttpClient

news.component.html文件中定义一个按钮,来实现post请求服务器数据。

<button (click)="doLogin()">post提交数据</button>

2、在news.component.ts文件中实现doLogin()方法,post请求接口可以使用开源的https:httpbin.org/post

// post请求
  doLogin() {
    const httpOptions = {
      headers: new HttpHeaders({"content-type": 'application/json'})
    };
    let url = 'https://httpbin.org/post';
    this.http.post(url, {name: '小笑残虹', age: 18}, httpOptions).subscribe((res: any) => {
      console.log(res);
      this.postDate = res.json;
    })
  }

Jsonp 请求数据

1、在app.module.ts中引入HttpClientModuleHttpClientJsonpModule 并注入。

import {HttpClientModule,HttpClientJsonpModule} from '@angular/common/http';
  
imports: [ BrowserModule, HttpClientModule, HttpClientJsonpModule ]

2、在用到的地方引入HttpClient并在构造函数声明。

// news.component.ts
  
import {HttpClient} from "@angular/common/http";
constructor(public http:HttpClient) { }
<button (click)="getJsonp()">jsonp处理跨域</button>
// jsonp 跨域请求
  getJsonp() {
    /*
    * http://a.itying.com/api/productlist?callback=xxx
    * http://a.itying.com/api/productlist?cb=xxx
    * */
    let api = 'http://a.itying.com/api/productlist';
    this.http.jsonp(api, 'callback').subscribe((res: any) => {
      console.log(res);
      this.list = res.result;
    })
  }

Angular 中使用第三方模块 axios 请求数据

1、安装 axios

npm install axios --save

2、用到的地方引入 axios

import axios from 'axios';

3、看axios文档使用

axios.get('/user?ID=12345') 
  .then(function (response){ 
  // handle success console.log(response); 
  }).catch(function (error) {
  // handle error console.log(error); 
  }).then(function () { 
  // always executed 
  });

下面,我们创建一个HttpService来使用axios,在这里可以对axios进行一些封装。

// service/http-service.service.ts
  
import {Injectable} from '@angular/core';

import axios from 'axios';

@Injectable({
  providedIn: 'root'
})
export class HttpServiceService {

  constructor() {
  }

  axiosGet(api) {
    return new Promise((resolve, reject) => {
      axios.get(api)
        .then(res => {
          resolve(res);
        })
    })
  }
}

然后在app.module.ts中引入HttpServiceService服务并注入到providers数组中。

// app.module.ts
  
// 引入服务
import {HttpServiceService} from './service/http-service.service';
  
// 注入服务
providers: [HttpServiceService],

之后在NewsComponent组件中使用HttpServiceService服务

// news.component.ts
  
// 引入服务
import {HttpServiceService} from '../service/http-service.service'

// 初始化
constructor(public httpService: HttpServiceService) {}
<!--news.component.html-->
<button (click)="axiosGetData()">通过axios获取数据</button>
// news.component.ts
  
// axios get方法获取数据
  axiosGetData() {
    let api = 'http://a.itying.com/api/productlist';
    this.httpService.axiosGet(api).then((res: any) => {
      console.log(res);
      this.list = res.data.result;
    })
  }