Angular 入坑 01

1. 目录结构

e2e —— 自动化测试
src —— 根目录
app —— App
app-routing.module.ts —— 项目路由配置文件
app.component.html —— App模板文件
app.component.scss —— App样式文件
app.component.spec.ts —— 单元测试文件
app.component.ts —— App组件的配置文件
app.module.ts —— NgModule配置温江
assets —— 放置静态资源
environments —— 放置环境配置
index.html —— 单页面应用的入口模板
main.ts —— 项目入口
styles.scss —— 全局样式表
test.ts —— 测试文件
.browserslistrc —— 浏览器兼容性配置
.editorconfig —— 浏览器格式化配置
.gitignore —— git忽略配置
angular.json —— Angular配置
karma.conf.js —— 自动化测试配置
package-lock.json —— Webpack 依赖锁定配置
package.json —— 项目依赖配置
README.md —— readme文件
tsconfig.app.json —— TypeScript配置文件
tsconfig.json —— App对于TypeScript检测的配置文件
tsconfig.spec.json —— TypeScript检测的配置文件

2. Angular CLI

2.1 初始化AngularCLI

安装 Angular CLI

npm install -g @angular/cli

创建一个 Angular 应用

ng new my-app

运行应用

ng serve --open

2.2 组件介绍以及组件创建

2.2.1 组件

组件的特征在于可以用于执行单个任务的代码块。可以从代码(类)中导出值。Angular应用程序被称为组件,可以使用许多组件构建应用程序。Angular的基本组件是可以从组件中导出的组件类

import { Component } from '@angular/core';
// 使用 @Component 装饰器声明当前这个类是一个组件
@Component({
  // 当前组件渲染的选择器
  selector: 'app-root',
  // 对应渲染的组件的模板
  templateUrl: './app.component.html',
  // 加载到当前模块的样式文件,可以接受多个样式表,是数组形式的
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  // 模板数据(可以使用插值表达式渲染到页面中)
  title = 'my-app';
}
2.2.2 创建组件

使用快速创建命令自动生成在 app.module中创建组件,其中ComponentName是组件名

ng g c ComponentName

新生成的组件目录中
test-component.component.html —— 组件模板文件
test-component.component.scss —— 组件样式文件
test-component.component.spec.ts —— 组件单元测试文件
test-component.component.ts —— 组件Js文件

3. 基本应用

3.1 插值表达式

插值表达式用法基本同 Vue 中的插值表达式
test-component.component.ts

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

@Component({
  selector: 'app-test-component',
  templateUrl: './test-component.component.html',
  styleUrls: ['./test-component.component.scss']
})
export class TestComponentComponent implements OnInit {

  name:String = 'Alex'

  constructor() { }

  ngOnInit(): void {
  }

}

test-component.component.html

test-component works!

{{ name }}

3.2 属性绑定

test-component.component.ts

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

@Component({
  selector: 'app-test-component',
  templateUrl: './test-component.component.html',
  styleUrls: ['./test-component.component.scss']
})
export class TestComponentComponent implements OnInit {

  name:String = 'Alex'
  h3Dom:Boolean = true
  constructor() { }

  ngOnInit(): void {
  }

}

test-component.component.html



属性绑定常量
属性绑定变量

hello H3

多类名绑定

类名条件渲染

class数组渲染

单一样式绑定

单一样式绑定

字符串形式多重样式绑定

对象形式多重样式绑定

ngClass绑定

ngStyle绑定, 也可以使用三元表达式进行书写

3.3 条件判断

test-component.component.ts

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

@Component({
  selector: 'app-test-component',
  templateUrl: './test-component.component.html',
  styleUrls: ['./test-component.component.scss']
})
export class TestComponentComponent implements OnInit {

  name:String = 'Alex'
  h3Dom:Boolean = true
  isShowMyDiv:Boolean = true
  constructor() { }

  ngOnInit(): void {
  }

}

test-component.component.html


条件判断
条件判断
if
else

3.4 循环语句

解析器会把 let color, let i 和 let odd翻译成命名变量 color, i 和 odd
微语法解析器接受of,会将他的首字母大写(Of), 然后加上属性的指令名(ngFor)前缀, 最终生成的名字是 ngFor的输入属性(colors)
NgFor指令在列表上循环,每个循环中都会设置和重置它自己的上下文对象上的属性。这些属性包括 index 和odd以及一个特殊的属性名 implicit属性的值, 它是由NgFor用当前迭代中的colors初始化的
NgFor
test-component.component.ts

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

@Component({
  selector: 'app-test-component',
  templateUrl: './test-component.component.html',
  styleUrls: ['./test-component.component.scss']
})
export class TestComponentComponent implements OnInit {

  name:String = 'Alex'
  h3Dom:Boolean = true
  isShowMyDiv:Boolean = true


  colors:Array = ['red', 'blue', 'yellow', 'green']
  type:Boolean = true

  constructor() { }

  ngOnInit(): void {
  }

}

test-component.component.html



{{ odd }} {{ i }} {{ color }}
{{ odd }} {{ i }} {{ color }}

type === true

type === false

默认显示

3.5 事件绑定

** 事件绑定**
事件绑定语法由左侧括号内的目标时间名和右侧引号内的模板语句组成。目标事件名是 click,模板语法是 onSave()
** 模板变量**
模板变量可以实现在当前模板的另一个部分使用这个数据。使用模板变量
test-component.component.ts

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

@Component({
  selector: 'app-test-component',
  templateUrl: './test-component.component.html',
  styleUrls: ['./test-component.component.scss']
})
export class TestComponentComponent implements OnInit {

  onSave(event:Event) {
    console.log('点击了按钮', event)
  }

  inputChange(event:Event) {
    console.log('触发了Input', event)
  }

  getUserName(v:string) {
    console.log(v)
  }

  constructor() { }

  ngOnInit(): void {
  }

}

test-component.component.html

test-component works!

3.6 数据双向绑定ngModel

双向绑定是应用中的组件共享数据的一种方式,使用双向绑定来侦听事件并在父组件和子组件之间同步更新
ngModel指令只对表单元素有效,所以在使用之前需要导入 FormsModule 模块

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { TestComponentComponent } from './test-component/test-component.component';

@NgModule({
  declarations: [
    AppComponent,
    TestComponentComponent
  ],
  // 申明组件需要用到的类
  imports: [
    BrowserModule,
    AppRoutingModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

test-component.component.ts

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

@Component({
  selector: 'app-test-component',
  templateUrl: './test-component.component.html',
  styleUrls: ['./test-component.component.scss']
})
export class TestComponentComponent implements OnInit {

  username:String = ''

  constructor() { }

  ngOnInit(): void {
  }
}

test-component.component.html

test-component works!

Input:

You are Typing: {{ username }}

3.7 表单控件

使用表单控件有三个步骤

  1. 在应用中注册响应式表单模块,该模块声明了一些你要用在响应式表单中的指令
  2. 生成一个新的 FormControl实例,并把它保存在组件中
  3. 在模板中注册这个 FormControl
    从 @angular/forms包中导入 ReactiveFormsModule, 并把它添加到你的 NgModule 的 imports 数组中
import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms'

@Component({
 selector: 'app-test-component',
 templateUrl: './test-component.component.html',
 styleUrls: ['./test-component.component.scss']
})
export class TestComponentComponent implements OnInit {

 myForm:FormControl = new FormControl('')

 handleChangeName () {
   // 必须通过 setValue 去修改值
   this.myForm.setValue('黄小明')
 }

 constructor() { }

 ngOnInit(): void {
 }
}

test-component works!

value: {{ myForm.value }}

3.8 表单控件分组

表单中通常包含几个相互管理的控件。响应式表单提供了两种把多个相关控件分组到同一个输入表单中的方法
如果要将表单组添加到此组件中,请执行以下步骤

  1. 创建一个 FormGroup 实例
  2. 把这个 FormGroup 模型关联到视图
  3. 保存表单数据
    创建一个 FormGroup实例
    在组件类中创建一个名为 loginForm 的属性, 并设置为 FormGroup 的一个新实例。要初始化这个 FormGroup,需要为构造函数提供一个由控件组成的对象,对象中的每个名字都要和表单控件的名字一一对应
    test-component.component.ts
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms'

@Component({
  selector: 'app-test-component',
  templateUrl: './test-component.component.html',
  styleUrls: ['./test-component.component.scss']
})
export class TestComponentComponent implements OnInit {

  loginForm = new FormGroup({
    username: new FormControl(''),
    password: new FormControl('')
  })

  handleShowData() {
      console.log(this.loginForm.value)
  }

  constructor() { }

  ngOnInit(): void {
  }
}

test-component.component.html

test-component works!

3.9 表单验证

表单元素添加 required 关键字表示必填,通过绑定ngModel的引用可以拿到当前组件的信息,通过引用获取验证的信息
test-component.component.ts

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

@Component({
  selector: 'app-test-component',
  templateUrl: './test-component.component.html',
  styleUrls: ['./test-component.component.scss']
})
export class TestComponentComponent implements OnInit {

  formData = {
    name: '',
    password: ''
  }

  handleSubmit(obj:Object) {
    console.log(obj)
  }

  constructor() { }

  ngOnInit(): void {
  }
}

test-component.component.html

test-component works!

name:
表单验证结果: {{ nameInp.valid }}
password:
{{ pasInp.valid }}

可以通过 ngModel 跟踪修改状态与有效性验证,它使用了三个CSS类来更新控件,用于反应当前状态

状态 为 true 时的类 为 false 时的类
控件已经被访问过 ng-touch ng-untouched ng-pristine
控件值时有效的 ng-valid ng-invalid

3.10 自定义表单验证

首先要引入几个类
test-component.component.ts

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms'

@Component({
  selector: 'app-test-component',
  templateUrl: './test-component.component.html',
  styleUrls: ['./test-component.component.scss']
})
export class TestComponentComponent implements OnInit {
  // 构造函数里注入 FormBuilder
  constructor(private formBuilder:FormBuilder) { }

  userName:String = ''
  password:String = ''
  phone:String = ''

  // 表单验证规则
  valiDateForm:FormGroup = this.formBuilder.group({
    userName: ['', [
      Validators.required, Validators.maxLength(18), Validators.minLength(6)
    ]],
    password: ['', [this.passwordVal]],
    // 同时使用内置验证,也使用自定义验证
    phone: ['', [Validators.required, this.phoneVal]]
  })

  // 自定义验证
  phoneVal(phone:FormControl):Object {
    const value = phone.value || ''
    if(!value) return { msg: '请输入手机号' }
    const valid = /[0-9]{11}/.test(value)
    return valid ? {} : { msg: '联系电话必须是11为数字' }
  }

  // 自定义验证
  passwordVal(password:FormControl):Object {
    const value = password.value || ''
    const valid = value > 0
    return valid ? {} : {msg: '密码至少把啦啦啦。。。'}
  }

  handleSubmit() {
    console.log(this.valiDateForm.get('userName'))
  }

  ngOnInit(): void {

  }
}

test-component.component.html

test-component works!

username:

{{ valiDateForm.get('userName')?.errors?'验证失败':''}}

password: phone:

3.11 管道

管道的作用是传输,不同的管道具有不同的作用。(其实就是数据处理)(其实就是Vue2中的Filter)

管道 功能
DatePipe 日期管道,格式化日期
JsonPipe 将输入数据对象经过JSON.stringfy()方法转换后输出的字符串
UpperCasePipe 将文本所有小写字母转换成大写字母
LowerCasePipe 将文本所有大写字母转换成小写字母
DecimalPipe 将数值按照特定的格式显示文本
CurrentcyPipe 将数值进行货币格式化处理
SlicePipe 将数组或者字符串裁剪成新字符串
PercentPipe 将数值转百分比格式

pipe用法
{{ 输入数据 | 管道:管道参数 }} (其中'|'是管道操作符)
链式管道 {{ 输入数据 | date | uppercase }}
管道流通方向自左向右,逐层执行
创建自定义管道:使用脚手架命令:ng g p test

3.12 生命周期

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

在开发中比较常用的生命周期是
ngOnChanges()
ngOnInit()
ngOnDestroy()

3.13 组件交互

@Input
父组件通过 @Input 给子组件绑定属性设置输入类数据
父传子类似Vue中的,父组件用冒号给子组件传值
父组件 home.component.html

 

子组件 title.component.ts

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

@Component({
  selector: 'app-title',
  templateUrl: './title.component.html',
  styleUrls: ['./title.component.scss']
})
export class TitleComponent implements OnInit {

  constructor() { }

  @Input()
  title?:String

  // 一般开发中常用的生命周期
  ngOnChanges(): void {
  }

  ngOnInit(): void {
  }

  ngOnDestroy(): void {
  }
}

@Output
父组件给子组件传递一个事件,子组件通过@Output发射触发事件
子传父,类似Vue中,this.$emit()
父组件 home.component.html

home works!

{{ list }}

父组件 home.component.ts

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

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {

  constructor() { }

  list:Array = ['1', '2']

  addListFun(str:String) {
    this.list?.push(str)
  }

  ngOnInit(): void {
  }

}

子组件 title.component.ts

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

@Component({
  selector: 'app-title',
  templateUrl: './title.component.html',
  styleUrls: ['./title.component.scss']
})
export class TitleComponent implements OnInit {

  constructor() { }

  inputValue:String = '111'

  @Output() addList = new EventEmitter()

  pushList() {
    this.addList.emit(this.inputValue)
  }

  ngOnInit(): void {
    this.pushList()
  }
}

@ViewChild()
通过 @viewChild 获取子组件实例, 获取子组件数据
父组件 home.component.html

home works!

{{ list }}

父组件 home.component.ts

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

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {

  constructor() { }

  list:Array = ['1', '2']

  addListFun(str:String) {
    this.list?.push(str)
  }

  @ViewChild('myAppTitle') child:any

  ngOnInit(): void {
    setTimeout(() => {
      // 此处相当于 Vue 中通过 refs 拿到了子组件的实例
      console.log(this.child)
    }, 1000);
  }
}

4. 服务与依赖注入

4.1 服务

angular中, 把从组件抽离出来的代码叫服务,服务的本质是方法(函数)
官方认为组件不应该直接获取或保存数据,它们应该聚焦于展示数据,而把数据访问的职责委托给某个服务。而服务就起着数据访问,逻辑处理的功能。把组件和服务区分开,以提高模块性和复用性。通过把组件中的视图有关的功能与其他类型的处理分离开,可以让组件类更精简、高效
可以使用如下命令创建一个服务,通过 @Injectable() 装饰器标识服务

ng g s xxx

下面演示创建一个 list 服务
list.service.ts

// 导入 Injectable 装饰器
import { Injectable } from '@angular/core';

// 是哦那个 Injectable 装饰器声明服务
@Injectable({
  // 作用域设定
  // root表示默认注入,注入到 app.module.ts内
  // 也可以写 null,表示不设定区域
  // 还可以是某个模块的名字(一般是懒加载模式)
  providedIn: 'root'
})
export class ListService {

  constructor() { }
}

4.2 依赖注入

创建一个服务

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

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

  constructor() { }

  // 注意该server是数据源,全局唯一
  myList:Array = ['Angular', 'React', 'Vue']

  getList() {
    // 此处可以进行异步的数据请求
    return this.myList
  }

  addList(str:String) {
    this.myList.push(str)
  }
}

在 app.module.ts 中引入服务

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { TestComponentComponent } from './test-component/test-component.component';
import { MyPipePipe } from './my-pipe.pipe';
import { HomeComponent } from './home/home.component';
import { TitleComponent } from './title/title.component';
// 引入服务
import { ListService } from './servers/list.service';

@NgModule({
  declarations: [
    AppComponent,
    TestComponentComponent,
    MyPipePipe,
    HomeComponent,
    TitleComponent
  ],
  // 申明组件需要用到的类
  imports: [
    BrowserModule,
    AppRoutingModule,
    ReactiveFormsModule,
    FormsModule
  ],
  // 注册服务
  providers: [ListService],
  bootstrap: [AppComponent]
})
export class AppModule { }

在组件中使用服务
home.component.ts

// 引入服务
import { ListService } from './../servers/list.service';
import { Component, OnInit} from '@angular/core';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss']
})

export class HomeComponent implements OnInit {

  constructor(private listService:ListService) { }

  list:Array | undefined

  ngOnInit(): void {
    setTimeout(() => {
      this.list = this.listService.getList()
      console.log(this.list)
    }, 1000);
 }
}

你可能感兴趣的:(Angular 入坑 01)