如何使用Angular 12构建一个任务管理器

41 阅读7分钟

如何使用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>

输出。

Todo List

注意:按钮是静态的,所以我们接下来要让它们发挥作用。

添加任务到待办事项列表

通过点击Add 按钮或按下Enter 键,任务将被添加到待办事项列表中。

让我们看看引擎盖下会发生什么,因为Add 按钮和输入框都在一个表单元素中,我们使用AngularFormGroup属性来处理它们。

为了使用FormGroup属性,我们首先在app.module.ts 文件中导入它,如下图所示。

import { FormsModule, ReactiveFormsModule } from '@angular/forms';

接下来,将模块添加到imports 数组中,如下图所示。

Form group

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代码。

遵循这些步骤。

  1. @angular/forms 包中导入[FormBuilder](https://angular.io/api/forms/FormBuilder) 服务。这个服务为创建控件提供了合适的方法。
    //todo.component.ts
    import { Component, OnInit } from '@angular/core';
    import { FormBuilder } from '@angular/forms';
  1. TodoComponent constructor() 中注入FormBuilder 服务。这个服务包含在[ReactiveFormsModule](https://angular.io/api/forms/ReactiveFormsModule) 模块中,这个模块已经被导入。
    //todo.component.ts
    
    export class TodoComponent implements OnInit {
    constructor(
    private formBuilder: FormBuilder
    ) {}
    } 
  1. 我们将使用FormBuilder 类的group() 方法来收集用户的输入。这些方法将newTodoForm 属性设置为一个包含todoItem 字段的表单模型。
    //todo.component.ts
    export class TodoComponent implements OnInit {
    newTodoForm = this.formBuilder.group({
        todoItem: ''
      })
    constructor(
    private formBuilder: FormBuilder
    ) {}
    } 
  1. 定义一个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>

标记任务为完成

当一个任务完成后,应将其标记为完成。这可以通过在任务中划一条线来实现。

为了实现这一点,我们将做以下工作。

  1. 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>
  1. 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.completetrue,那么使用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>

删除一个任务

任务可以被删除或从待办事项列表中移除。为了实现这一点,应该同时进行以下工作。

  1. 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>
  1. 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的开发者会有很大帮助。