前面我们已经简单学习过 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”选择框的状态应该随之改变。例如,当所有待办都标记为已完成时,这个选择框也应该被选中。
待办
一个待办有三种可能的交互:
- 点击选择框,将该待办标记为已完成。这一步骤需要更新其
completed
属性的值,然后切换其父元素<li>
的completed
类 - 双击
<label>
进入编辑模式,将.editing
类添加到<li>
- 鼠标滑过待办列表,显示移除按钮(
.destroy
)
编辑
进入编辑模式之后,隐藏其它组件,显示一个 input,input 的默认值是当前编辑的待办内容。该 input 应该获得焦点(.focus()
)。在失去焦点或者按下回车时,提交保存编辑的信息,同时移除editing
类。记得要对 input 调用.trim()
函数,然后检查是否非空。如果为空,则应该删除该待办。如果编辑时点击 ESC 键,则应该离开编辑模式,丢弃所做的任何修改。
计数器
以复数形式显示当前有效的待办。将数字放在<strong>
标签中。同时需要正确显示item
的复数形式:0 items
、1 item
、2 items
。例如,2 items left。
Clear completed 按钮
点击之后移除已完成的待办。如果没有已完成的待办,隐藏该按钮。
持久化
应用应该能够动态将待办持久化保存到 localStorage。如果开发框架提供了持久化数据的方法(例如 Backbone.sync),直接使用框架提供的功能。否则,使用标准的 localStorage。尽可能使用 id
、title
、completed
保存每一个项目。记得使用如下 localStorage 的名字:todos-[framework]
。编辑模式不能被持久化保存。
路由
所有实现都应该有路由。如果框架支持路由,使用框架内建功能。否则,使用/assets
文件夹中提供的 Flatiron Director 路由库。应该实现下面的路由:#/
(所有 - 默认);#/active
和 #/completed
(也可以使用 #!/
)。当路由发生改变时,待办列表应该在模型层次过滤,然后在过滤结果的链接上添加 selected
类。在过滤结果中修改项目,应该同时更新。例如,在过滤结果 Active
中,项目被选中,则其应该被隐藏。记得在重新加载时要激活当前过滤结果。
下面,我们来使用 Angular 完成这样一个项目。
首先,我们要创建一个项目,使用前面介绍过的命令:
ng new todomvc-app
等待项目初始化完成,就可以看到之前我们熟悉的项目结构。
接下来,我们在生成项目中实现上面说的 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
运行项目:
这就是我们最终的运行效果。
我们在开始一个项目时,一般会先由设计人员设计好页面,然后由美工开发人员依照设计稿完成 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.main
和footer.footer
的代码移动到app-todo-list
组件。之所以要把这部分剥离,主要是考虑到,整个 TodoAPP 的业务都集中在这部分,将其独立出来会更方便开发。为方便期间,我们只是简单地将 HTML 代码移动到组件中,也可以考虑创建一个独立的模块,专门处理这部分业务逻辑。
重新刷新一下页面,可以看到没有任何变化。
现在,我们已经大致完成了组件的拆分。接下来,我们将逐步实现 TodoMVC 的全部需求。