Angular 是一个开发平台。它能帮你更轻松的构建Web 应用。Angular 集声明式模板、依赖注入、端到端工具和一些最佳实践于一身,为你解决开发方面的各种挑战。
使用TS
使用TS的好处
- Angular的最佳推荐
- 增强了项目的可维护性
- 有利于多人协作的开发
- 降低了开发人员的沟通成本
在Angula中应用
类型的注解
—为函数/变量添加约束
todo.component.ts
....
// 父组件完成数据添加操作
addTodo(todoName) {
// 类型注解
let id: number
if (this.todos.length === 0) {
id = 1
} else {
id = this.todos[this.todos.length - 1].id + 1
}
.....
}
....
在let id
后面添加类型注解,如果下面的id=1
写为id=‘1’
就会得到一个错误不能将类型‘1’分配给数字‘Number’
,在开发期间就可以通过这种方式来添加约束。
如果这个变量是有值的,就不需要添加类型注解了。
接口
—对值所具有的结构进行类型检查
接口规定了结构,达到了访问统一。
方法一
todo.component.ts
...
// 创建接口
interface Todo{
id: number,
name: string,
done: boolean
}
...
export class TodoComponent implements OnInit {
....
// 记得是对象模式 Todo后面要加[]
todos: Todo[] = [
{id: 1, name: '吃饭', done: true},
{id: 2, name: '睡觉', done: false},
{id: 4, name: '1吃饭', done: true},
{id: 5, name: '2睡觉', done: false},
{id: 6, name: '3吃饭', done: true},
{id: 7, name: '4睡觉', done: false}
]
....
}
方法二
todo.component.ts
import { Component, OnInit, OnChanges } from '@angular/core';
...
export class TodoComponent implements OnInit,OnChanges {
.......
ngOnInit() {
}
ngOnChanges() {
}
}
OnInit
一个生命周期钩子,它会在 Angular 初始化完了该指令的所有数据绑定属性之后调用。 定义 ngOnInit()
方法可以处理所有附加的初始化任务。
OnChanges
当 Angular(重新)设置数据绑定输入属性时响应。首次调用一定会发生在 ngOnInit()
之前。
Angular 会找到并调用像
ngOnInit()
这样的钩子方法,有没有接口无所谓。
泛型
—泛型类EventEmitter约定参数类型
todo-header.component.ts
......
// 使用了泛型 <String>
add = new EventEmitter<String>()
addTodo() {
if (this.todoName.trim() === '') {
return
}
// 参数 this.todoName 就只能使用string类型的
this.add.emit(this.todoName)
this.todoName = ''
}
......
}
类成员修饰符
—public公共(默认),private私有
代码
todo.component.ts
import { Component, OnInit } from '@angular/core';
// 创建接口
interface Todo{
id: number,
name: string,
done: boolean
}
@Component({
selector: 'app-todo',
templateUrl: './todo.component.html',
styleUrls: ['./todo.component.css']
})
export class TodoComponent implements OnInit {
constructor() { }
todos: Todo[] = [
{id: 1, name: '吃饭', done: true},
{id: 2, name: '睡觉', done: false},
{id: 4, name: '1吃饭', done: true},
{id: 5, name: '2睡觉', done: false},
{id: 6, name: '3吃饭', done: true},
{id: 7, name: '4睡觉', done: false}
]
// private todo: any
addTodo(todoName: string) {
// id
// 类型注解
let id: number
if (this.todos.length === 0) {
return 1
} else {
id = this.todos[this.todos.length - 1 ].id - 1
}
// 放入todos数组
this.todos.push({
id,
name: todoName,
done: false,
})
}
delTodo(id: number) {
this.todos = this.todos.filter(todo => todo.id !== id)
}
changeTodo(id: number) {
const curTodo = this.todos.find(todo => todo.id === id)
curTodo.done = !curTodo.done
}
ngOnInit() {
}
}
todo-header.component.ts
import { Component, OnInit, Output, EventEmitter, OnChanges } from '@angular/core';
@Component({
selector: 'app-todo-header',
templateUrl: './todo-header.component.html',
styleUrls: ['./todo-header.component.css']
})
export class TodoHeaderComponent implements OnInit {
constructor() { }
// 任务名称
todoName = ''
// 类型注解--有bug,当空白添加的时候没有默认值会进行报错
// todoName: string
@Output()
add = new EventEmitter<String>()
addTodo() {
if (this.todoName.trim() === '') {
return
}
this.add.emit(this.todoName)
this.todoName = ''
}
ngOnInit() {
}
}
todo-list.component.ts
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
// 创建接口
interface Todo{
id: number,
name: string,
done: boolean
}
@Component({
selector: 'app-todo-list',
templateUrl: './todo-list.component.html',
styleUrls: ['./todo-list.component.css']
})
export class TodoListComponent implements OnInit {
constructor() { }
@Input()
todos: Todo[]
@Output()
del = new EventEmitter<number>()
@Output()
change = new EventEmitter<number>()
trackByTodo(index: number,todo: Todo) {
return todo.id
}
// 删除
delTodo(e, id: number) {
e.preventDefault()
this.del.emit(id)
}
// 任务完成状态切换
changeTodo(id: number) {
this.change.emit(id)
}
ngOnInit() {
}
}
服务
作用
- 组件应该只提供用于数据绑定的属性和方法
- 组件不应该定义任何诸如从服务器获取数据、验证用户输入等操作
- 应该把各种处理任务定义到可注入的服务中
- 服务的作用:处理业务逻辑,供组件使用
- 服务和组件的关系:组件是服务的消费者
概念
- 通过
@Injectable()
装饰器 来表示一个服务 - 服务需要注册提供商才可以使用
- Angular通过依赖注入(DI)来为组件提供服务
- DI使得在使用服务时,只提供要使用的服务即可。不需要手动创建服务实例
- 推荐在
constructor
中提供组件中用到的服务
使用
创建一个服务,该命令在todos的文件夹下创建了一个todos的服务。
$ ng g s todos/todos
todos.service.ts
// 导入Injectable 告诉Angular这个文件是一个服务
import { Injectable } from '@angular/core';
@Injectable({
// 是一个服务中的提供商
providedIn: 'root'
})
export class TodosService {
constructor() { }
// 服务的方法
todotest(){
console.log('TodoTest')
}
}
todo.component.ts
import { Component, OnInit } from '@angular/core';
// 导入服务
import { TodosService } from '../todos.service';
...
export class TodoComponent implements OnInit {
// 告诉组件: 组件中需要使用这个服务
constructor(private TodosService: TodosService) { }
addTodo(todoName: string) {
// 调用服务
this.TodosService.todotest()
.....
}
}
在todo.component.ts
中通过TodosService
来进行调用服务的方法TodoTest
。
如果没有服务注册提供商会进行以下报错
Error: StaticInjectorError(AppModule)[TodoComponent -> TodosService]: StaticInjectorError(Platform: core)[TodoComponent -> TodosService]: NullInjectorError: No provider for TodosService! .....
注册提供商
-
通过
@Injectable
的providedIn: 'root'
注册为根级提供商app.component.ts
import { Component } from '@angular/core'; // 导入服务 import {TodosService} from './todos/todos.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { // 告诉跟组件: 组件中需要使用这个服务 constructor(private TodosService: TodosService) { } }
-
通过
@NgModule
的providers: []
注册为模块内可用的提供商todos.module.ts
// 导入服务 import { TodosService} from './todos.service'; @NgModule({ ... // 在模块中注册可用的提供商 providers: TodosService }) export class TodosModule { }
这样仅仅是在模块中使用,具有惰性。根注入器并不知道这些模块。
-
通过
@Component
的providers: []
注册为组件的提供商todos.component.ts
import { Component, OnInit } from '@angular/core'; // 导入服务 import { TodosService } from '../todos.service'; @Component({ selector: 'app-todo', templateUrl: './todo.component.html', styleUrls: ['./todo.component.css'], // 在组件中注册可用的提供商 providers: TodosService }) export class TodoComponent implements OnInit { // 告诉组件: 组件中需要使用这个服务 constructor(private TodosService: TodosService) { } addTodo(todoName: string) { // 调用服务 this.TodosService.todotest() ..... } }
在父子组件中,是可以使用的,但是在兄弟组件中就必须注册才可以使用。
TODOS使用服务
使用服务修改todos
- 把组件中的业务逻辑抽离到服务中
- 在组件中调用服务中对应的方法
数据
-
在组件
todos.component.ts
中导入服务 -
在
constructor
中告诉组件要使用服务不要在
constructor
中放入业务逻辑,只能去提供一些属性/服务 -
将数据抽离到服务中
-
ngOnInit
可以放入一些初始化的代码 -
记得在服务中添加数据的接口类型约束
服务中提供的数据类型和组件中使用的是一样的
抽离为单独的文件,进行导入
todos/todo/todos.component.ts
import { Component, OnInit } from '@angular/core';
// 导入服务
import { TodosService } from '../todos.service';
// 接口
import { Todo } from "../todo";
@Component({
selector: 'app-todo',
templateUrl: './todo.component.html',
styleUrls: ['./todo.component.css']
})
export class TodoComponent implements OnInit {
// 告诉组件: 组件中需要使用这个服务
constructor(private TodosService: TodosService) { }
todos
.....
// ngOnInit可以放入一些初始化的代码
ngOnInit() {
this.todos = this.TodosService.getTodos()
}
}
todos/todos.service.ts
import { Injectable } from '@angular/core';
// 接口
import { Todo } from './todo';
@Injectable({
providedIn: 'root'
})
export class TodosService {
constructor() { }
// 提供数据
getTodos() {
const todos: Todo[] = [
{id: 1, name: '1', done: true},
{id: 2, name: '2', done: false}
]
return todos
}
}
todos/todo.ts
export interface Todo{
id: number,
name: string,
done: boolean
}
添加任务
- 从组件中复制添加任务到服务中
- 服务中没有
todos
的数据,声明todos
,在getTodos
中添加初始化数据 - 组件直接调用服务中的方法
todos/todo/todos.component.ts
....
export class TodoComponent implements OnInit {
// 告诉组件: 组件中需要使用这个服务
constructor(private TodosService: TodosService) { }
todos
addTodo(todoName: string) {
this.TodosService.addTodo(todoName)
}
....
ngOnInit() {
this.todos = this.TodosService.getTodos()
}
}
todos/todos.service.ts
import { Injectable } from '@angular/core';
// 接口
import { Todo } from './todo';
@Injectable({
providedIn: 'root'
})
export class TodosService {
constructor() { }
todos: Todo[]
// 提供数据
getTodos() {
const todos: Todo[] = [
{id: 1, name: '1', done: true},
{id: 2, name: '2', done: false}
]
// 添加一个初终数据
this.todos = todos
return todos
}
// 添加任务
addTodo(todoName: string) {
let id: number
if (this.todos.length === 0) {
return 1
} else {
id = this.todos[this.todos.length - 1 ].id - 1
}
this.todos.push({
id,
name: todoName,
done: false,
})
}
}
删除和切换任务
-
先进行切换任务的处理,在进行删除任务的处理
-
从组件中复制切换任务到服务中
todos/todo/todos.component.ts
.... export class TodoComponent implements OnInit { .. changeTodo(id: number) { this.TodosService.changeTodo(id) } ... }
todos/todos.service.ts
... export class TodosService { ... // 切换任务 changeTodo(id: number) { const curTodo = this.todos.find(todo => todo.id === id) curTodo.done = !curTodo.done } }
-
从组件中复制删除任务到服务中,会发现无法进行删除
在删除操作中,改变了
this.todos
的指向 -
更改删除任务的业务逻辑
todos/todo/todos.component.ts
.... export class TodoComponent implements OnInit { .. changeTodo(id: number) { this.TodosService.delTodo(id) } ... }
todos/todos.service.ts
... export class TodosService { ... // 删除任务 delTodo(id: number) { // this.todos = this.todos.filter(todo => todo.id !== id) const curIndex = this.todos.findIndex(todo => todo.id === id) this.todos.splice(curIndex,1) } }
完成
完
本文首次发布于 Azr的博客, 作者 @azrrrrr ,转载请保留原文链接.
原文链接: http://amor9.cn/2019/01/15/ng3/