Angular 学习之路 17 – Todo App

前面我们已经简单学习过 Angular 的一些核心内容,下面我们将完成一个演示项目,来看看在实际工程中,这些内容如何有机的整合在一起。

我们选择的是 TodoMVC。TodoMVC 是一个开源 JavaScript 项目框架。不同于一个项目或者一个开发库,TodoMVC 提供了一个应用程序的标准范本需求,要求使用各种不同的 MV* 框架实现这么一个 Todo 应用。

TodoMVC 是一个开源 JavaScript 项目框架。不同于一个项目或者一个开发库,TodoMVC 提供了一个应用程序的标准范本需求,要求使用各种不同的 MV* 框架实现这么一个 Todo 应用。TodoMVC 目的是通过使用各种不同的开发框架实现同一个应用,使我们能够清晰对比各个开发框架之间的区别。

虽然 TodoMVC 的目的是对比各个开发框架之间的区别,但它提供了一个足够清晰且准确的需求列表,那么我们就可以通过实现这么一个项目,来学习 Angular 的开发。

TodoMVC 的完整需求如下(原文地址在 https://github.com/tastejs/todomvc/blob/master/app-spec.md):

没有待办时

当没有待办时,应当隐藏#main#footer

新的待办

在应用顶部的输入框敲下回车,添加新的待办。当页面加载完毕时,这个 input 应该获得焦点,最好是使用 input 的属性autofocus。按下回车创建一个新的待办,将其追加到待办列表的末尾,然后清空输入框。记得要对 input 调用 .trim()函数,然后在创建新的待办之前检查非空。

将所有待办标记为已完成

checkbox 将所有待办修改为与其自身相同的状态。记得在点击了“Clear completed”按钮之后,清空已选择的状态。在某个待办完成或未完成时,“Mark all as complete”选择框的状态应该随之改变。例如,当所有待办都标记为已完成时,这个选择框也应该被选中。

待办

一个待办有三种可能的交互:

  1. 点击选择框,将该待办标记为已完成。这一步骤需要更新其completed属性的值,然后切换其父元素<li>completed
  2. 双击<label>进入编辑模式,将 .editing 类添加到<li>
  3. 鼠标滑过待办列表,显示移除按钮(.destroy

编辑

进入编辑模式之后,隐藏其它组件,显示一个 input,input 的默认值是当前编辑的待办内容。该 input 应该获得焦点(.focus())。在失去焦点或者按下回车时,提交保存编辑的信息,同时移除editing类。记得要对 input 调用.trim()函数,然后检查是否非空。如果为空,则应该删除该待办。如果编辑时点击 ESC 键,则应该离开编辑模式,丢弃所做的任何修改。

计数器

以复数形式显示当前有效的待办。将数字放在<strong>标签中。同时需要正确显示item的复数形式:0 items1 item2 items。例如,2 items left。

Clear completed 按钮

点击之后移除已完成的待办。如果没有已完成的待办,隐藏该按钮。

持久化

应用应该能够动态将待办持久化保存到 localStorage。如果开发框架提供了持久化数据的方法(例如 Backbone.sync),直接使用框架提供的功能。否则,使用标准的 localStorage。尽可能使用 idtitlecompleted保存每一个项目。记得使用如下 localStorage 的名字:todos-[framework]。编辑模式不能被持久化保存。

路由

所有实现都应该有路由。如果框架支持路由,使用框架内建功能。否则,使用/assets文件夹中提供的 Flatiron Director 路由库。应该实现下面的路由:#/ (所有 - 默认);#/active 和 #/completed (也可以使用 #!/)。当路由发生改变时,待办列表应该在模型层次过滤,然后在过滤结果的链接上添加 selected 类。在过滤结果中修改项目,应该同时更新。例如,在过滤结果 Active 中,项目被选中,则其应该被隐藏。记得在重新加载时要激活当前过滤结果。

下面,我们来使用 Angular 完成这样一个项目。

首先,我们要创建一个项目,使用前面介绍过的命令:

ng new todomvc-app

等待项目初始化完成,就可以看到之前我们熟悉的项目结构。

TodoMVC 项目结构

接下来,我们在生成项目中实现上面说的 TodoMVC 应用。

TodoMVC 的 HTML 文件在 todomvc-app-template/index.html,CSS 文件在 todomvc-app-css 仓库。我们可以使用 git 获取全部所需的文件:

git clone https://github.com/tastejs/todomvc-app-template.git
git clone https://github.com/tastejs/todomvc-app-css.git

在 todomvc-app-template 仓库,我们要使用的是 index.html 这个文件,在 todomvc-app-css 仓库,我们要使用的是 index.css 这个文件。注意,虽然我们得到的是两个单一文件,但我们的 Angular 应用需要将页面按照组件切分,并且每个组件的样式应该在组件自己的样式表中,所以,我们不会直接使用这些文件,而是会将其按照组件分割,保存到对应的组件中。

下面为了查看最终效果,我们将 index.html 复制到项目目录下的 src/app/app.component.html,将 index.css 复制到项目下的 src/styles.css。

由于原始版本的 index.html 有 CSS 和 JS 的导入语句,会导致 Angular 编译失败,所以我们需要将复制之后的 app.component.html 中的 <link> 和 <script> 这样的标签全部注释或删除。

然后使用ng serve运行项目:

TodoMVC app 运行效果

这就是我们最终的运行效果。

我们在开始一个项目时,一般会先由设计人员设计好页面,然后由美工开发人员依照设计稿完成 HTML 和 CSS,最后交给程序开发人员实现业务功能。现在,我们已经有了 HTML 和 CSS,那么接下来就是要按照设计图分析出整个页面所需要的组件。

虽然我们不能保证最终的实现究竟是由几个组件构成(毕竟开发过程中会遇到很多意想不到的问题,这是非常常见的),但我们还是可以将页面大致分为三个组件:app-header,app-todo-list 和 app-footer;其中,app-todo-list 由一个 app-todo-list-creator,一组 app-todo-list-item 和一个 app-todo-list-footer 组成。

现在我们创建这些组件:

ng g c header
ng g c todo-list
ng g c footer

然后,我们把 app.component.html 里面的<header>标签移动到 header.component.html。注意,因为模板原始的 HTML 代码中,用于输入待办事项的input.new-todo是在<header>中的,所以这部分代码也放在了app-header组件。

<header class="header">
  <h1>todos</h1>
  <input class="new-todo" placeholder="What needs to be done?" autofocus>
</header>

把刚刚移走的代码替换为HeaderComponent的选择器app-header

...
<section class="todoapp">
  <app-header></app-header>
  <!-- This section should be hidden by default and shown when there are todos -->
  <section class="main">
    ...

再把 app.component.html 中最后的<footer>标签移动到 footer.component.html:

<footer class="info">
  <p>Double-click to edit a todo</p>
  <!-- Remove the below line ↓ -->
  <p>Template by <a href="http://sindresorhus.com">Sindre Sorhus</a></p>
  <!-- Change this out with your name and url ↓ -->
  <p>Created by <a href="http://todomvc.com">you</a></p>
  <p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>

再将原来的代码替换为app-footer

    ...
    <!-- Hidden if no completed items are left ↓ -->
    <button class="clear-completed">Clear completed</button>
  </footer>
</section>
<app-footer></app-footer>
<!-- Scripts here. Don't remove ↓ -->
...

这样,我们使用自己创建的 Angular 组件替换了原来的 HTML 代码。重新运行一下,页面跟原来是一样的,说明我们进行了无缝替换。注意,这里我们只迁移了 HTML 代码,所有的 CSS 还是在 styles.css 文件中。可以把这个文件理解为全局样式表,因此,尽管 HTML 分散在各个组件,但样式还是没有影响。在后面的章节中,我们会慢慢将 CSS 也拆分到具体的组件。

接下来,我们继续拆分组件:把section.mainfooter.footer的代码移动到app-todo-list组件。之所以要把这部分剥离,主要是考虑到,整个 TodoAPP 的业务都集中在这部分,将其独立出来会更方便开发。为方便期间,我们只是简单地将 HTML 代码移动到组件中,也可以考虑创建一个独立的模块,专门处理这部分业务逻辑。

重新刷新一下页面,可以看到没有任何变化。

现在,我们已经大致完成了组件的拆分。接下来,我们将逐步实现 TodoMVC 的全部需求。

Leave a Reply