hjzheng / CUF_meeting_knowledge_share

Record CUF team meeting knowledge share
121 stars 49 forks source link

2015-7-10 使用UI Router实现AngularJS 多步骤表单(渣译) #37

Open hjzheng opened 9 years ago

hjzheng commented 9 years ago

使用UI Router实现AngularJS 多步骤表单(渣译)

原文地址: https://scotch.io/tutorials/angularjs-multi-step-form-using-ui-router

今天,我们将使用 AngularJS,UIRouter 和 AngularJS ngAnimate 模块去创建一个动态的多步骤表单。这项技术可以被用于大型表单。我们也可以看到该技术被用于 Web 上的很多地方。像购物车,注册表单,入职流程,等已经通过使用多步骤表单形式提高用户舒适度的在线表单。下面就是我们所要创建的:

qq 20150710172814

UI Router 具有的如下功能:

嵌套状态 ( nested states ) 并且每个状态对应不同的视图的功能, 利用这些功能,我们能很容易的制作一个多步骤表单。为了快速理解它的好处和 UI Router 是如何工作,请查阅我们的其它文章: AngularJS Routing Using UI-Router 。让我们言归正传,开始创建我们精彩的表单!

建立工程

我们将为应用使用简单的结构,需要一个 layout 文件,表单每一部分的视图文件,一个样式表和保存所有 Angular 代码的 JavaScript 文件。

下面是我们的应用程序的文件。创建这些文件,我们将开始填写他们。

- index.html
- form.html
- form-profile.html
- form-interests.html
- form-payment.html
- app.js
- style.css

每个 form-____.html 的文件将会被作为嵌套视图用于表单中,这些是将要创建的表单的每一部分。

Layout/Template 文件 (index.html)

让我们开始工程,使用 index.html 作为 main 文件,在 index.html 导入所有的资源( AngularJS, ngAnimate, UI Router, JavaScript 脚本和样式 )并放置 ui-view 告诉 UI Router 在哪里插入视图,我们也会使用 Bootstrap 快速设置样式。

<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">

    <!-- CSS -->
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootswatch/3.1.1/darkly/bootstrap.min.css">
    <link rel="stylesheet" href="style.css">

    <!-- JS -->
    <!-- load angular, nganimate, and ui-router -->
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.10/angular-ui-router.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular-animate.min.js"></script>
    <script src="app.js"></script>

</head>

<!-- apply our angular app -->
<body ng-app="formApp">

    <div class="container">

        <!-- views will be injected here -->
        <div ui-view></div>

    </div>

</body>
</html>

随着所有的文件装载完毕,让我们进入 app.js , 开始创建我们的 Angular 应用和基于状态的路由。注意 formApp 是如何应用到 body 上的。

构建 Angular 应用 ( app.js )

我们将会创建我们的应用和路由。对大型应用,你会将 angular 应用,路由和控制器分开放入它们各自单独的模块中,但是对于我们的目标,我们会将它们放在一块。

``` app.js

// app.js
// create our angular app and inject ngAnimate and ui-router 
// =============================================================================
angular.module('formApp', ['ngAnimate', 'ui.router'])

// configuring our routes 
// =============================================================================
.config(function($stateProvider, $urlRouterProvider) {

    $stateProvider

        // route to show our basic form (/form)
        .state('form', {
            url: '/form',
            templateUrl: 'form.html',
            controller: 'formController'
        })

        // nested states 
        // each of these sections will have their own view
        // url will be nested (/form/profile)
        .state('form.profile', {
            url: '/profile',
            templateUrl: 'form-profile.html'
        })

        // url will be /form/interests
        .state('form.interests', {
            url: '/interests',
            templateUrl: 'form-interests.html'
        })

        // url will be /form/payment
        .state('form.payment', {
            url: '/payment',
            templateUrl: 'form-payment.html'
        });

    // catch all route
    // send users to the form page 
    $urlRouterProvider.otherwise('/form/profile');
})

// our controller for the form
// =============================================================================
.controller('formController', function($scope) {

    // we will store all of our form data in this object
    $scope.formData = {};

    // function to process the form
    $scope.processForm = function() {
        alert('awesome!');
    };

});

现在,我们拥有了依赖注入 ngAnimate 和 ui.router 的 Angular 模块,也有已经创建好的路由。注意,在这里我们如何为每个状态定义 url ,视图文件( templateUrl )和控制器。 form 将会作为主状态,它可以通过 . 表示的子状态,例如 在 form.profile 中的 . 。这背后的想法是,每个嵌套状态将被纳入主表单视图( main form view ),作为应用程序改变的状态。我们会在下一个章节证明这一点。我们现在要为表单和它的每一个嵌套状态创建视图。

表单模板视图 ( form.html )

让我们开始创建 form.html 文件。它将会被作为剩下的表单视图文件的模板文件,就像 index.html 被用作整个工程的总模板一样。所有我们需要做的是将 ui-view 包含在这个文件中,使嵌套状态知道从哪里注入它们的视图。

<!-- form.html -->
<div class="row">
<div class="col-sm-8 col-sm-offset-2">

    <div id="form-container">

        <div class="page-header text-center">
            <h2>Let's Be Friends</h2>

            <!-- the links to our nested states using relative paths -->
            <!-- add the active class if the state matches our ui-sref -->
            <div id="status-buttons" class="text-center">
                <a ui-sref-active="active" ui-sref=".profile"><span>1</span> Profile</a>
                <a ui-sref-active="active" ui-sref=".interests"><span>2</span> Interests</a>
                <a ui-sref-active="active" ui-sref=".payment"><span>3</span> Payment</a>
            </div>
        </div>

        <!-- use ng-submit to catch the form submission and use our Angular function -->
        <form id="signup-form" ng-submit="processForm()">

            <!-- our nested state views will be injected here -->
            <div id="form-views" ui-view></div>
        </form>

    </div>

    <!-- show our formData as it is being typed -->
    <pre>
        {{ formData }}
    </pre>

</div>
</div>

注意,这是我们第二次在工程中使用 ui-view 了,这也正是 UI Router 为何如此强大;允许嵌套状态和视图。这为我们创建应用提供了很大的灵活性。更多关于 UI Router 视图的信息,可以查阅它的官方文档。添加基于状态 Active 类,我们希望每一个状态按钮在它是活动的时候能够显示出来。为了做到这一点,我们使用 UI Router 提供的 ui-sref-active 的指令。当 ui-sref 匹配当前的状态时,这样就会添加我们指定的类。为你的表单添加校验,你可以阅读 AngularJS Form Validation 这时候,你可能想知道我们表单是看起来是什么样子,让我们进入浏览器来看一看!

qq 20150710172909

到达这里,不是所有的事情都是我们希望的那样,但是已经有伟大的事情开始了。让我们继续推进,添加一些样式,然后再添加嵌套视图和动画。

基本的样式 (style.css)

为了得到一个好看的表单,我们设置 form-container 和 status-buttons 样式。

/* style.css */
/* BASIC STYLINGS
============================================================================= */
body                            { padding-top:20px; }

/* form styling */
#form-container                { background:#2f2f2f; margin-bottom:20px;
    border-radius:5px; }
#form-container .page-header   { background:#151515; margin:0; padding:30px; 
    border-top-left-radius:5px; border-top-right-radius:5px; }

/* numbered buttons */
#status-buttons                 {  }
#status-buttons a               { color:#FFF; display:inline-block; font-size:12px; margin-right:10px; text-align:center; text-transform:uppercase; }
#status-buttons a:hover         { text-decoration:none; }

/* we will style the span as the circled number */
#status-buttons span            { background:#080808; display:block; height:30px; margin:0 auto 10px; padding-top:5px; width:30px; 
    border-radius:50%; }

/* active buttons turn light green-blue*/
#status-buttons a.active span   { background:#00BC8C; }

现在,按钮看起来好看多了,让我们继续嵌套视图(nested views)。

嵌套表单视图 form-profile.html, form-interests.html, form-payment.html

这会是容易的部分。我们定义不同的带有输入框的视图。我们把它们和 formData 对象绑定,使我们可以看到表单中输入的数据。这里有我们的视图文件:

Form Profile View

<!-- form-profile.html -->
<div class="form-group">
    <label for="name">Name</label>
    <input type="text" class="form-control" name="name" ng-model="formData.name">
</div>

<div class="form-group">
    <label for="email">Email</label>
    <input type="text" class="form-control" name="email" ng-model="formData.email">
</div>

<div class="form-group row">
<div class="col-xs-6 col-xs-offset-3">
    <a ui-sref="form.interests" class="btn btn-block btn-info">
    Next Section <span class="glyphicon glyphicon-circle-arrow-right"></span>
    </a>
</div>
</div>

Form Interests View

<!-- form-interests.html -->
<label>What's Your Console of Choice?</label>
<div class="form-group">
    <div class="radio">
        <label>
           <input type="radio" ng-model="formData.type" value="xbox" checked>
           I like XBOX
        </label>
    </div>
    <div class="radio">
        <label>
            <input type="radio" ng-model="formData.type" value="ps">
            I like PS4
        </label>
    </div>
</div>

<div class="form-group row">
<div class="col-xs-6 col-xs-offset-3">
    <a ui-sref="form.payment" class="btn btn-block btn-info">
    Next Section <span class="glyphicon glyphicon-circle-arrow-right"></span>
    </a>
</div>
</div>

Form Payment View

<!-- form-payment.html -->
<div class="text-center">
    <span class="glyphicon glyphicon-heart"></span>
    <h3>Thanks For Your Money!</h3>

    <button type="submit" class="btn btn-danger">Submit</button>
</div>

那么现在,我们拥有三个定义好的视图,当我们查看表单时,它们会被展示。我们也使用 next button 和 ui-sref 连接到每个新状态。

当使用 ui-sref 时,你想连接已经在路由中定义过的状态,而不是 URL。Angular 会使用 ui-sref 为你创建 href 。

qq 20150710172647

为 form 添加动画

从一开始,ngAnimate 就已经进入到我们的工程,当我们改变状态,已经添加用于动画的类。它会自动添加类 ng-enter 和 ng-leave 用于 view 显示和消失。现在我们所要做的就是设置那些样式,我们有我们的完成表单!要理解 Angular 动画,这篇文章是一个很好的起点。让我们进入 CSS 文件,添加动画,并把它们应用到我们的表单。

/* style.css */
/* ANIMATION STYLINGS
============================================================================= */
#signup-form            { position:relative; min-height:300px; overflow:hidden; padding:30px; }
#form-views             { width:auto; }

/* basic styling for entering and leaving */
/* left and right added to ensure full width */
#form-views.ng-enter,
#form-views.ng-leave      { position:absolute; left:30px; right:30px;
    transition:0.5s all ease; -moz-transition:0.5s all ease; -webkit-transition:0.5s all ease; 
}

/* enter animation */
#form-views.ng-enter            { 
    -webkit-animation:slideInRight 0.5s both ease;
    -moz-animation:slideInRight 0.5s both ease;
    animation:slideInRight 0.5s both ease; 
}

/* leave animation */
#form-views.ng-leave            { 
    -webkit-animation:slideOutLeft 0.5s both ease;
    -moz-animation:slideOutLeft 0.5s both ease;
    animation:slideOutLeft 0.5s both ease;   
}

/* ANIMATIONS
============================================================================= */
/* slide out to the left */
@keyframes slideOutLeft {
    to      { transform: translateX(-200%); }
}
@-moz-keyframes slideOutLeft {  
    to      { -moz-transform: translateX(-200%); }
}
@-webkit-keyframes slideOutLeft {
    to      { -webkit-transform: translateX(-200%); }
}

/* slide in from the right */
@keyframes slideInRight {
    from    { transform:translateX(200%); }
    to      { transform: translateX(0); }
}
@-moz-keyframes slideInRight {
    from    { -moz-transform:translateX(200%); }
    to      { -moz-transform: translateX(0); }
}
@-webkit-keyframes slideInRight {
    from    { -webkit-transform:translateX(200%); }
    to      { -webkit-transform: translateX(0); }
}

首先,我们设置表单样式,当一个新的视图离开或进入的时候,它们是绝对定位的。这样可以确保当进入的时候,一个视图不会推挤其他视图。其次,我们在 .ng-enter 和 .ng-leave 类里应用动画。再次,我们使用 @keyframes 定义动画。随着所有部分一起工作起来,我们全部表单都具有了 Angular 动画。UI Router based states, and Angular data-binding.

结论

UI-Router,ngAnimate,和所有在 Angular 中有趣的东西能走到一起去创造伟大的应用。希望这篇文章向您展示如何制作一个多步骤的表单,并使用多种 Angular 的技术和工具来构建它。这些概念已经被应用到许多其他的用户界面。感谢您的阅读,大胆的说出你建议和提出问题。

演示:http://embed.plnkr.co/M03tYgtfqNH09U4x5pHC/preview 代码:http://plnkr.co/edit/M03tYgtfqNH09U4x5pHC?p=preview