使用Angular编写TodoMVC Vol 4

序言

本章将通过angular默认的ng-route组件来复用视图与前端的路由控制。

上一个章节的完成图

Vol3 完成图

本章目的

在底部增加一个状态的过滤,可以分别显示“全部”,“未完成”和“已完成”不同状态下的清单。

1. 使用ng-route复用视图

1.1 调整index.html

首先我们先来审阅一下当年的index.html代码

<!doctype html>
<html lang="en" data-framework="angularjs">
<head>
    <meta charset="utf-8">
    <title>AngularJS • TodoMVC</title>
    <link rel="stylesheet" href="node_modules/todomvc-common/base.css">
    <link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
</head>
<body ng-app="todomvc" ng-controller="TodoController as vm">
<section id="todoapp">
    <header id="header">
        <h1>todos</h1>
        <form id="todo-form" ng-submit="vm.addTask()">
            <input id="new-todo" placeholder="添加新的任务?" ng-model="vm.newTask" autofocus>
        </form>
    </header>
    <section id="main" ng-cloak>
        <ul id="todo-list">
            <li ng-repeat="task in vm.tasks" ng-class="{completed: task.completed, editing: task === vm.editedTask}">
                <div class="view">
                    <input class="toggle" type="checkbox" ng-model="task.completed">
                    <label ng-dblclick="vm.editTask(task)">{{task.title}}</label>
                    <button class="destroy" ng-click="vm.removeTask(task)"></button>
                </div>
                <form>
                    <input class="edit" ng-trim="false" ng-model="task.title"  >
                </form>
            </li>
        </ul>
    </section>
</section>
    <script src="node_modules/angular/angular.js"></script>
    <script src="node_modules/angular-route/angular-route.js"></script>
    <script src="js/app.js"></script>
    <script src="js/controllers/todo.controller.js"></script>
</body>
</html>

body标签的section标签内的内容才是有有逻辑并且可分离的视图代码。
故我们使用ng-route来规划前端的url分发控制与对应的,通过对目标URL的撞他变化加载对应的视图与控制器文件。

1.2 分离业务视图至partials/todo.html文件

我们将index.html代码分离出来,新建一个partials/todo.html,其内容如下。
目录结构

新建partials目录存放视图文件
<section id="todoapp">
    <header id="header">
        <h1>todos</h1>
        <form id="todo-form" ng-submit="vm.addTask()">
            <input id="new-todo" placeholder="添加新的任务?" ng-model="vm.newTask" autofocus>
        </form>
    </header>
    <section id="main" ng-cloak>
        <ul id="todo-list">
            <li ng-repeat="task in vm.tasks" ng-class="{completed: task.completed, editing: task === vm.editedTask}">
                <div class="view">
                    <input class="toggle" type="checkbox" ng-model="task.completed">
                    <label ng-dblclick="vm.editTask(task)">{{task.title}}</label>
                    <button class="destroy" ng-click="vm.removeTask(task)"></button>
                </div>
                <form>
                    <input class="edit" ng-trim="false" ng-model="task.title"  >
                </form>
            </li>
        </ul>
    </section>
</section>

消除这段代码后整个index.html就只剩下相关的入口及配置信息代码就变得干净很多。
因为使用了ng-route之后控制器的初始化就不需要我们显式的写在视图中。故我们删除掉body标签中的ng-controller属性。

<!--
<body ng-app="todomvc" ng-controller="TodoController as vm">
-->
<body ng-app="todomvc">

最后在body标签下引入ng-route的动态视图指令ng-view

<body ng-app="todomvc">
    <ng-view />

    <script src="node_modules/angular/angular.js"></script>
    <script src="node_modules/angular-route/angular-route.js"></script>
    <script src="js/app.js"></script>
    <script src="js/controllers/todo.controller.js"></script>
</body>

1.3 引入route配置信息

我们重新编辑app.js文件增加根路径的视图与控制器加载逻辑。

(function(){
    'use strict';

    angular.module('todomvc', ['ngRoute'])
        .config(routerConfig);
    routerConfig.$inject = ['$routeProvider'];


    function routerConfig($routeProvider){
        $routeProvider
        .when('/',{
            controller: 'TodoController',
            templateUrl: 'partials/todo.html',
            controllerAs: 'vm'
        })
    };
})();

routeConfig中操作$routeProvider服务,向其添加路由规则。

.when('/',{
            controller: 'TodoController', //控制器
            templateUrl: 'partials/todo.html', //视图
            controllerAs: 'vm' // 控制器As的别名语法,Angular1.2以后提倡的语法
        })

这样我们就让angular在客户端维护了一个可以根据url做出影响的前端运行时环境,这个时候我们重新在浏览器刷新地址则发现应用与之前是没区别的,唯一的区别是路径上多了一个#符号。


多了一个#号

默认模式下,浏览器都只访问index.html的入口页 /#/之后的路径为ng-route控制的url部分,这样即使前端便可以进行无刷新的页面url的跳转操作了。

2. 向应用中添加底部状态控制栏

这次我们希望通过不同的url来显示不同的任务清单列表:

  • 在/路径下显示所有状态的任务清单
  • 在/active路径下显示所有未完成的任务清单
  • 在/completed路径下显示所有完成的任务清单

2.1 修改ng-route

添加新/:status路由,将active于completed作为参数传给controller做数据的筛选。

      .when('/:status', {
        controller: 'TodoController',
        templateUrl: 'partials/todo.html',
        controllerAs: 'vm'
      })

2.2 在controller层获取路由参数

我们在Controller层想要获取路径上的参数,则需要向Controller注入$routeParams服务,在通过$routeParams.paramName的形式便可以获取对应的参数值。
我们修改我们TodoController使其可以对不同的status状态做出不同的过滤操作。
首先,我们编写一个函数可以根据不同的status值来过滤处理controller中的任务清单数组

        function _filterDataByStatus(tasks,status){
            if (status === 'active'){
                return tasks.filter(function(task){
                    return (task.completed != true )
                }) 
            }else if(status === 'completed'){
                return tasks.filter(function(task){
                    return (task.completed == true )
                }) 
            }else{
                return tasks;
            }
        }

然后,调整TodoController中的初始化逻辑,通过$routeParam服务来获取路径中的status的值。

        function init(){

            var tasks = [
            {
                title: "第一个任务",
                completed: true
            },
            {
                title: "第二个任务",
                completed: false

            }];
            vm.status = $routeParams.status||"";
            vm.tasks = _filterDataByStatus(tasks,vm.status)
        }

我们额外声明了一个vm.status变量用于记录当前的status的值,用于模板中的底部状态过滤标签对当前激活的过滤条件做高亮显示。

2.3 增加底部的状态切换区域

我们在todo.html中增加一段新的footer标签区域用于显示底部的状态切换的状态标签按钮。


    <footer id="footer" ng-show="vm.tasks.length" ng-cloak>
        <span id="todo-count"><strong>{{remainingCount}}</strong>
            <ng-pluralize count="remainingCount" when="{ one: 'item left', other: 'items left' }"></ng-pluralize>
        </span>
        <ul id="filters">
            <li>
                <a ng-class="{selected: vm.status == ''} " href="#/">All</a>
            </li>
            <li>
                <a ng-class="{selected: vm.status == 'active'}" href="#/active">Active</a>
            </li>
            <li>
                <a ng-class="{selected: vm.status == 'completed'}" href="#/completed">Completed</a>
            </li>
        </ul>
    </footer>

底部有三个按钮分别对应路径"/","/active"与"/completed"。并根据vm.status使用ng-class来高亮显示当前激活的过滤条件标签。

2.4 测试应用

我们分别测试不同状态下的清单显示情况

2.4.1 默认显示全部

显示全部

2.4.2 待完成

待完成

2.4.3 已完成

已完成

2.5 问题

似乎看上去我们的过滤功能以前完成了,但是我们可以添加一个新的任务,再进行条件过滤,这个时候则会发现新建的任务清单“不见了”。
这是因为不同路由下的虽然为同名controller但是其中的实例变量还是会重新初始化。两个Controller实例没有进行数据的共享。
vm.tasks的赋值逻辑

            var tasks = [
            {
                title: "第一个任务",
                completed: true
            },
            {
                title: "第二个任务",
                completed: false

            }];

            vm.status = $routeParams.status||"";

            //此处的进行双向绑定
            vm.tasks = _filterDataByStatus(tasks,vm.status)
不同路由下不同Controller实例中的变量不同享

在下一章的文章中我们将介绍Angular中常见几种在controller中共享、传递数据的方法并选取一种来调整我们的代码。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,029评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,238评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,576评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,214评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,324评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,392评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,416评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,196评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,631评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,919评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,090评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,767评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,410评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,090评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,328评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,952评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,979评论 2 351

推荐阅读更多精彩内容

  • AngularJS是什么?AngularJs(后面就简称ng了)是一个用于设计动态web应用的结构框架。首先,它是...
    200813阅读 1,592评论 0 3
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,644评论 18 139
  • 序言 本章我们将学习如果利用service在controller之间共享、传递数据。还将调整我们在不同状态下显示不...
    AkiraPan阅读 610评论 0 1
  • AngularJSAngularJS 是一个 MV* 框架, 最适于开发客户端的单页面应用。它不是个功能库,...
    一直以来都很好阅读 894评论 0 0
  • 通过AngularJS仿豆瓣一刻的案例:https://github.com/zhongxiaolian/doub...
    中小恋阅读 1,755评论 1 21