把AngularJS路由(route)映射到URL参数和客户端事件

原文链接:http://www.bennadel.com/blog/2420-Mapping-AngularJS-Routes-Onto-URL-Parameters-And-Client-Side-Events.htm

原作者:Ben Nadel 2012-9-12 9:11

翻译:雪狼 2013-7-3

本文链接:http://angularjs.cn/A06X

不久以前,我讨论了映射RESTful资源URI到URL参数和服务端事件的问题。在开发一个瘦客户端程序的时候,同样需要在客户端实现很多与服务端类似的URL路由功能。但是对于不断成长的、复杂的富客户端界面(Rich client UI)来说,“路由-子页面”的机制会变得更加困难。稍后,我找到了Google的AngularJS作为定义客户端应用的框架。模仿上次的思路,我想我同样可以把AngularJS路由映射到客户端URL参数和客户端事件。 AngularJS天生具有一个健壮的路由和浏览历史管理机制。并且,它同时支持基于hash的(hash即url中#后面的部分)和基于HTML5 push-state的方案(这里我只使用基于hash的方案)。路由提供者(route provider)是把路由映射到模板路径时的核心概念,并且通过一系列的when()方法调用来实现:

$route.when(
    "/friends/:friendID",
    {
        templateUrl: "views/friend-detail.htm"
    }
);

在上面的例子中,指定的路由导致了指定的模板被渲染到页面上的ng-view指令处。这种方式对于比较小的网站和路由层次比较浅的网站是非常合适的,但是当你具有嵌套的导航和复杂的界面时,这种直接路由到子页面(route-to-partical)的编程模式很快就会变成瓶颈。

但是,你不用非得使用模板来实现路由,更好的方案是,你可以通过一些客户端渲染事件(render-event)来实现它。使用和映射RESTful资源URI到服务端事件相同的原理,我们可以把客户端路由解析为一组树形的、用于描述页面状态的值。

这个小把戏的关键是要明白URL的hash段可以包含任何数据(不过不能与一些保留字冲突)所以,与其我们传入一个templateUrl值,还不如传入一个event数据(event data):

$route.when(
    "/friends/:friendID",
    {
        event: "friends.view"
    }
);

在这个例子中,我们把路由映射到一个渲染事件:friends.view。其中“event”数据对应的参数(friends.view)将被解析为$route当前状态($route.current)的一部分,而路由中的“:friendID”值,将被解析为$routeParams的一部分。

为了展示这种方式如何渲染页面,我将创建只有一个controller的演示程序,它将根据路由提供的渲染事件(render-event)来渲染具体内容。

<!doctype html>
<html ng-app="Demo" ng-controller="AppController">
<head>
    <meta charset="utf-8" />

    <title>AngularJS Routing</title>

    <style type="text/css">

        a {
            color: #333333 ;
            }

        a.on {
            color: #CC0000 ;
            font-weight: bold ;
            text-decoration: none ;
            }

    </style>
</head>
<body>

    <h1>
        AngularJS Routing
    </h1>

    <p>
        Current Render Action:

        <!--
            我们将把strong元素中的内容绑定到一个当前作用域的模型(model)中,即renderAction变量。然后,当Controller得到renderAction变量时,它将被更新到这里。
        -->
        <strong ng-bind="renderAction">Unknown</strong>
    </p>

    <!--
        为了控制导航内容,我们将根据当前作用域中的状态来把"on"这个css类附加到下列元素上。
    -->
    <p>
        <a href="#/home" ng-class="{ on: isHome }">Home</a> -
        <a href="#/friends" ng-class="{ on: isFriends }">Friends</a> -
        <a href="#/contact/ben" ng-class="{ on: isContact }">Contact</a>
    </p>

    <!--
        当路由变化的时候,我们要设置renderPath:一个用于定义页面将如何渲染的数组型变量。
        我们可以根据这些值来显示/加载各个子页面。
    -->
    <div ng-switch on="renderPath[ 0 ]">

        <!-- Home Content. -->
        <div ng-switch-when="home">

            <p>
                This is the homepage content.
            </p>

            <p>
                Sub-path: <em>{{ renderPath[ 1 ] }}</em>.
            </p>

        </div>

        <!-- Friends Content. -->
        <div ng-switch-when="friends">

            <p>
                Here are my friends!
            </p>

            <p>
                Sub-path: <em>{{ renderPath[ 1 ] }}</em>.
            </p>

        </div>

        <!-- Contact Content. -->
        <div ng-switch-when="contact">

            <p>
                Feel free to contact me.
            </p>

            <p>
                Sub-path: <em>{{ renderPath[ 1 ] }}</em>.
            </p>

            <p>
                Username: <em>{{ username }}</em>
            </p>

        </div>

    </div>


    <!-- Load AngularJS from the CDN. -->
    <script
        type="text/javascript"
        src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.min.js">
    </script>
    <script type="text/javascript">


        // 创建我们这个演示程序的主module。
        var Demo = angular.module( "Demo", [] );

        // 配置路由,$routeProvider服务将被自动注入到config函数中。
        Demo.config(
            function( $routeProvider ){
                // 我们通常定义路由时,把路由映射到要用于渲染的模板,不过,这种方式只适合于简单网站。
                // 当你构建具有嵌套导航的复杂应用时,你可能需要做一些更复杂的事情。
                // 这种情况下,你需要路由到一组"动作(action)"而不是模板。
                $routeProvider
                    .when(
                        "/home",
                        {
                            action: "home.default"
                        }
                    )
                    .when(
                        "/friends",
                        {
                            action: "friends.list"
                        }
                    )
                    .when(
                        "/contact/:username",
                        {
                            action: "contact.form"
                        }
                    )
                    .otherwise(
                        {
                            redirectTo: "/dashboard"
                        }
                    )
                ;

            }
        );


        // -------------------------------------------------- //
        // -------------------------------------------------- //


        // 定义我们的根控制器
        Demo.controller(
            "AppController",
            function( $scope, $route, $routeParams ){

                // 更新页面的渲染方式
                render = function(){

                    // 取得当前所选路由的action值
                    var renderAction = $route.current.action;

                    // 接着,让我们修改renderPath变量,来使它更新到页面的相应位置。
                    var renderPath = renderAction.split( "." );

                    // 从params中获得username变量
                    // 注意:对于除了“contact”路由之外的任何路由,这个值都会是undefined。此处为了简化演示,我不对此做任何防御性编程。
                    var username = ($routeParams.username || "");

                    // 重置用于控制导航栏中css类的各个逻辑变量
                    var isHome = (renderPath[ 0 ] == "home");
                    var isFriends = (renderPath[ 0 ] == "friends");
                    var isContact = (renderPath[ 0 ] == "contact");

                    // 把这些变量保存到model(即$scope)中
                    $scope.renderAction = renderAction;
                    $scope.renderPath = renderPath;
                    $scope.username = username;
                    $scope.isHome = isHome;
                    $scope.isFriends = isFriends;
                    $scope.isContact = isContact;

                };

                // 开始监听“路由变化”事件,我们修改renderAction变量,以便它显示在Strong元素中
                $scope.$on(
                    "$routeChangeSuccess",
                    function( $currentRoute, $previousRoute ){

                        // 刷新显示内容
                        render();

                    }
                );

            }
        );


    </script>

</body>
</html>

在这个范例中,我们的应用程序控制器监听了路由的变化。当路由变化时(注意:在页面首次被加载时同样会触发),AppController取出路由映射表中的action值,并且注入到当前页面的scope中。 这个值(以及由它派生出来的renderPath变量)又被用于通过ng-switch指令来根据条件渲染页面。

现在,这个页面只有一级导航。但是ng-switch指令可以很轻松的嵌套,以便让你的应用程序支持深度链接(deep-linking)。此外,ng-switch-when指令还可以与AngularJS的ng-include指令配合来实现子页面的延迟加载:

<div ng-switch on=" renderPath[ 0 ]">

    <div ng-switch-when="home" ng-include=" 'home.htm' "></div>
    <div ng-switch-when="friends" ng-include=" 'friends.htm' "></div>
    <div ng-switch-when="contact" ng-include=" 'contact.htm' "></div>

</div>

在服务端,事件用于管理资源映射以及存取/转换这些资源。在富客户端应用程序中,事件则扮演了一个截然不同的角色:不再映射到资源,而是映射到页面状态。并且,根据你用户界面的复杂程度,状态还可以被定义为一个复杂的树形分区结构。幸运的是,AngularJS的路由机制使把路由映射到渲染事件成为可能,然后我们就可以通过渲染页面来展示所需的内容了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值