本系列笔记主要参考:
Angular学习视频
Angular官方文档
Angular系列笔记
特此感谢!
目录
- 1.Angular 介绍
- 2.Angular 环境搭建、创建 Angular 项目、运行 Angular 项目
- 2.1.环境搭建
- 2.2.创建 Angular 项目
- 2.3.运行项目
- 3.Angular 目录结构分析
- 3.1.目录结构分析
- 3.2.app.module.ts 分析
- 4.Angular 组件以及组件里面的模板
- 4.1.创建和使用组件
- 4.2.Angular 绑定数据
- 4.3.绑定属性
- 4.4.数据循环 \*ngFor
- 4.5.条件判断 ngIf
- 4.6.ngSwitch
- 4.7.执行事件 (click)
- 4.8.双向数据绑定(重要)
- 4.9.[ngClass] 、[ngStyle]
- 4.10.管道
- 4.11.例子:Angular 表单
- 5.Angular 中的服务以及自定义服务
- 5.1.Angular 中的服务
- 5.2.例子:TodoList 和搜索历史缓存数据功能
- 6.Angular 中的 Dom 操作以及@ViewChild 以及 Angular 执行 css3 动画
- 6.1.Angular 中的 DOM 操作
- 6.2.父子组件中通过 ViewChild 调用子组件的方法
- 6.3.实例:Angular 执行 css3 动画
- 7.Angular 中父子组件以及组件之间通讯
- 7.1.父组件给子组件传值
- 7.1.1.@input
- 7.1.2.@ViewChild
- 7.2.子组件通过@Output 触发父组件的方法
- 7.3.非父子组件通信
- 8.Angular 的生命周期
- 9.Rxjs 异步数据流编程-Rxjs 快速入门教程
- 9.1.Rxjs 介绍
- 9.2.RXJS 的使用过程
- 9.3.目前常见的异步编程方法
- 9.4.Rxjs 区别于 Promise 的几种应用
- 9.4.1.Rxjs unsubscribe 取消订阅
- 9.4.2.Rxjs 订阅后多次执行
- 9.5. Angualr6.x 之前使用 Rxjs 的工具函数 map filter
- 9.6.Rxjs 的延迟执行
- 10. Angular 中的数据交互(get jsonp post )
- 10.1.Angular get 请求数据
- 10.2.Angular post 提交数据
- 10.3.Angular Jsonp 请求数据
- 10.4.Angular 中使用第三方模块 axios 请求数据
- 11.Angular 中的路由
- 11.1.Angular 中的路由介绍
- 11.2.Angular 中的路由使用
- 11.3.动态路由
- 11.3.1.动态路由基本使用
- 11.3.2.动态路由的 JS 跳转
- 11.4.路由 get 传值 js 跳转
- 11.5.父子路由
- 12.Angular 中自定义模块
- 12.1.Angular 内置模块
- 12.2.angular 自定义模块
- 12.2.1.angular 自定义模块的使用
- 12.2.2.angular 自定义模块实现懒加载
1.Angular 介绍
Angular 是一个基于 TypeScript 构建的开发平台。它包括:
- 一个基于组件的框架,用于构建可伸缩的 Web 应用
- 一组完美集成的库,涵盖各种功能,包括路由、表单管理、客户端-服务器通信等
- 一套开发工具,可帮助你开发、构建、测试和更新代码
Angular 是谷歌开发的一款开源的 web 前端框架,诞生于 2009 年,由 Misko Hevery 等人创建,后为 Google 所收购。是一款优秀的前端 JS 框架,已经被用于 Google 的多款产品当中。
根据项目数统计 angular(1.x 、2.x 、4.x、5.x、6.x、7.x 、8.x、9.x)是现在网上使用量最大的框架。
Angular 基于 TypeScript 和 react、vue 相比 Angular 更适合中大型企业级项目。
2.Angular 环境搭建、创建 Angular 项目、运行 Angular 项目
2.1.环境搭建
- 下载安装 NodeJs
- 安装 Angular CLI
npm install -g @angular/cli
ng --version //校验安装成功与否
相关配置可以参考如下链接:https://blog.csdn.net/weixin_43938259/article/details/104792233
2.2.创建 Angular 项目
- 命令行进入到准备创建项目的文件夹下
- 新建项目
ng new angularDemo
//或者这样,跳过下载依赖包,后续再下载
ng new angularDemo --skip-install
cd angularDemo
cnpm install
中间会提示是否创建带路由和样式编辑器,按需选取
2.3.运行项目
终端输入如下命令,即可运行项目
cd angularDemo
ng serve --open
//或者这样
ng serve --o
//或者这样
ng serve
3.Angular 目录结构分析
3.1.目录结构分析
- node_modules 第三方依赖包存放目录
- e2e 端到端的测试目录 用来做自动测试的
- src 应用源代码目录
|- app 包含定义应用逻辑和数据的组件文件
|- app.component.ts 为应用的根组件定义逻辑,名为 AppComponent。当你向应用中添加组件和服务时,与这个根组件相关联的视图就会成为视图树的根。
|- app.component.html 定义与根组件 AppComponent 关联的 HTML 模板。
|- app.component.css 为根组件 AppComponent 定义了基本的 CSS 样式表。
|- app.component.spec.ts 为根组件 AppComponent 定义了一个单元测试。
|- app.module.ts 为应用的根组件定义逻辑,名为 AppComponent。定义了名为 AppModule 的根模块,它会告诉 Angular 如何组装应用。这里最初只声明一个 AppComponent。当你向应用中添加更多组件时,它们也必须在这里声明。
|- assets目录 资源目录,存储静态资源的 比如图片
|- environments 目录环境配置。Angular是支持多环境开发的,我们可以在不同的环境下(开发环境,测试环境,生产环境)共用一套代码,主要用来配置环境的
|- index.html 整个应用的根html,程序启动就是访问这个页面
|- main.ts 整个项目的入口点,Angular通过这个文件来启动项目
|- polyfills.ts 主要是用来导入一些必要库,为了让Angular能正常运行在老版本下
|- styles.css 主要是放一些全局的样式
|- tsconfig.app.json TypeScript编译器的配置,添加第三方依赖的时候会修改这个文件
|- tsconfig.spec.json 不用管
|- test.ts 也是自动化测试用的
|- typings.d.ts不用管
- .angular-cli.json Angular命令行工具的配置文件。后期可能会去修改它,引一些其他的第三方的包
比如jquery等
- karma.conf.js karma是单元测试的执行器,karma.conf.js是karma的配置文件
- package.json
这是一个标准的npm工具的配置文件,这个文件里面列出了该应用程序所使用的第三方依赖包。实际上我们在新建项目的时候,等了半天就是在下载第三方依赖包。下载完成后会放在node_modules这个目录中,后期我们可能会修改这个文件。
- protractor.conf.js 也是一个做自动化测试的配置文件
- README.md 说明文件
- tslint.json 是tslint的配置文件,用来定义TypeScript代码质量检查的规则,不用管它
3.2.app.module.ts 分析
app.module.ts
4.Angular 组件以及组件里面的模板
4.1.创建和使用组件
// 1.创建组件 ng g component 目录名/组件名
ng g component components/header
// 2.使用组件 <app-header></app-header>
组件 ts 文件分析
import { Component, OnInit } from "@angular/core"; /*引入 angular 核心*/
@Component({
selector: "app-header" /*使用这个组件的名称*/,
templateUrl: "./header.component.html" /*html 模板*/,
styleUrls: ["./header.component.css"] /*css 样式*/,
})
export class HeaderComponent implements OnInit {
/*实现接口*/
constructor() {
/*构造函数*/
}
ngOnInit() {
/*初始化加载的生命周期函数*/
}
}
4.2.Angular 绑定数据
- 组件 ts 文件中声明属性
声明属性的几种方式:
/*
public 共有 *(默认) 可以在这个类里面使用、也可以在类外面使用
protected 保护类型 他只有在当前类和它的子类里面可以访问
private 私有 只有在当前类才可以访问这个属性
*/
public title:string = 'this is a title'
- 数据文本绑定-{{}}
<h1>{{title}}</h1>
<div>1+1={{1+1}}<!--还可以运算--></div>
- 绑定 HTML
this.h = "<h2>这是一个 h2 用[innerHTML]来解析</h2>";
<div [innerHTML]="h"></div>
4.3.绑定属性
<div [id]="id" [title]="msg">调试工具看看我的属性</div>
4.4.数据循环 *ngFor
<!--普通循环-->
<ul>
<li *ngFor="let item of list">
{{item}}
</li>
</ul>
<!-- 循环的时候设置 key -->
<ul>
<li *ngFor="let item of list;let i = index;">
{{item}} --{{i}}
</li>
</ul>
<!-- template 循环数据 -->
<ul>
<li template="ngFor let item of list">
{{item}}
</li>
</ul>
4.5.条件判断 ngIf
<!-- 常用 -->
<p *ngIf="flag">这是 ngIF 判断是否显示</p>
<p template="ngIf flag">这是 ngIF 判断是否显示</p>
4.6.ngSwitch
<ul [ngSwitch]="score">
<li *ngSwitchCase="1">已支付</li>
<li *ngSwitchCase="2">订单已经确认</li>
<li *ngSwitchCase="3">已发货</li>
<li *ngSwitchDefault>无效</li>
</ul>
4.7.执行事件 (click)
<button class="button" (click)="getData()">点击按钮触发事件</button>
<button class="button" (click)="setData()">点击按钮设置数据</button>
getData(){ /*自定义方法获取数据*/
//获取
alert(this.msg);
}
setData(){
//设置值
this.msg='这是设置的值';
}
键盘事件
<input type="text" (keyup)="keyUpFn($event)" />
keyUpFn(e){
console.log(e)
}
// e可能会报错,因为现阶段版本要求加上类型了,改成这样keyUpFn(e:any){...}
4.8.双向数据绑定(重要)
- app.module.ts 引入 FormsModule
...
import { FormsModule } from '@angular/forms';
...
imports: [
BrowserModule,
FormsModule
],
- 组件中使用
<!-- 需要在组件中声明inputValue -->
<input type="text" [(ngModel)]="inputValue" /> {{inputValue}}
4.9.[ngClass] 、[ngStyle]
<!-- 'xxx':xx 类名:布尔值-->
<div [ngClass]="{'red': true, 'blue': false}">这是一个 div</div>
public flag=false;
<div [ngClass]="{'red': flag, 'blue': !flag}">这是一个 div</div>
public arr = [1, 3, 4, 5, 6];
<ul>
<li *ngFor="let item of arr, let i = index">
<span [ngClass]="{'red': i==0}">{{item}}</span>
</li>
</ul>
<div [ngStyle]="{'background-color':'green'}">你好 ngStyle</div>
public attr='red';
<div [ngStyle]="{'background-color':attr}">你好 ngStyle</div>
4.10.管道
<p>{{today | date:'yyyy-MM-dd HH:mm:ss' }}</p>
public today=new Date();
其他管道:http://bbs.itying.com/topic/5bf519657e9f5911d41f2a34
4.11.例子:Angular 表单
实现效果:
具体实施:https://www.bilibili.com/video/BV1X4411472m?p=5
参见 angularDemo4
5.Angular 中的服务以及自定义服务
5.1.Angular 中的服务
- Angular 引入服务的原因
在 Angular 应用程序中,很多功能都需要共享数据和业务逻辑,例如:HTTP 请求、表单验证、日志记录等。这些功能如果放到组件中实现,会导致代码冗长、难以维护和测试。为了解决这个问题,Angular 提供了服务的概念。
通过使用服务,我们可以把应用程序中的通用代码提取出来,封装成一个可注入的类,并且可以在整个应用程序中共享使用。这样做有助于提高代码的复用性、可维护性和可测试性。同时也可以使组件更加专注于展示数据和处理用户交互。
- 服务的定义
服务是 Angular 中的一个可注入类,它通常用于实现某种特定的功能,例如:HTTP 请求、表单验证、日志记录等。服务中包含了一些属性和方法,可以供其他组件或服务调用。服务的定义方式如下:
- 创建一个普通的 Typescript 类。
- 在类上添加@Injectable()装饰器,表示该类可以被注入到其他组件或服务中使用。这个装饰器告诉 Angular 依赖注入系统,这个类是一个服务。
- 在服务类中定义属性和方法。这些属性和方法可以用来存储数据、操作数据或执行某些任务。
import { Injectable } from "@angular/core";
/* @Injectable() 服务
注意,这个新的服务导入了 Angular 的 Injectable 符号,并且给这个服务类添加了 @Injectable() 装饰器。 它把这个类标记为依赖注入系统的参与者之一。
@Injectable() 装饰器会接受该服务的元数据对象,就像 @Component() 对组件类的作用一样。
*/
/* 默认情况下,Angular CLI 命令 ng generate service 会通过给 @Injectable() 装饰器添加 providedIn: 'root' 元数据的形式,用根注入器将你的服务注册成为提供者。 */
@Injectable({
providedIn: "root",
})
export class MyService {
private data: any;
getData() {
return this.data;
}
setData(data: any) {
this.data = data;
}
}
上面的代码定义了一个名为 MyService 的服务类,它包含了一个名为 data 的私有属性,以及两个公共方法:getData 和 setData。这个服务可以在应用程序中被注入到其他组件或服务中使用。
- 服务的好处
- 提高代码复用性:服务使得代码可以更容易地被复用,因为它们可以在整个应用程序中被注入使用。
- 降低代码耦合度:通过服务,我们可以把业务逻辑从组件中分离出来,使得组件变得更加专注于展示数据和处理用户交互。这样做可以降低代码之间的耦合度,使得应用程序更容易维护和测试。
- 易于测试:由于服务可以在整个应用程序中被共享使用,所以我们可以更容易地编写单元测试和集成测试,以确保我们的应用程序的稳定性和正确性。
更好的模块化管理:服务可以帮助我们更好地管理应用程序的各个模块,使得代码更容易扩展和维护。
- 服务的使用
- 创建服务
首先,在 Angular 应用程序中创建一个服务。可以通过运行以下命令来生成一个服务:
ng generate service my-service
这将在项目中创建一个名为“my-service”的服务,并在 app.module.ts 文件中注册它。
- 注入服务
要使用服务,需要将其注入到组件或其他服务中。使用构造函数注入服务:
// 引入
import { MyService } from '../myservice.service';
// 注入服务
constructor(private myService: MyService) { }
- 添加方法
现在已经注入了服务,可以在组件中使用它了。例如,如果在“my-service”服务中有一个名为“getData”的方法,则可以在组件中调用该方法:
getData(){
this.data = this.myService.getData()
}
PS:一般会在 ngOnInit()方法中调用 getData()这个方法,而不是在构造函数中
让构造函数保持简单,只做最小化的初始化操作,比如把构造函数的参数赋值给属性。构造函数不应该做任何事。它当然不应该调用某个函数来向远端服务(比如真实的数据服务)发起 HTTP 请求。
而是选择在 ngOnInit 生命周期钩子中调用 getData(),之后 Angular 会在构造出 该组件 的实例之后的某个合适的时机调用 ngOnInit()。
5.2.例子:TodoList 和搜索历史缓存数据功能
- 提出来的服务
- storage.service.ts
import { Injectable } from "@angular/core";
@Injectable({
providedIn: "root",
})
export class StorageService {
constructor() {}
set(key: string, value: any) {
localStorage.setItem(key, JSON.stringify(value));
}
get(key: string) {
return JSON.parse(localStorage.getItem(key) || "");
}
remove(key: string) {
localStorage.removeItem(key);
}
}
- app.module.ts
import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { FormsModule } from "@angular/forms";
import { AppComponent } from "./app.component";
import { SearchComponent } from "./components/search/search.component";
import { TodolistComponent } from "./components/todolist/todolist.component";
//引入并且配置服务
import { StorageService } from "./services/storage.service";
FormsModule;
NgModule;
@NgModule({
declarations: [AppComponent, SearchComponent, TodolistComponent],
imports: [BrowserModule, FormsModule],
providers: [StorageService],
bootstrap: [AppComponent],
})
export class AppModule {}
- app.component.html
<app-todolist></app-todolist>
- 实例一:TodoList
- todolist.component.html
<h2>todoList</h2>
<div class="todolist">
<input
id="todoInput"
type="text"
[(ngModel)]="keyword"
(keyup)="doAdd($event)"
/>
<hr />
<h3>待办事项</h3>
<ul>
<li *ngFor="let item of todolist,let key = index" [hidden]="item.status==1">
<input
type="checkbox"
[(ngModel)]="item.status"
(change)="checkBoxChange()"
/>{{item.title}} <button (click)="deleteTodo(key)">X</button>
</li>
</ul>
<h3>已完成事项</h3>
<ul>
<li *ngFor="let item of todolist,let key = index" [hidden]="item.status==0">
<input type="checkbox" [(ngModel)]="item.status" />{{item.title}}
<button (click)="deleteTodo(key)">X</button>
</li>
</ul>
</div>
- todolist.component.scss
h2 {
text-align: center;
}
.todolist {
width: 400px;
margin: 20px auto;
#todoInput {
margin-bottom: 20px;
width: 300px;
height: 32px;
}
li {
line-height: 60px;
}
}
- todolist.component.ts
/*
1、ng g service services/storage
2、app.module.ts 里面引入创建的服务 并且声明import { storageService } from './services/storage.serviceproviders:[storageService]
3、在用到的组件里面
//引入服务
import { storageService ] from '../../services/storage. service';
//初始化
constructor(public storage:StorageService){
console.log(storage);
}
*/
import { Component } from "@angular/core";
//引入服务
import { StorageService } from "src/app/services/storage.service";
@Component({
selector: "app-todolist",
templateUrl: "./todolist.component.html",
styleUrls: ["./todolist.component.scss"],
})
export class TodolistComponent {
public keyword: string = "";
public todolist: any[] = [];
//服务注入
constructor(public storage: StorageService) {
console.log(storage);
}
ngOnInit() {
//服务使用
var todolist = this.storage.get("todolist");
if (todolist) {
this.todolist = todolist;
}
}
doAdd(e: any) {
if (e.keyCode == 13) {
// console.log(this.keyword)
if (this.keyword !== "") {
if (!this.todoListHasKeyword(this.todolist, this.keyword)) {
this.todolist.push({ title: this.keyword, status: false });
//******数据持久化******
this.storage.set("todolist", this.todolist);
} else {
alert("数据已经存在!");
}
this.keyword = "";
}
}
}
deleteTodo(key: any) {
this.todolist.splice(key, 1);
//******数据持久化******
this.storage.set("todolist", this.todolist);
}
todoListHasKeyword(todolist: any[], keyword: any) {
let flag: boolean = false;
todolist.forEach((item) => {
if (item.title == keyword) flag = true;
});
return flag;
}
//监听多选框变化事件
checkBoxChange() {
console.log("change");
this.storage.set("todolist", this.todolist);
}
}
- 实列二:搜索历史缓存
- search.component.html
<div class="search">
<input type="text" [(ngModel)]="keyword" />
<button (click)="doSearch()">搜索</button>
<hr />
<ul>
<li *ngFor="let item of historyList,let key = index">
{{item}}
<button (click)="deleteHistory(key)">x</button>
</li>
</ul>
</div>
2.search.component.scss
.search {
width: 400px;
margin: 20px auto;
input {
margin-bottom: 20px;
width: 300px;
height: 32px;
}
button {
height: 32px;
width: 80px;
}
li {
button {
width: 20px;
height: 20px;
border-radius: 50%;
}
}
}
- search.component.ts
import { Component } from "@angular/core";
//引入服务
import { StorageService } from "src/app/services/storage.service";
@Component({
selector: "app-search",
templateUrl: "./search.component.html",
styleUrls: ["./search.component.scss"],
})
export class SearchComponent {
public keyword: string = "";
public historyList: string[] = [];
constructor(public storage: StorageService) {
console.log(storage);
}
ngOnInit() {
//页面刷新会出发这个生命周期函数
var searchList = this.storage.get("searchList");
if (searchList) {
this.historyList = searchList;
}
}
doSearch() {
// console.log(this.keyword)
if (this.keyword !== "") {
// let index = this.historyList.indexOf(this.keyword)
// if(index != -1) this.historyList.splice(index,1)
// this.historyList.unshift(this.keyword)
if (this.historyList.indexOf(this.keyword) == -1) {
this.historyList.push(this.keyword);
//数据持久化
this.storage.set("searchList", this.historyList);
//重置keyword
this.keyword = "";
}
}
}
deleteHistory(key: number) {
// console.log(key)
this.historyList.splice(key, 1);
//数据持久化
this.storage.set("searchList", this.historyList);
}
}
6.Angular 中的 Dom 操作以及@ViewChild 以及 Angular 执行 css3 动画
6.1.Angular 中的 DOM 操作
- 原生 JS
ngAfterViewInit(){
let oBox1:any = document.getElementById('box1')
console.log(oBox1.innerHTML)
oBox1.style.color='blue'
}
- ViewChild
// ViewChild获取DOM节点
// 1.模板中给dom起名字 #xxx
<div #myBox>
我是一个Dom节点
</div>
// 2.在业务逻辑里面引入ViewChild并获取节点
import { ViewChild } from '@angular/core';
// 写在类里面,获取dom节点
@ViewChild('myBox') mybox:any;
// 3.ngAfterViewInit生命周期函数获取Dom
//使用节点
console.log(this.mybox.nativeElement)
this.mybox.nativeElement.style.width = '100px'
this.mybox.nativeElement.style.height = '50px'
this.mybox.nativeElement.style.background = 'red'
console.log(this.mybox.nativeElement.innerHTML);
比如:
<app-header #header></app-header>
<hr />
<div #myBox>我是一个Dom节点</div>
<button (click)="getChildRun()">获取子组件的方法</button>
//引入ViewChild
import { Component, ViewChild } from "@angular/core";
@Component({
selector: "app-news",
templateUrl: "./news.component.html",
styleUrls: ["./news.component.scss"],
})
export class NewsComponent {
// 获取dom节点
@ViewChild("myBox") mybox: any;
// 获取一个组件
@ViewChild("header") header: any;
constructor() {}
ngOnInit(): void {}
ngAfterViewInit(): void {
console.log(this.mybox.nativeElement);
this.mybox.nativeElement.style.width = "100px";
this.mybox.nativeElement.style.height = "50px";
this.mybox.nativeElement.style.background = "red";
console.log(this.mybox.nativeElement.innerHTML);
// //调用子组件的方法
// this.header.run()
}
getChildRun() {
this.header.run();
}
}
6.2.父子组件中通过 ViewChild 调用子组件的方法
- 调用子组件给子组件定义一个名称
<app-footer #footerChild></app-footer>
- 引入 ViewChild
import { Component, OnInit, ViewChild } from "@angular/core";
- ViewChild 和刚才的子组件关联起来
@ViewChild('footerChild') footer;
- 调用子组件
run(){
this.footer.footerRun();
}
6.3.实例:Angular 执行 css3 动画
transition.module.ts
import { Component } from "@angular/core";
@Component({
selector: "app-transition",
templateUrl: "./transition.component.html",
styleUrls: ["./transition.component.scss"],
})
export class TransitionComponent {
showAside() {
//原生js获取节点
var asideDom: any = document.getElementById("aside");
asideDom.style.transform = "translate(0,0)";
}
hideAside() {
//原生js获取节点
var asideDom: any = document.getElementById("aside");
asideDom.style.transform = "translate(100%,0)";
}
}
7.Angular 中父子组件以及组件之间通讯
7.1.父组件给子组件传值
7.1.1.@input
父组件不仅可以给子组件传递简单的数据,还可把自己的方法以及整个父组件传给子组件。
- 父组件调用子组件的时候传入数据
<app-header [msg]="msg"></app-header>
- 子组件引入 Input 模块
import { Component, OnInit, Input } from "@angular/core";
3.子组件中 @Input 接收父组件传过来的数据
export class HeaderComponent implements OnInit {
// Input
@Input() msg: string;
constructor() {}
ngOnInit() {}
}
- 子组件中使用父组件的数据
<h2>这是头部组件--{{msg}}</h2>
7.1.2.@ViewChild
- 调用子组件给子组件定义一个名称
<app-footer #footerChild></app-footer>
- 引入 ViewChild
import { Component, OnInit, ViewChild } from "@angular/core";
- ViewChild 和刚才的子组件关联起来
@ViewChild('footerChild') footer;
- 调用子组件
run(){
this.footer.footerRun();
}
7.2.子组件通过@Output 触发父组件的方法
- 子组件引入 Output 和 和 EventEmitter
import { Component, OnInit, Input, Output, EventEmitter } from "@angular/core";
- 子组件中实例化 EventEmitter
@Output() private outer=new EventEmitter<string>();
/*用 EventEmitter 和 output 装饰器配合使用 <string>指定类型变量*/
- 子组件通过 EventEmitter 对象 outer 实例广播数据
sendParent(){
this.outer.emit('msg from child')
}
- 父组件调用子组件的时候,定义接收事件 , outer 就是子组件的 EventEmitter 对象 outer
<app-header (outer)="runParent($event)"></app-header>
- 父组件接收到数据会调用自己的 runParent, 这个时候就能拿到子组件的数据
//接收子组件传递过来的数据
runParent(msg:string){
alert(msg);
}
7.3.非父子组件通信
- 公共的服务
- Localstorage (推荐)
- Cookie
8.Angular 的生命周期
Angular 中的生命周期函数
官方文档:https://www.angular.cn/guide/lifecycle-hooks
生命周期函数通俗的讲就是组件创建、组件更新、组件销毁的时候会触发的一系列的方法。
当 Angular 使用构造函数新建一个组件或指令后,就会按下面的顺序在特定时刻调用这些生命周期钩子方法。
9.Rxjs 异步数据流编程-Rxjs 快速入门教程
9.1.Rxjs 介绍
RxJS 是 ReactiveX 库的 JavaScript 实现。它是一种强大的库,用于使用可观察对象进行异步和事件驱动编程。它提供了一种功能强大且易于使用的方式来处理异步数据流、事件序列和响应式编程。
RxJS 可以帮助开发人员轻松处理异步代码、事件和基于流的数据,同时还可以保持代码的简洁性和可读性。RxJS 的主要优点之一是它采用函数响应式编程(FRP)的方法,这意味着可以使用链式调用和组合操作来处理数据流。
RxJS 中最常用的概念是 Observable、Observer 和 Subscription。Observable 表示一个可观察对象,也就是数据源,可以发出多个值或者一个 error 或者完结信号。Observer 只是一个监听器,用于处理从 Observable 中接收的值,可以是一个回调函数或者具有 next、error、complete 方法的对象。Subscription 表示 Observable 的执行,可以取消 Observable 的执行。
除了上述概念之外,RxJS 还提供了许多操作符,用于处理 Observable 流中的数据,例如 map、filter、reduce、merge 等等。通过使用这些操作符,可以将 RxJS 的数据流转换为任何开发人员需要的形式。
RxJS 适用于各种场景,包括 Web 应用程序、移动应用程序和服务器端应用程序。通过使用 RxJS,开发人员可以轻松创建高效、可读和可维护的代码,并处理异步任务、事件和流式数据。
RXJS 的优点
异步编程:RXJS 提供了一种强大的异步编程解决方案,使得处理异步事件变得更加容易和直观。
响应式编程:RXJS 提供了一种响应式编程范式,让我们可以轻松地创建基于数据流的应用程序。
可组合性:RXJS 的操作符具有高度可组合性,可以轻松地将它们组合起来以创建复杂的数据流。
具有丰富的操作符:RXJS 拥有丰富的操作符,包括 map、filter、reduce、scan 等等,这些操作符大大简化了数据转换和过滤的过程。
RXJS 的使用场景
RXJS 在 JavaScript 中的应用非常广泛,特别是在 Web 开发中。以下是一些场景:
处理异步请求:通过使用 Observable 对象,我们可以轻松地处理异步请求,并在请求完成时获取到数据。
处理用户输入:通过创建一个数据流,我们可以监听用户输入事件,然后进行相应的处理。
处理 UI 事件:同样,我们也可以监听 DOM 事件,并根据事件的类型进行相应的操作。
处理 WebSocket 连接:使用 RXJS 可以很方便地处理 WebSocket 连接,从而实现实时通信。
总的来说,RXJS 提供了一种灵活、可组合、可扩展的异步编程解决方案,可以在许多场景下使用。
9.2.RXJS 的使用过程
RxJS 是一个流处理库,它允许你以声明式和响应式的方式处理数据流。下面是使用 RxJS 的一些基本步骤:
- 安装 RxJS
可以使用 npm 或 yarn 来安装 RxJS。例如,通过 npm 安装可以使用以下命令:
npm install rxjs
- 引入 RxJS 模块
在需要使用 RxJS 的地方引入 RxJS 模块。例如,如果你想使用 Observable,则可以这样引入:
import { Observable } from "rxjs";
- 创建 Observable
使用 Observable.create()方法创建一个 Observable 对象。例如,你可以创建一个简单的 Observable,该 Observable 将发出某些值然后完成:
const observable = new Observable((observer) => {
observer.next(1);
observer.next(2);
observer.next(3);
observer.complete();
});
- 订阅 Observable
使用 Observable.subscribe()方法对 Observable 进行订阅,并定义处理程序以处理 Observable 发出的值。例如,可以定义一个回调函数来打印 Observable 发出的每个值:
observable.subscribe((value) => console.log(value));
- 处理 Observable
可以使用操作符(如 map、filter 等)对发出的值进行处理。例如,可以使用 map 操作符将每个值加倍:
observable
.pipe(map((value) => value * 2))
.subscribe((value) => console.log(value));
以上是 RxJS 的一些基本步骤。当然,RxJS 有更多的特性和操作符可供使用,具体取决于你的需求。
9.3.目前常见的异步编程方法
- 回调函数
//1、回调函数解决异步传参问题
getCallBackData(cb: any) {
setTimeout(() => {
var data = '张三--Callback';
// return data;
cb(data);
}, 1000);
}
// callback获取异步数据
let callbackData = this.request.getCallBackData((data: any) => {
console.log("callback:" + data);
});
-
事件监听/发布订阅
-
Promise
getPromiseData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
var data = '张三--Promise';
// return data;
resolve(data);
}, 3000);
});
}
// promise获取异步数据
let promiseData = this.request.getPromiseData();
promiseData.then((data) => {
console.log(data);
});
// console.log(promiseData);
- Rxjs
getRxjsData() {
return new Observable<any>((observer) => {
setTimeout(() => {
var data = '张三--Rxjs';
// return data;
observer.next(data);
// observer.error('失败返回这个')
}, 3000);
});
}
// Rxjs
let rxjsData = this.request.getRxjsData();
rxjsData.subscribe((data: any) => {
console.log(data);
});
从上面列子可以看到 RxJS 和 Promise 的基本用法非常类似,除了一些关键词不同。Promise 里面用的是 then()
和 resolve()
,而 RxJS 里面用的是 next()
和 subscribe()
。
从上面例子我们感觉 Promise 和 RxJS 的用法基本相似。其实Rxjs相比Promise 要强大很多
。比如 Rxjs 中可以中途撤回、Rxjs 可以发射多个值、Rxjs 提供了多种工具函数等等。
9.4.Rxjs 区别于 Promise 的几种应用
9.4.1.Rxjs unsubscribe 取消订阅
Promise 的创建之后,动作是无法撤回的。Observable 不一样,动作可以通过 unsbscribe() 方法中途撤回,而且 Observable 在内部做了智能的处理。
Promise 创建之后动作无法撤回:
let promise = new Promise((resolve) => {
setTimeout(() => {
resolve("---promise timeout---");
}, 2000);
});
promise.then((value) => console.log(value));
Rxjs 可以通过 unsubscribe() 可以撤回 subscribe 的动作:
let stream = new Observable((observer) => {
let timeout = setTimeout(() => {
clearTimeout(timeout);
observer.next("observable timeout");
}, 2000);
});
let disposable = stream.subscribe((value) => console.log(value));
setTimeout(() => {
//取消执行
disposable.unsubscribe();
}, 1000);
9.4.2.Rxjs 订阅后多次执行
如果我们想让异步里面的方法多次执行,比如下面代码。
这一点 Promise 是做不到的,对于 Promise 来说,最终结果要么 resole(兑现)、要么 reject(拒绝),而且都只能触发一次。如果在同一个 Promise 对象上多次调用 resolve 方法,则会抛异常。
而 Observable 不一样,它可以不断地触发下一个值,就像 next() 这个方法的名字所暗示的那样。
//多次执行
getPromiseIntervalData() {
return new Promise((resolve, reject) => {
setInterval(() => {
var data = '张三--Promise--interval';
// return data;
resolve(data);
}, 3000);
});
}
//promise多次执行
let promiseIntervalData = this.request.getPromiseIntervalData()
promiseIntervalData.then((data)=>{//只会执行一次
console.log(data);
})
getRxjsIntervalData() {
return new Observable<any>((observer) => {
setInterval(() => {
var data = '张三--Rxjs--interval';
// return data;
observer.next(data);
// observer.error('失败返回这个')
}, 3000);
});
}
//rxjs多次执行
let rxjsIntervalData = this.request.getRxjsIntervalData()
rxjsIntervalData.subscribe((data)=>{//可以多次执行
console.log(data);
})
9.5. Angualr6.x 之前使用 Rxjs 的工具函数 map filter
注意:Angular6 以后使用以前的 rxjs 方法,必须安装 rxjs-compat 模块才可以使用 map、filter 方法。
angular6 后官方使用的是 RXJS6 的新特性,所以官方给出了一个可以暂时延缓我们不需要修改 rsjx 代码的办法。
// 下载包
npm install rxjs-compat
// 引入rxjs
import {Observable} from 'rxjs';
import 'rxjs/Rx';
//使用
let stream= new Observable<any>(observer => {
let count = 0;
setInterval(() => {
observer.next(count++);
}, 1000);
});
// filter
stream.filter(val=>val%2==0).subscribe(value => console.log("filter>"+value));
// map
stream.map(value => {
return value * value
}).subscribe(value => console.log("map>"+value));
map()操作符将 Observable 发出的每个值映射
到另一个值,并将其作为新的Observable返回
。这使得您可以对 Observable 发出的值执行某些转换,例如将数字乘以 2、将字符串转换为大写字母等等。
与 map()类似,filter()也是一个操作符,用于过滤Observable发出的数据
。它基于给定的条件,只发出符合条件的值
。例如,如果我们有一个包含 1 到 10 的数字序列的 Observable,并且只想过滤出偶数,则可以使用 filter()操作符来完成。
9.6.Rxjs 的延迟执行
RxJS 提供了多种延迟执行的方式。以下是其中的几种:
delay()
操作符:将 Observable 的发射事件延迟一段时间后再发出。例如,of(1, 2, 3).pipe(delay(1000)) 会在 1 秒后依次发出 1、2、3 这三个值。timer()
操作符:在指定时间后发出一个值,可以配合其他操作符使用。例如,timer(1000).pipe(map(() => ‘hello’)) 会在 1 秒后发出字符串 ‘hello’。debounceTime()
和 throttleTime() 操作符:分别用于防抖和节流。debounceTime() 会在 Observable 发射值后等待一段时间,然后才发出这个值。如果在等待期间又有新的值发射,就会重新开始计时。throttleTime() 则是在一段时间内只发出第一个值,忽略后续的值,直到时间到了才允许下一个值通过。delayWhen()
操作符:将 Observable 的发射事件延迟一段时间后再发出,但是这段时间是由一个 Observable 决定的。例如,of(1, 2, 3).pipe(delayWhen(() => timer(1000))) 会在 1 秒后依次发出 1、2、3 这三个值。
除了上述操作符外,还有 timeout()
、delayWhen()
等操作符也可以实现延迟执行。在使用时需要根据实际需求选择合适的操作符。
import { Observable, fromEvent } from "rxjs";
import { map, filter, throttleTime } from "rxjs/operators";
var button = document.querySelector("button");
fromEvent(button, "click")
.pipe(throttleTime(1000))
.subscribe(() => console.log(`Clicked`));
10. Angular 中的数据交互(get jsonp post )
10.1.Angular get 请求数据
1、在 app.module.ts 中引入 HttpClientModule 并注入
import { HttpClientModule } from "@angular/common/http";
imports: [BrowserModule, HttpClientModule];
2、在用到的地方引入 HttpClient 并在构造函数声明
import { HttpClient } from "@angular/common/http";
constructor(public http:HttpClient) { }
3、get 请求数据
var api = "http://a.itying.com/api/productlist";
this.http.get(api).subscribe((response) => {
console.log(response);
});
10.2.Angular post 提交数据
Angular5.x 以后 get、post 和和服务器交互使用的是 HttpClientModule 模块。
1、在 app.module.ts 中引入 HttpClientModule 并注入
import { HttpClientModule } from "@angular/common/http";
imports: [BrowserModule, HttpClientModule];
2、在用到的地方引入 HttpClient 、HttpHeaders 并在构造函数声明 HttpClient
import { HttpClient, HttpHeaders } from "@angular/common/http";
constructor(public http:HttpClient) { }
3、post 提交数据
const httpOptions = {
headers: new HttpHeaders({ "Content-Type": "application/json" }),
};
var api = "http://127.0.0.1:3000/doLogin";
this.http
.post(api, { username: "张三", age: "20" }, httpOptions)
.subscribe((response) => {
console.log(response);
});
10.3.Angular Jsonp 请求数据
1、在 app.module.ts 中引入 HttpClientModule、HttpClientJsonpModule 并注入
import { HttpClientModule, HttpClientJsonpModule } from "@angular/common/http";
imports: [BrowserModule, HttpClientModule, HttpClientJsonpModule];
2、在用到的地方引入 HttpClient 并在构造函数声明
import { HttpClient } from "@angular/common/http";
constructor(public http:HttpClient) { }
3、jsonp 请求数据
var api = "http://a.itying.com/api/productlist";
this.http.jsonp(api, "callback").subscribe((response) => {
console.log(response);
});
10.4.Angular 中使用第三方模块 axios 请求数据
1、安装 axios
npm install axios --save
2、用到的地方引入 axios
import axios from "axios";
3、看文档使用
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
});
11.Angular 中的路由
11.1.Angular 中的路由介绍
Angular 中的路由可以用来实现不同组件之间的导航。它允许用户在应用程序中导航到不同的页面或视图,而无需刷新整个页面。
11.2.Angular 中的路由使用
以下是如何使用路由在 Angular 中进行导航的步骤:
- 安装 Angular 路由模块
您需要安装@angular/router 模块,这可以通过 npm 包管理器来完成。在终端窗口中运行以下命令:
npm install @angular/router --save
- 在 app.module.ts 中导入 RouterModule 和 Routes
打开 app.module.ts 文件,并导入 RouterModule 和 Routes:
import { RouterModule, Routes } from "@angular/router";
一般,我们在新建一个 angular 项目时,会选择是否添加路由,如果输入 y 即可自动帮我们引入第二步的包,所以通常 1、2 步可以省略
- 创建路由数组
在 app-routing.module.ts(创建项目时选择创建路由时自动生成该文件)(app.module.ts)中,创建一个路由数组,它将定义您的应用程序中的所有路由。示例如下:
const routes: Routes = [
{ path: "", component: HomeComponent },
{ path: "about", component: AboutComponent },
{ path: "contact", component: ContactComponent },
{ path: "**", redirectTo: "" }, //找不到页面时就重定向
];
在这个例子中,我们定义了三个路由:首页(/),关于页面(/about)和联系页面(/contact)。
- 将路由添加到 NgModule 中
在 app-routing.module.ts(app.module.ts)的 NgModule 元数据中,将路由添加到 imports 数组中:
@NgModule({
declarations: [AppComponent, HomeComponent, AboutComponent, ContactComponent],
imports: [BrowserModule, RouterModule.forRoot(routes)],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
这一步通常也可以省略,创建项目时已经生成了
- 在模板中添加 router-outlet
在 AppComponent 模板中添加一个 router-outlet 标签,这将成为您的应用程序中所有组件的路由容器:
<router-outlet></router-outlet>
- 创建导航链接
在应用程序的任何组件中,都可以使用 routerLink 指令创建导航链接。例如,在菜单组件中,您可以创建以下链接:
<!-- 快捷键 ng-router-link -->
<a routerLink="/">Home</a>
<a routerLink="/about">About</a>
<a routerLink="/contact">Contact</a>
- 触发导航
当用户单击其中一个导航链接时,Angular 会自动导航到相应的路由。如果您要通过编程方式进行导航,可以注入 Router 服务并使用 navigate()方法来实现:
import { Router } from '@angular/router';
constructor(private router: Router) {}
goToAboutPage() {
this.router.navigate(['/about']);
}
这就是如何在 Angular 中使用路由进行导航的基本步骤。
11.3.动态路由
11.3.1.动态路由基本使用
1.配置动态路由
const routes: Routes = [
{ path: "home", component: HomeComponent },
{ path: "news", component: NewsComponent },
// /:id 接收动态参数
{ path: "newscontent/:id", component: NewscontentComponent },
{
path: "",
redirectTo: "/home",
pathMatch: "full",
},
];
2.跳转传值(理不明白的话看视频更好理解)
<a [routerLink]="[ '/newscontent/',aid]">跳转到详情</a>
<a [routerLink]="/newscontent/{{aid}}">跳转到详情</a>
3.获取动态路由的值
import { ActivatedRoute } from "@angular/router";
constructor( private route: ActivatedRoute) {
}
ngOnInit() {
console.log(this.route.params);
this.route.params.subscribe(data=>this.id=data.id);
}
11.3.2.动态路由的 JS 跳转
- 引入
import { Router } from "@angular/router";
2.初始化
export class HomeComponent implements OnInit {
constructor(private router: Router) {}
ngOnInit() {}
goNews() {
// this.router.navigate(['/news', hero.id]);
this.router.navigate(["/news"]);
}
}
3.路由跳转
this.router.navigate(["/news", hero.id]);
11.4.路由 get 传值 js 跳转
- 引入 NavigationExtras
import { Router, NavigationExtras } from "@angular/router";
- 定义一个 goNewsContent 方法执行跳转 ,用 用 NavigationExtras 配置传参 。
goNewsContent(){
let navigationExtras: NavigationExtras = {
queryParams: { 'session_id': '123' },
fragment: 'anchor'
};
this.router.navigate(['/news'],navigationExtras);
}
- 获取 get 传值
constructor(private route: ActivatedRoute) {
console.log(this.route.queryParams);
}
11.5.父子路由
- 创建组件引入组件
import { NewsaddComponent } from "./components/newsadd/newsadd.component";
import { NewslistComponent } from "./components/newslist/newslist.component";
- 配置路由
{
path: 'news',
component:NewsComponent,
children: [
{
path:'newslist',
component:NewslistComponent
},
{
path:'newsadd',
component:NewsaddComponent
}]}
- 父组件中定义 router-outlet
<router-outlet></router-outlet>
// js跳转路由
/*
---------动态路由----------
1.引入声明模块
import { Router } from '@angular/router';
constructor(public router:Router){}
2.跳转
this.router.navigate(['/productcontent', '1234'])
//跳转并进行get传值
let queryParams:NavigationExtras = {
queryParams:{aid:111}
}
this.router.navigate(['/news'],queryParams)
-----------get传值-----------
1.引入声明模块
import { Router,NavigationExtras } from '@angular/router';
constructor(public router:Router){}
2.跳转
//跳转并进行get传值
let queryParams:NavigationExtras = {
queryParams:{aid:111}
}
12.Angular 中自定义模块
12.1.Angular 内置模块
Angular 是一个 JavaScript 框架,它的核心模块包括:
-
@angular/core
:这是最重要的模块之一,它定义了 Angular 应用程序所需的各种核心组件和服务。包括:Component、Directive、Pipe、Service 等。
-
@angular/common
:这个模块提供了许多通用的 Angular 功能,例如日期格式化、数字格式化、国际化支持等。包括:CommonModule、NgIf、NgForOf、DecimalPipe 等。
另外,Angular 还有其他几个常用的模块:
-
@angular/forms
:这个模块提供了表单构建、验证、交互等功能。包括:FormsModule、ReactiveFormsModule、FormControl 等。
-
@angular/platform-browser
:这个模块允许你在浏览器中运行 Angular 应用程序,并为其提供了 DOM、HTTP 等基本功能。包括:BrowserModule、platformBrowserDynamic、HttpClient 等。
通过引入这些模块可以让我们更方便地开发 Angular 应用程序。
12.2.angular 自定义模块
当我们项目比较小的时候可以不用自定义模块。但是当我们项目非常庞大的时候把所有的组件都挂载到根模块里面不是特别合适。所以这个时候我们就可以自定义模块来组织我们的项目。并且通过 Angular 自定义模块可以实现路由的懒加载。
12.2.1.angular 自定义模块的使用
- 命令行创建模块
// 新建模块
ng g module module/user
// 模块中新建组件
ng g component module/user //这一步执行完后就会和app的内容差不多
ng g component module/user --routing //还可以加上路由
ng g component module/user/components/profile
//模块中新建服务
ng g service module/user/services/common
- app.module.ts 引入自定义模块
// 引入自定义模块
import { UserModule } from './modules/user/user.module';
...
imports: [
...
UserModule,
...
],
- html 中调用模块
<h2>调用用户模块</h2>
<app-user></app-user>
这里一般我们只能调用到自定义模块的根组件,如果想要在外部调用自定义模块的子组件,比如 ProfileComponent,就需要在自定义模块(user.module.ts)中导出
exports:[UserComponent, ProfileComponent],//暴露组件,让其他模块里面可以使用暴露的组件
12.2.2.angular 自定义模块实现懒加载
懒加载—并没有引入相应组件,而是通过路由去跳转到相应界面
- app.module.ts 中并不导入自定义模块中的组件
import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, AppRoutingModule],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
- app.module.html
<header>
<a [routerLink]="[ '/user' ]">用户模块</a>
<a [routerLink]="[ '/product' ]">产品模块</a>
<a [routerLink]="[ '/article' ]">文章模块</a>
</header>
<router-outlet></router-outlet>
- app-routing.module.ts
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
// 懒加载---并没有引入相应组件,而是通过路由去跳转到相应界面
const routes: Routes = [
{
path: "user",
loadChildren: () =>
import("./module/user/user.module").then((x) => x.UserModule),
},
{
path: "product",
loadChildren: () =>
import("./module/product/product.module").then((x) => x.ProductModule),
},
{
path: "article",
loadChildren: () =>
import("./module/article/article.module").then((x) => x.ArticleModule),
},
{ path: "**", redirectTo: "user" },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
- user-routing.module.ts
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { ProfileComponent } from "./components/profile/profile.component";
import { AddressComponent } from "./components/address/address.component";
import { UserComponent } from "./user.component";
//第一个path!!!
const routes: Routes = [
{ path: "", component: UserComponent },
{ path: "profile", component: ProfileComponent },
{ path: "address", component: AddressComponent },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class UserRoutingModule {}