Angular项目 TodoMVC

起步

  • 准备

    • 下载静态模板
      • git clone https://github.com/tastejs/todomvc-app-template.git --depth 1
      • 在静态文件根目录下 npm install
    • 初始化项目
      • ng new todomvc-angular
      • 在进行基础的选择之后,ctrl+c打断下载
      • 打开新建的项目目录,CMD进入cnpm install
      • 启动npm start或者ng serve
      • angular后台服务器开启http://localhost:4200/
  • 将静态模板装载到angular脚手架生成的项目文件中

    • 将静态文件中的index.html文件HTML骨架复制粘贴到项目app文件夹下的app.component.html文件中
    • 在项目文件的根目录中引入安装包todomvc-common todomvc-app-css,cnpm i todomvc-common todomvc-app-css
    • 在项目文件根目录中,找到angular.json文件,找到styles全局配置文件路径文件,打开此路径中的文件,引入全局css配置,依赖文件
      • 这里载入的第三方包中的样式文件可以不写路径,直接写第三方包名称路径即可
      • 这里的CSS样式也可以写入局部CSS配置中
      • @import url('todomvc-app-css/index.css');
      • @import url('todomvc-common/base.css');
      • 至此,静态页面完全移植至angular项目中
  • TodoMVC需求说明

    • 数据列表显示
      • 有数据
      • 无数据
    • 添加任务
      • 页面初始化的时候文本框获得焦点
      • 敲回车添加到任务列表中
      • 不允许有非空数据
      • 添加完成清空文本框
    • 标记所有任务完成/未完成
    • 任务项
      • 切换任务完成状态
      • 删除任务项
      • 双击label进入编辑模式
    • 编辑任务项
      • 编辑文本框自动获取焦点
      • 在编辑文本框中敲回车或者失去焦点
      • 输入状态按下esc取消编辑
    • 显示所有未完成任务数
      • 模板逻辑
      • 方法
      • 计算属性
    • 清除所有已完成任务
    • 将数据持久化到localStorage中
    • 路由状态切换
      • 点击链接过滤数据的输出
      • 刷新保持过滤状态
      • 切换点击链接的样式
  • angular列表循环的写法

    • *ngFor = "表达式"
    • <li *ngFor="let todo of todos">{{todo.title}}</li>
  • angular条件渲染

    • *ngIf="todos.length"
    • 若是在某标签中写上为假的判断*ngIf="false",则此标签隐藏
  • angular模板语法之层级包裹

    • 原先写法,在需要的模块架构之上,加上统一的div,然后写上判断语句*ngIf="todos.length"
    • 模板语法:将div改成<ng-template [ngIf]="todos.length"></ng-template>
<ng-template [ngIf]="todos.length"></ng-template>
  • angular事件的写法
    • (keyup.enter)="addTodo($event)"
    • addTodo事件函数中,this指向数据对象,这点和Vue相似,事件指向数据
<input 
  class="new-todo" 
  placeholder="What needs to be done?" 
  autofocus 
  (keyup.enter)="addTodo($event)"
>
  • angular双向数据绑定
    • 在主模块中引入import { FormsModule } from '@angular/forms';
    • 在主模块下面的依赖列表中加入:FormsModule
    • [(ngModel)]="todo.done"
<input 
  class="toggle" 
  type="checkbox"
  [(ngModel)]="todo.done"
>
  • angular样式绑定
    • [ngClass]="{completed: todo.done}"
<li 
  *ngFor="let todo of todos"
  [ngClass]="{completed: todo.done}"
>
  • get set属性存取器
    • get 数据写入页面
      • 当所有的t.done为真时,返回数据true,写入checked中
    • set 数据取出页面
      • 当input状态改变时,将checked的值传入函数,然后子类每项赋值
  // 子开关控制总开关,全选总开关选中
  // 所有的t.done为真时,返回数据,存入checked
  get toggleAll(){
    return this.todos.every(t => t.done)
  }
  // 总开关控制子开关,全选全不选
  // 当总开关触发时`$event.target.checked`
  // 当input状态改变时,将checked的值传入函数,然后每项赋值
  set toggleAll(val){
    this.todos.forEach(t => t.done = val)
  }
  • angular获取当前索引
    • 在for循环时,拿下索引变量i
    • 将索引变量传参给事件函数removeTodo(i)
    • 索引函数得到当前索引值
<li 
  *ngFor="let todo of todos; let i = index;"
  [ngClass]="{completed: todo.done}"
>
  <button 
    class="destroy"
    (click)='removeTodo(i)'
  ></button>
</li>
removeTodo(index: number){
  this.todos.splice(index, 1)
  console.log(index);
}
  • 删除当前项方法
  removeTodo(index: number){
    this.todos.splice(index, 1)
  }
  • ngClass属性
    • 类名: 布尔判断
<li 
  *ngFor="let todo of todos; let i = index;"
  [ngClass]="{
    completed: todo.done,
    editing: currentEditing === todo
  }"
></li>
  • 关于选择当前对象
    • 在循环对象创建中,是一个对象数组
    • 因此,额外的创建一个空对象用来装载当前数据,以索引值为契机
    • 然后,将装载的数据在对象数组中遍历匹配
    • 匹配成功,即为选中当前对象
    • 取消选择,只要将临时的对象清空即可
const todos = [
  {
    id: 1,
    title: '摄影',
    done: true
  },
  {
    id: 2,
    title: '设计',
    done: true
  },
  {
    id: 3,
    title: '程序',
    done: false
  }
]
// 定义对象数组
public todos: {
  id: number,
  title: string,
  done: boolean
}[] = todos
// 定义匹配对象
public currentEditing: {
  id: number,
  title: string,
  done: boolean
} = null
<!-- 将匹配对象在数组对象中遍历寻找,相同即执行 -->
<li 
  *ngFor="let todo of todos; let i = index;"
  [ngClass]="{
    completed: todo.done,
    editing: currentEditing === todo
  }"
>
<!-- 鼠标双击后,将当前值传给匹配对象 -->
<label
  (dblclick)="currentEditing = todo"
>{{ todo.title }}</label>
</li>
  • 解构赋值
    • e就是$event的传参
const {keyCode, target} = e;
  • 过滤器
    • 过滤器中的回调函数用箭头函数,可以避免this问题
get remaningCount(){
  return this.todos.filter(t => !t.done).length
}
  • 获取标签内容

    • e.srcElement.innerHTML
    • e.srcElement.innerText
  • 实现导航切换数据过滤的功能

  public visibility: string = "all"
  get filterTodos(){
    if(this.visibility === 'all'){
      return todos
    }else if(this.visibility === 'active'){
      return todos.filter(t => !t.done)
    }else if(this.visibility === 'completed'){
      return todos.filter(t => t.done)
    }
  }
  • 初始化钩子函数

    • 初始化钩子函数 ngOnInit()
    • 该函数是一个特殊的Angular生命周期钩子函数
    • 他会在Anhular应用初始化的时候执行一次
  • 哈希函数

    • 哈希函数即是描点函数
  ngOnInit(){
    // Angular初始化时,监听hash的变化
    // 此处要用箭头函数
    window.onhashchange = () => {
      // 当用户点击锚点时,我们需要获取当前的锚点链接
      // 然后动态的将根组件中的visibility设置为当前点击的锚点标识
      const hash = window.location.hash;
      console.log(hash);
    }
  }
  • 实现导航切换数据过滤的功能
    • 提供一个属性,该属性会根据当前点击的链接返回过滤之后的数据
      • filterTodos
    • 提供一个属性,用来存储当前点击的链接标识
      • visibility 是一个字符串
      • all active completed
    • 为链接添加点击事件,当点击导航链接的时候,改变
      • 在方法内写出选择判断,当点击某个值时,将选项匹配给visibility
      • 改变li遍历创建时的规则,由全部创建改为,filterTodos方法返回的数据
<li 
  *ngFor="let todo of filterTodos; let i = index;"
  [ngClass]="{
    completed: todo.done,
    editing: currentEditing === todo
  }"
>
</li>
public visibility: string = "all"
ngOnInit(){
  // Angular初始化时,监听hash的变化
  // 此处要用箭头函数
  window.onhashchange = () => {
    // 当用户点击锚点时,我们需要获取当前的锚点链接
    // 然后动态的将根组件中的visibility设置为当前点击的锚点标识
    const hash = window.location.hash.substr(1)
    switch(hash){
      case '/':
        this.visibility = 'all'
        break
      case '/active':
        this.visibility = 'active'
        break
      case '/completed':
        this.visibility = 'completed'
        break
    }
  }
}
get filterTodos(){
  if(this.visibility === 'all'){
    return todos
  }else if(this.visibility === 'active'){
    return todos.filter(t => !t.done)
  }else if(this.visibility === 'completed'){
    return todos.filter(t => t.done)
  }
}
  • 刷新保存过滤状态
    • 将以上代码中,哈希判断的代码作为另一个方法
    • 在钩子函数初始化时,执行一次哈希判断
    • 然后再在哈希事件中调用
    • 由于在哈希事件中,this指向window,因此,此时的this需要bind绑定
  ngOnInit(){
    // Angular初始化时,监听hash的变化
    // 此处要用箭头函数
    this.hashchangeHandler();
    window.onhashchange = this.hashchangeHandler.bind(this)
  }
  hashchangeHandler(){
    const hash = window.location.hash.substr(1)
    switch(hash){
      case '/':
        this.visibility = 'all'
        break
      case '/active':
        this.visibility = 'active'
        break
      case '/completed':
        this.visibility = 'completed'
        break
    }
  }
  • 数据改变触发的钩子函数

    • ngDoCheck()
    • 当数据发生变化时,刷新数据
  • 数据本地永久化存储

    • todos = JSON.parse(window.localStorage.getItem('todos') || '[]')
      • 将字符串转化为JSON数组
      • 从本地数据库window.localStorage读取数据
    • window.localStorage.setItem('todos', JSON.stringify(this.todos))
      • 将JSON数组转化为字符串
      • 将页面更新后的数据存储到本地数据库window.localStorage中
  public todos: {
    id: number,
    title: string,
    done: boolean
  }[] = JSON.parse(window.localStorage.getItem('todos') || '[]')

  ngDoCheck(){
    window.localStorage.setItem('todos', JSON.stringify(this.todos))
  }
原文地址:https://www.cnblogs.com/SharkJiao/p/13760848.html