美文网首页程序员我爱编程
使用Angular编写TodoMVC Vol 4

使用Angular编写TodoMVC Vol 4

作者: AkiraPan | 来源:发表于2016-06-06 15:46 被阅读163次

序言

本章将通过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中共享、传递数据的方法并选取一种来调整我们的代码。

相关文章

网友评论

    本文标题:使用Angular编写TodoMVC Vol 4

    本文链接:https://www.haomeiwen.com/subject/peakdttx.html