如何使用Angular 12构建一个任务管理器
Angular是一个流行的Javascript框架。它可以用来构建不同的应用程序,从简单到复杂不等。
一个任务管理器可以更有效地编写、组织和重新安排任务。我们将建立的任务管理器将允许用户添加任务,将任务标记为完成,并删除任务。
在本教程中,我们将讨论构建一个Angular任务管理器示例应用程序的概念。
前提条件
要继续学习本教程。
- 你应该在你的本地机器上安装[Node.js]。
- 你应该对[Angular]和JavaScript相当熟悉。对[TypeScript]的了解是有用的,但不是先决条件。
- 你应该在你的本地开发机器上安装[Angular CLI]。
主要收获
在本教程结束时,你应该能够。
- 建立一个简单的任务管理器。
- 将项目保存到本地存储。
创建一个新的angular项目
在你的命令提示符上运行CLI命令ng new *project name*
,创建一个新的Angular项目。
ng new todoApp
上述Angular CLI命令为项目安装了所有必要的AngularNPM包和其他依赖项。
为了向浏览器提供你的应用程序,cd
进入项目根目录并运行以下命令。
ng serve
创建一个组件
现在我们已经建立了我们的项目,让我们继续并创建一个todo
组件来处理我们所有的逻辑。
运行下面的命令来创建一个todo
组件。
ng generate component todo
# OR
ng g c todo
接下来,让我们在todo.component.html
文件中添加模板代码。在本教程中,我们将使用Tailwind CSS进行样式设计。
在todo.component.html
文件中复制并粘贴以下代码。
<div class="h-100 w-full flex items-center justify-center bg-teal-lightest font-sans">
<div class="bg-white rounded shadow p-6 m-4 w-full lg:w-3/4 lg:max-w-lg">
<div class="mb-4">
<h1 class="text-grey-darkest">Todo List</h1>
<form class="flex mt-4">
<input class="shadow appearance-none border rounded w-full py-2 px-3 mr-4 text-grey-darker" placeholder="Add Todo">
<button class="flex-no-shrink p-2 border-2 rounded text-teal border-teal hover:text-white hover:bg-teal">Add</button>
</form>
</div>
<div>
<div class="flex mb-4 items-center">
<p class="w-full text-grey-darkest">task 1</p>
<button class="flex-no-shrink p-2 ml-4 mr-2 border-2 rounded hover:text-white text-green border-green hover:bg-green">Done</button>
<button class="flex-no-shrink p-2 ml-2 border-2 rounded text-red border-red hover:text-white hover:bg-red">Remove</button>
</div>
<div class="flex mb-4 items-center">
<p class="w-full text-green">task 2</p>
<button class="flex-no-shrink p-2 ml-4 mr-2 border-2 rounded hover:text-white text-green border-green hover:bg-green">Done</button>
<button class="flex-no-shrink p-2 ml-2 border-2 rounded text-red border-red hover:text-white hover:bg-red">Remove</button>
</div>
</div>
</div>
</div>
输出。
注意:按钮是静态的,所以我们接下来要让它们发挥作用。
添加任务到待办事项列表
通过点击Add
按钮或按下Enter
键,任务将被添加到待办事项列表中。
让我们看看引擎盖下会发生什么,因为Add
按钮和输入框都在一个表单元素中,我们使用AngularFormGroup属性来处理它们。
为了使用FormGroup属性,我们首先在app.module.ts
文件中导入它,如下图所示。
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
接下来,将模块添加到imports
数组中,如下图所示。
在todo.component.html
,在<form>
元素中添加[FormGroup]
属性,并将其设置为newTodoForm
。这将有助于收集和整理每个元素的值为一个对象。
接下来,继续添加一个formControlName
,并将其设置为todoItem
,同时绑定添加一个事件监听器(ngSubmit)
到表单元素。
该事件监听器将监听提交的事件并触发一个addTask()
方法。
<div class="h-100 w-full flex items-center justify-center bg-teal-lightest font-sans">
<div class="bg-white rounded shadow p-6 m-4 w-full lg:w-3/4 lg:max-w-lg">
<div class="mb-4">
<h1 class="text-grey-darkest">Todo List</h1>
<form [formGroup]="newTodoForm" class="flex mt-4" (ngSubmit)="addTask()">
<input class="shadow appearance-none border rounded w-full py-2 px-3 mr-4 text-grey-darker" placeholder="Add Todo" formControlName="todoItem">
<button type='submit' class="flex-no-shrink p-2 border-2 rounded text-teal border-teal hover:text-white hover:bg-teal">Add</button>
</form>
</div>
然后为了使我们的表单发挥作用,我们将在我们的todo.component.ts
文件中写一些TypeScript代码。
遵循这些步骤。
- 从
@angular/forms
包中导入[FormBuilder](https://angular.io/api/forms/FormBuilder)
服务。这个服务为创建控件提供了合适的方法。
//todo.component.ts
import { Component, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms';
- 在
TodoComponent
constructor()
中注入FormBuilder
服务。这个服务包含在[ReactiveFormsModule](https://angular.io/api/forms/ReactiveFormsModule)
模块中,这个模块已经被导入。
//todo.component.ts
export class TodoComponent implements OnInit {
constructor(
private formBuilder: FormBuilder
) {}
}
- 我们将使用
FormBuilder
类的group()
方法来收集用户的输入。这些方法将newTodoForm
属性设置为一个包含todoItem
字段的表单模型。
//todo.component.ts
export class TodoComponent implements OnInit {
newTodoForm = this.formBuilder.group({
todoItem: ''
})
constructor(
private formBuilder: FormBuilder
) {}
}
- 定义一个
addTask()
方法,将一个任务添加到一个空数组中。记住,addTask()
是与表单上的(ngSubmit)
事件相关的方法。
- 创建一个空数组
taskList
,以后将保存输入的任务。 - 在
addTask()
,收集输入元素的值,并将其存储在一个叫做value
的常量变量中。 - 将
value
推到taskList
数组中。id
被设置为taskList
数组的长度,而名称被设置为变量value
。 - 重置输入元素。
整个Todo组件类如下。
//todo.component.ts
import { Component, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms';
export class TodoComponent implements OnInit {
taskList: any[] = []
newTodoForm = this.formBuilder.group({
todoItem: ''
})
constructor(
private formBuilder: FormBuilder
) { }
addTask() {
const value = this.newTodoForm.value.todoItem
this.taskList.push({ id: this.taskList.length, name: value })
this.newTodoForm.reset();
}
}
我们需要用*ngFor
循环显示taskList
数组。这是Angular中的一个预定义指令。
它接受一个数组,在一个模板上迭代数据,用不同的数据复制模板。它与JavaScript中的forEach()
方法相同,也是在一个数组上进行迭代。
循环将在todo.component.html
文件中完成。循环之后,设置<p>
,动态地显示任务。
<div class="h-100 w-full flex items-center justify-center bg-teal-lightest font-sans">
<div class="bg-white rounded shadow p-6 m-4 w-full lg:w-3/4 lg:max-w-lg mt-4">
<div class="mb-4">
<h1 class="text-4xl font-black">Todo List</h1>
<form [formGroup]="newTodoForm" class="flex mt-4" (ngSubmit)="addTask()">
<input class="shadow appearance-none border rounded w-full py-2 px-3 mr-4 text-grey-darker"
placeholder="Add Todo" formControlName="todoItem">
<button type="submit"
class="flex-no-shrink p-2 border-2 rounded text-teal border-teal hover:text-white hover:bg-teal"
>Add
</button>
</form>
</div>
<div>
<div class="flex mb-4 items-center" *ngFor= "let value of taskList">
<p class="w-full text-grey-darkest">{{value.name}}</p>
<button
class="flex-no-shrink p-2 ml-4 mr-2 border-2 rounded text-green border-green hover:bg-green">Done</button>
<button
class="flex-no-shrink p-2 ml-2 border-2 rounded text-red border-red hover:bg-red">Remove</button>
</div>
</div>
</div>
</div>
标记任务为完成
当一个任务完成后,应将其标记为完成。这可以通过在任务中划一条线来实现。
为了实现这一点,我们将做以下工作。
- 用
markDone(value)
方法给todo.component.html
上的Done
按钮添加一个点击事件。
<p class="w-full text-grey-darkest" [ngClass]="{'line-through': value.completed}"> {{value.name}}</p>
<button class="flex-no-shrink p-2 ml-4 mr-2 border-2 rounded text-green border-green hover:bg-green" (click)="markDone(value)">Done</button>
- 在
TodoComponent
类中,我们将定义markDone()
方法。
//todo.component.ts
completed: boolean = false;
markDone(value: any) {
value.completed = !value.completed
value.completed === true ?
this.taskList.push(this.taskList.splice(this.taskList.indexOf(value), 1)[0]) :
this.taskList.unshift(this.taskList.splice(this.taskList.indexOf(value), 1)[0])
}
在上面的代码片段中,创建了一个变量completed
,并设置为布尔值false
。这个变量用于切换Done
按钮。因此,当value.complete
为真时,将一个穿行类绑定到任务中。
<p class="w-full text-grey-darkest" [ngClass]="{'line-through': value.completed}"> {{value.name}}</p>
接下来是一个条件语句;如果value.complete
是true
,那么使用push()
方法将任务推到taskList[]
数组的末端。否则,使用unshift()
方法将其移到taskList
数组的开头。
让我们继续下去,在我们的代码中实现这一点,如下所示。
//todo.component.ts
import { Component, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms';
@Component({
selector: 'app-todo',
templateUrl: './todo.component.html',
styleUrls: ['./todo.component.css']
})
export class TodoComponent implements OnInit {
completed: boolean = false;
taskList: any[] = []
newTodoForm = this.formBuilder.group({
todoItem: ''
})
constructor(
private formBuilder: FormBuilder
) { }
addTask() {
const value = this.newTodoForm.value.todoItem
this.taskList.push({ id: this.taskList.length, name: value })
this.newTodoForm.reset();
}
markDone(value: any) {
value.completed = !value.completed
value.completed === true ?
this.taskList.push(this.taskList.splice(this.taskList.indexOf(value), 1)[0]) :
this.taskList.unshift(this.taskList.splice(this.taskList.indexOf(value), 1)[0])
}
}
<div class="h-100 w-full flex items-center justify-center bg-teal-lightest font-sans">
<div class="bg-white rounded shadow p-6 m-4 w-full lg:w-3/4 lg:max-w-lg mt-4">
<div class="mb-4">
<h1 class="text-4xl font-black">Todo List</h1>
<form [formGroup]="newTodoForm" class="flex mt-4" (ngSubmit)="addTask()">
<input class="shadow appearance-none border rounded w-full py-2 px-3 mr-4 text-grey-darker"
placeholder="Add task" formControlName="todoItem">
<button type="submit"
class="flex-no-shrink p-2 border-2 rounded text-teal border-teal hover:text-black hover:bg-teal"
>Add
</button>
</form>
</div>
<div>
<div class="flex mb-4 items-center" *ngFor= "let value of taskList">
<p class="w-full text-grey-darkest" [ngClass]="{'line-through': value.completed}">{{value.name}}</p>
<button class="flex-no-shrink p-2 ml-4 mr-2 border-2 rounded text-green border-green hover:bg-green" (click)="markDone(value)">Done</button>
<button
class="flex-no-shrink p-2 ml-2 border-2 rounded text-red border-red hover:bg-red">Remove</button>
</div>
</div>
</div>
</div>
删除一个任务
任务可以被删除或从待办事项列表中移除。为了实现这一点,应该同时进行以下工作。
- 给
Remove
按钮添加一个点击事件。接下来,将点击事件设置为removeTask(i)
方法,将任务的索引传入该方法。
<div>
<div class="flex mb-4 items-center" *ngFor= "let value of taskList; index as i">
<p class="w-full text-grey-darkest" [ngClass]="{'line-through': value.completed}">{{value.name}}</p>
<button
class="flex-no-shrink p-2 ml-4 mr-2 border-2 rounded text-green border-green hover:bg-green" (click)="markDone(value)">Done</button>
<button
class="flex-no-shrink p-2 ml-2 border-2 rounded text-red border-red hover:bg-red" (click)="removeTask(i)">Remove</button>
</div>
</div>
</div>
</div>
- 在
todo.component.ts
文件中定义removeTask
方法。
//todo.component.ts
removeTask(i: any) {
this.taskList.splice(i, 1)
}
splice()
方法将移除其索引被传入removeTask
方法的任务。
保存任务到本地存储
LocalStorage是一个JavaScript功能,允许网站以键值对的形式存储数据。
浏览器存储这些数据时没有过期日期。这意味着,即使浏览器被刷新或关闭后,存储的数据仍将保留。
有不同的localStorage方法,可以用来实现特定的需求。
当任何任务被添加到待办事项列表时,它也应该被添加到本地存储中。这可以通过修改addTask()
方法来实现,如下所示。
//todo.component.ts
addTask() {
const value = this.newTodoForm.value.todoItem
this.taskList.push({ id: this.taskList.length, name: value })
window.localStorage.setItem('task', JSON.stringify(this.taskList))
this.newTodoForm.reset();
}
localStorage是Windows界面的一个只读属性。因此,在上面的代码片断中使用了windows.localStorage
。
使用.setItem()
,它接受一个键和值,一个任务被存储在本地存储。在这种情况下,键是task
,而值是taskList
数组。
JSON.stringify()
方法将taskList
数组的内容转换为字符串。接下来,将window.localStorage.setItem('task', JSON.stringify(this.taskList))
添加到removeTask()
。这样,即使在浏览器被刷新后,也能保持任务从待办事项列表中删除。
//todo.component.ts
removeTask(i: any) {
this.taskList.splice(i, 1)
window.localStorage.setItem('task', JSON.stringify(this.taskList))
}
最后,我们希望保存的任务即使在关闭或刷新浏览器后也能保留。为了实现这一点,我们必须使用一个名为[ngOninit()](https://angular.io/api/core/OnInit)
的angular生命周期钩子。
该[ngOninit()](https://angular.io/api/core/OnInit)
,只有在指令被实例化时才会被调用。
//todo.component.ts
ngOnInit(): void {
this.taskList = window.localStorage.getItem('task') ? JSON.parse(localStorage.getItem('task')) : []
}
在上面的代码中,本地存储方法getItem()
被用来从本地存储中获取项目并显示它们。
总结
这篇文章是一个完整的指南,告诉你如何在Angular中建立一个简单的任务管理器。我知道这对刚开始学习Angular的开发者会有很大帮助。