angular学习笔记 - 知识点

介绍

angularJs是第一版,angular是第二版以后的统称。
angular不兼容angularJs。
HttpClient(基于RxJS响应式编程)
相比于其他框架,更适用于复杂应用。性能高,体积小。


13406581-3123b898d3efb7f2.png

初始化

官方文档,快速上手
npm install -g @angular/cli安装脚手架
ng new my-app创建项目
cd my-app切换到项目
ng serve --open启动项目

文件目录结构

13406581-c159d7a7627e6e8a.png

13406581-25d0af033277ebde.png
13406581-26fa4b18690dd13e.png

tslint配置

tslint.json

组件体验

13406581-97b275f019fab29c.png

模块

根模块
作用:启动应用


13406581-58ce3dc580851a52.png

模块:独立、封闭的,模块间的引用通过导入和导出来完成
模块包含:组件、服务、指令,这些要配置后才生效

@NgModule

根组件装饰器
告诉angular将这个类当作模块处理
@NgModule{{元数据对象}}

13406581-97a7746c2c971cea.png

@Component

组件装饰器


13406581-1ca1e9c14ff01cfe.png

数据绑定

插值表达式

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

@Component({
  selector: 'app-root',
  // templateUrl: './app.component.html',
  template: '<div title="{{company}}">{{title}}--{{company}}--{{1+2}}-- {{fn()}}</div>',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = '塞纳河后台管理'
  company = 'snh'
  fn () {
    return '方法返回内容'
  }
}

属性绑定
插值表达式中的属性绑定方式,在执行时最终会转换成这种属性绑定

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

@Component({
  selector: 'app-root',
  // templateUrl: './app.component.html',
  template: `
    <a [href]="url">链接</a>
    <input type="checkbox" [checked]="isChecked" />
  `,
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  url = 'http://www.baidu.com'
  isChecked = true
}

事件绑定

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

@Component({
  selector: 'app-root',
  // templateUrl: './app.component.html',
  template: `
    <button (click)="handleClick()" (mouseenter)="handleMouseEnter()">请点击</button>
    <a [href]="url" (click)="handleClick($event)">链接</a>
  `,
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  url = 'http://www.baidu.com'
  handleClick (event) {
    // 阻止浏览器的默认行为
    event.preventDefault()
    console.log('handleClick', event)
  }
  handleMouseEnter () {
    console.log('handleMouseEnter')
  }
}

双向数据绑定
属性绑定和事件绑定的结合

13406581-1fd6fb6231426c06.png

app.module.ts
13406581-6c86346e67010b3d.png

app.component.ts

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

@Component({
  selector: 'app-root',
  // templateUrl: './app.component.html',
  template: `
    <input type="text" [(ngModel)]="msg" />
    <p>{{msg}}</p>
  `,
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  msg = '我是默认值'
}

语言服务

13406581-973d929c8e827e75.png

指令

组件:拥有模板的指令
属性型指令:改变元素外观和行为的指令
结构型指令:添加和移除DOM元素,改变DOM布局的指令

[ngClass]

13406581-7d3ec2e8f3b245b8.png

[ngStyle]

13406581-d9ff555005a5628e.png

*ngIf

13406581-4b09405f34bbecec.png

*ngFor

基本使用


13406581-5f783bb194513c66.png

隔行变色


13406581-a314d0590ef81fd8.png

使用trackBy解决性能问题
普通数据在渲染时没有性能问题,对象数组在渲染时有性能问题。这时需要使用trackBy来解决。

13406581-e3fc92dad566cec0.png

todos案例

组件通讯

ng generate component child 生成子组件,并自动完成了相关的配置

父组件传值给子组件


13406581-d5a947d96e79a00e.png

13406581-0f0b52e82d81d17c.png

13406581-2ca0cd0aff84c196.png

13406581-30bc2b17562de1a2.png

子组件传值给父组件


13406581-5848f2bcbef777f4.png

子组件创建事件,触发事件,传递参数


13406581-7a3dd0a0b9b3ee8d.png

13406581-4782985c70896f6e.png

父组件绑定事件,接收参数
13406581-9a8e348a5e1492d0.png

13406581-65b3c6a16ffb6bbf.png

todos案例分离组件

ng generate module todos 创建todos模块
目标结构如下

13406581-911634f858588b30.png

指定位置,创建三个子组件
ng generate component todos/todo
ng generate component todos/todo-header
ng generate component todos/todo-list

13406581-77edc6682d383738.png

在根模块中使用todos模块

13406581-e20b531e0e44e708.png

13406581-e4fd76b201a28d01.png

然后将原来写在根模块中的内容,搬到 todos模块中即可。
注意: FormsModule也要搬过去,否则 todos模块中不能使用。

抽离todo-header组件
要用到组件之间的通讯,子传父 - 任务添加

抽离todo-list组件
任务展示、删除、状态切换
要用到组件之间的通讯,父传子 - 拿到todos数据
要用到组件之间的通讯,子传父 - 删除和修改状态时,要修改父组件中的数据,为了保持数据源的单一性

todo父组件 - 提供代办事项的数据

使用TypeScript

angular官方推荐使用ts
增强了项目的可维护性
有利于协作开发

ts语法

  1. 类型注解
let id: number
  1. 接口
// 创建接口
interface Todo {
  id: number,
  name: string,
  done: boolean
}
export class TodoComponent implements OnInit {
  constructor() { }
  // 任务列表
  todos: Todo[] = [
    { id: 1, name: '玩游戏啊', done: true },
    { id: 2, name: '点外卖呀', done: false },
    { id: 3, name: '看bilibili', done: false }
  ]
}
  1. 泛型
  @Output()
  add = new EventEmitter<string>()
  1. 类成员修饰符
  private todoName: string

接口的另一种使用方式


13406581-7798255f08e68e6b.png
13406581-7b3d350a18c46037.png
13406581-5a8a0626c2fdeecc.png

服务

组件:
提供数据绑定的属性和方法

服务:
处理业务逻辑,供组件使用,如从服务器获取数据、验证用户输入等

组件是服务的消费者

服务说明:

  1. @injectable()装饰器来表示一个服务
  2. 服务要注册提供商才能使用
  3. angular通过依赖注入(DI)来位组件提供服务
  4. DI提供要使用的服务即可。不需要手动创建服务实例
  5. 推荐在constructor中提供组件中用到的服务

服务的创建和基本使用

ng generate service todos/todos 生成服务

todos.service.ts中,如下是服务的提供商,必须有@Injectable

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

@Injectable({
  providedIn: 'root'
})
export class TodosService {
  constructor() { }
  // 提供方法
  todoTest () {
    console.log('test-TodoService')
  }
}

使用方式,todo.component.ts,这样,当点击添加的时候,即可调用服务

// 导入服务
import { TodosService } from '../todos.service'

export class TodoComponent implements OnInit, OnChanges {

  constructor(private todoService: TodosService) { }
  addTodo(todoName: string) {
    // 演示服务的调用
    this.todoService.todoTest()
  }

  ngOnInit() {
  }
  ngOnChanges() {
  }
}

注册提供商的三种方式

  1. 根级提供商,在所有组件中都可以调用
@Injectable({
  providedIn: 'root'
})
  1. 模块内可用的提供商

todos.service.ts

@Injectable()

todos.module.ts

// 导入服务
import { TodosService } from './todos.service'
@NgModule({
  providers: [
    TodosService
  ]
})
  1. 组件内可用的提供商(其子组件也可以使用,兄弟组件不可使用)

todos.service.ts

@Injectable()

todo.component.ts

// 导入服务
import { TodosService } from './../todos.service';

@Component({
  providers: [TodosService]
})

export class TodoComponent implements OnInit, OnChanges {
  constructor(private todoService: TodosService) { }
  addTodo(todoName: string) {
    // 演示服务的调用
    this.todoService.todoTest()
  }
  ngOnInit() {
  }
  ngOnChanges() {
  }
}

todos案例使用服务修改

把组件中的业务逻辑抽离到服务中

HttpClient

angular是内置的客户端,不是使用第三方的。

  • 作用: 发送http请求
  • 封装了XMLHttpRequest
  • 使用基于可观察(Observable)对象的api
  • 提供了请求和响应拦截器
  • 流式错误处理机制

HttpClient的基本使用

app.module.ts中导入

// 导入HttpClient模块
import {HttpClientModule} from '@angular/common/http'
@NgModule({
  imports: [
    HttpClientModule
  ],
})

app.component.html触发事件

<div>
  <button (click)="getData()">获取数据</button>
  <h3>通过HttpClient获取到的数据是:{{name}}</h3>
</div>

app.component.ts 接口是用的assets中的json文件模拟的

// 导入HttpClient
import { HttpClient } from '@angular/common/http'
export class AppComponent {
  constructor(private http: HttpClient) { }
  name: string
  getData () {
    this.http.get('../assets/todos.json').subscribe((res: any) => {
      console.log(res)
      this.name = res.name
    })
  }
}

添加类型检查后的app.component.ts ,这种写法比较严谨

interface Todo {
  name: string,
  description: string
}
export class AppComponent {
  constructor(private http: HttpClient) { }
  name: string
  getData () {
    this.http.get<Todo>('../assets/todos.json').subscribe((res: Todo) => {
      console.log(res)
      this.name = res.name
    })
  }
}

获取完整的响应

使用{ observe: 'response' }即可获取完整的响应
类型检查时要使用HttpResponse

// 导入HttpClient
import { HttpClient, HttpResponse } from '@angular/common/http'

export class AppComponent {
  constructor(private http: HttpClient) { }
  name: string
  getData() {
    this.http.get<Todo>('../assets/todos.json', { observe: 'response' })
    .subscribe((res: HttpResponse<Todo>) => {
      console.log(res)
      console.log(res.headers.get('content-type'), res.body)
      this.name = res.body.name
    })
  }
}

错误处理

  getData() {
    this.http.get<Todo>('../assets/todos.json1', { observe: 'response' })
      .subscribe((res: HttpResponse<Todo>) => {
        console.log(res)
        console.log(res.headers.get('content-type'), res.body)
        this.name = res.body.name
      },
      err => {
        console.log(err)
      }
    )
  }

json-server

json-server官方文档
npm install -g json-server安装包
新建db.json文件,在里面写好json数据

{
  "todos": [
    { "id": 1, "name": "玩游戏啊", "done": true },
    { "id": 2, "name": "点外卖啊", "done": false },
    { "id": 3, "name": "看bilibii", "done": false }
  ]
}

json-server --watch db.json

13406581-fe7d1d37edcd1d8a.png

这就是接口的地址

其他请求

接口地址使用的是json-server生成的地址
app.component.html

<div>
  <button (click)="getData()">get获取</button>
  <button (click)="addData()">post增加</button>
  <button (click)="delData()">del删除</button>
  <button (click)="updateData()">patch修改</button>
</div>

app.component.ts

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

// 导入HttpClient
import { HttpClient } from '@angular/common/http'

interface Todo {
  name: string,
  description: string
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  constructor(private http: HttpClient) { }
  name: string
  url = 'http://localhost:3000/todos'
  // get
  getData() {
    this.http.get(this.url).subscribe(res => {
      console.log(res)
    })
  }
  // post
  addData() {
    this.http.post(this.url,{
      name: '测试测试',
      done: true
    }).subscribe(res => {
      console.log('post success:', res)
    })
  }
  // del
  delData() {
    this.http.delete(`${this.url}/2`).subscribe(res => {
      console.log('delete success:', res);
    })
  }
  // patch
  updateData() {
    this.http.patch(`${this.url}/3`,{
      name: '黑呀~我是修改后的数据呀~'
    }).subscribe(res => {
      console.log('patch success:', res)
    })
  }
}

todos案例 HttpClient

路由

实现SPA(单页应用程序)的基础设施
URL和组件的对应规则
angular使用HTML5风格(history.pushState)的导航
支持:重定向、路由高亮、通配符路由、路由参数、子路由、路由模块、路由守卫、异步路由

路由的基本使用

路由的基本使用

  1. index.html
  <base href="/">
  1. 导入RouterModule

  2. 配置路由规则appRoutes

  3. 根模块中配置

    13406581-862e65f6332cbe2c.png

    forRoot说明:
    路由服务应该是单例的,但在路由懒加载等场景下会造成服务多次注册,所以使用forRoot()方法导入模块,保证项目中只有一个Router服务

  4. 添加路由出口<router-outlet></router-outlet>

配置更多路由

新建组件
配置多个路由规则即可

默认路由

  { path: '', redirectTo: '/home', pathMatch: 'full' }

通配符路由

用来匹配没有的路由规则,跳转到404页面

  // 通配符路由,要放在最后面,不然所有的路由都不能正常匹配
  { path: '**', component: PageNotFoundComponent }`

编程式导航

import { Component, OnInit } from '@angular/core';
// 导入路由提供的服务
import { Router } from '@angular/router'
@Component({
  selector: 'app-page-not-found',
  templateUrl: './page-not-found.component.html',
  styleUrls: ['./page-not-found.component.css']
})
export class PageNotFoundComponent implements OnInit {
  // 注入服务
  constructor(private router: Router) { }
  time = 5

  ngOnInit() {
    const timerId = setInterval( () => {
      this.time--
      if (this.time === 0) {
        clearInterval(timerId)
        // 编程式导航
        this.router.navigate(['/home'])
      }
    }, 1000)
  }
}

路由的参数

ng g c car新建模块
配路由规则,:id表示路由参数

  { 
    path: 'car/:id', 
    component: CarComponent 
  },

app.component.html中设置跳转

  <ul>
      <li>
        <a routerLink="car/1">野马</a>
      </li>
      <li>
          <a routerLink="car/2">法拉利</a>
      </li>
      <li>
          <a routerLink="car/3">兰博基尼</a>
      </li>
      <li>
          <a routerLink="car/4">奇瑞QQ</a>
      </li>
    </ul>

car.component.ts中获取路由参数

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

// 导入路由服务
import { ActivatedRoute } from '@angular/router'

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

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
    this.route.paramMap.subscribe(param => {
      // param中可拿到当前路由跳转的参数
      const id = param.get('id')
      console.log(param, id);
    })
  }
}

子路由

13406581-95e32802efbf4197.png

新建组件
配置路由规则

  {
    path: 'home',
    component: HomeComponent,
    children: [{
      path: 'home/child',
      component: HomeChildComponent
    }]
  },

给子路由出口, home.component.html

<p>
  home works!
  <a routerLink="home/child">子路由</a>
  <router-outlet></router-outlet>
</p>

路由激活高亮

  <!-- 在css中设置actived的样式,即为高亮样式 -->
  <!-- [routerLinkActiveOptions]是精确匹配才高亮 -->
  <a routerLink='/home' routerLinkActive="actived" [routerLinkActiveOptions]="{exact: true}">首页</a>
  <a routerLink='/about' routerLinkActive="actived">关于</a>

表单

  • 响应式表单
    很强大,推荐
    模型驱动,数据驱动视图的思想
    同步的数据访问,保证数据和视图是一致的、可预测的
    增强了可测试性,让测试变得简单
    内置表单验证器

  • 模板驱动表单
    数据双向绑定实现
    vue angular.js

响应式表单

响应式表单

  1. 导入响应式表单模块
import { ReactiveFormsModule } from '@angular/forms';
  1. 生成并导入一个新的表单控件
  2. 在模板中注册该控件
  3. 更新用户名和获取用户名方法
  // 获取用户名
  getUserName () {
    console.log(this.username.value)
  }
  // 更新用户名
  setUserName () {
    this.username.setValue('fdd')
  }

表单验证

  • 内置表单验证器
    username.errors中拿值,判断是否通过校验
    username.dirty中拿值,判断是否输入过
<p *ngIf="username.dirty && username.errors?.required">用户名为必填项</p>

hasError()中拿到值,判断是否通过校验

ngOnInit () {
    console.log(this.username)
    console.log(this.username.hasError('required'))
  }

多个条件的校验

  password = new FormControl('123', [
    Validators.required,
    Validators.minLength(4)
  ])
  <p *ngIf="password.dirty && password.errors?.minlength">密码格式不正确</p>
  • 自定义表单验证器
  // 自定义表单验证
  nickname = new FormControl('', [this.nicknameValidate])
  nicknameValidate(control) {

    console.log(control);
    if (/^[a-z]{3,6}$/.test(control.value)) {
      return null
    }
    return { error: true }
  }
  <p *ngIf="nickname.dirty && nickname.hasError('error')">昵称格式不正确</p>

FormGroup

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

// 导入表单控件
import { FormControl, Validators, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-form-group',
  templateUrl: './form-group.component.html',
  styleUrls: ['./form-group.component.css']
})
export class FormGroupComponent implements OnInit {
  constructor() { }

  loginForm = new FormGroup({
    username: new FormControl('', Validators.required),
    password: new FormControl('123', [
      Validators.required,
      Validators.minLength(4)
    ])
  });
  onSubmit() {
    if (this.loginForm.valid) {
      console.log('submit');
    } else {
      console.log('err');
    }
  }
  ngOnInit() {
    // console.log(this.loginForm)
  }
}
<div>
  <form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
    <label>
      <span>用户名:</span>
      <input type="text" formControlName="username" />
      <span>{{this.loginForm.value.username}}</span>
    </label>
    <label>
      <span>密码:</span>
      <input type="text" formControlName="password" />
      <span>{{this.loginForm.value.password}}</span>
    </label>
    <button type="submit">提交</button>
  </form>
    <p *ngIf="this.loginForm.controls.username.dirty && this.loginForm.controls.username.errors?.required">用户名为必填项</p>
    <p *ngIf="this.loginForm.controls.password.dirty && this.loginForm.controls.password.errors?.minlength">密码格式不正确</p>
  </div>

FormBuilder

生成表单控件的便捷方法
导入、注入

  loginForm = this.fb.group({
    username: ['', Validators.required],
    password: ['123', [
      Validators.required,
      Validators.minLength(4)
    ]]
  });

转载于:https://www.jianshu.com/p/c360a8134c40

猜你喜欢

转载自blog.csdn.net/weixin_34080571/article/details/91212964
今日推荐