AngularJS 多步表单 - 按 Enter 键应转到下一步并在最后一步提交

AngularJS multistep form - pressing enter key should move to next step and submit on last step

我的多步操作有问题并在输入时提交。我已经按照 this 教程创建了一个多步骤表单。

现在的问题是,当您在第一步中按回车键时,它会提交表单,而这不是本意。

我有一个将军form.html是这样的:

<form id="appointment-form" name="appointmentform" ng-submit="processForm(appointmentform.$valid)">
    <!-- our nested state views will be injected here -->
    <div id="form-views" ui-view></div>
</form>

我也有

表格-license.html(步骤 1)

<!-- form-license.html -->
<label>Nummerplaat ingeven</label>
<div class="form-group">
    <div class="col-xs-8 col-xs-offset-2">
        <input required type="text" class="form-control" name="license" ng-model="formData.license">
    </div>
</div>


<div class="form-group row">
    <div class="col-xs-4 col-xs-offset-4">
        <a ng-click="next(1)" ng-disabled="!licenseValidated" class="btn btn-next btn-block">
            Volgende
        </a>
    </div>
</div>

表格-profile.html(第 2 步)

<div class="profile">
    <div class="form-group">
        <label class="col-sm-3 control-label" for="name">Name</label>
        <div class="col-sm-9">
            <input type="text" class="form-control" name="name" ng-model="formData.profile.name">
        </div>
    </div>

    <div class="form-group row">
        <div class="col-xs-8 col-xs-offset-2">
            <a ng-click="next(1)" ui-sref="form.license" class="btn btn-block btn-previous col-xs-3">
                VORIGE
            </a>
            <a ng-click="next(2)" ng-disabled="!infoValidated" class="btn btn-block btn-next col-xs-3">
                Volgende
            </a>
        </div>
    </div>
</div>

表格-appointment.html(第 3 步)

<div class="appointment">
    <!-- form-payment.html -->
    <div class="text-center">
        <div id="calendar" class="span8 calendar" ui-calendar="uiConfig.calendar" full-calendar  ng-model="eventSources"></div>
        <div id="appointment_hours">
            <div ng-repeat="(key, value) in formData.appointments">
                <input id='<% key %>' type='radio' ng-model='formData.appointment_hour' ng-change="change('<% key %>')" name='appointment' value='<% key %>'>
                <!-- change int to string -->
                <label for='<% key %>'><% key | format_time %></label><br>
            </div>
        </div>



        <div class="form-group row">
            <div class="col-xs-8 col-xs-offset-2">
                <a ng-click="next(2)" ui-sref="form.profile" class="btn btn-block btn-previous col-xs-3">
                    VORIGE
                </a>
                <button type="button" class="btn btn-block btn-next col-xs-3">VOLGENDE</button>
            </div>
        </div>
    </div>
</div>

我的配置和控制器如下所示:

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

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

    $interpolateProvider.startSymbol('<%');
    $interpolateProvider.endSymbol('%>');

    $stateProvider

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

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

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

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

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

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

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

    console.log($scope.agendaValidated);

    $scope.change = function(key){
        // get number of available spots
        var n_appointments = $scope.formData.appointments[key];
        var first_available = n_appointments[0];

        // set column id
        $scope.formData.column_id = first_available.column_id;
    };

    $scope.obj = {
        licenseplate: true
    };

    $scope.calendarData = [];
    $scope.appointments = [];

    var date = new Date();
    var d = date.getDate();
    var m = date.getMonth();
    var y = date.getFullYear();

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

    $scope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
        $('.loader').hide();
    });


    $scope.next = function(step){
        $('.loader').show();
        if(step == 1) // licensePlate
        {
            // receive customer data
            $http.post('/api/licenseplate', { license: $scope.formData.license })
                .success(function(data, status, headers, config){
                    var item = data.item;

                    console.log(item);

                    if(item) // fill in the name, address, email and telephone number
                    {
                         $scope.formData.profile.name = item.driver_name;
                         $scope.formData.profile.street = item.driver_street;
                         $scope.formData.profile.zipcode = item.driver_zipcode;
                         $scope.formData.profile.city = item.driver_city;
                         $scope.formData.profile.email = item.driver_email;
                         $scope.formData.profile.telephone = item.driver_tel_no;
                    }

                    $state.go('form.profile');
                })
                .error(function(data, status, headers, config) {
                    console.log("Data: " + data +
                        "<hr />status: " + status +
                        "<hr />headers: " + headers +
                        "<hr />config: " + config);
                });
        }
        else if(step == 2)
        {
            // save customer data

            // get calendar appointments
            var current_date = (m+1) + '/' + d + '/' + y;

            $http.post('/api/calendar', { date: current_date })
                .success(function(data, status, headers, config){
                    console.log(data);

                    var item = data.items.item;

                    item.sort(function(a, b) {
                        return a.column_id - b.column_id;
                    });

                    $scope.calendarData.pop();
                    $scope.calendarData.push(item);

                    $state.go('form.appointment');

                })
                .error(function(data, status, headers, config) {
                    console.log("Data: " + data +
                        "<hr />status: " + status +
                        "<hr />headers: " + headers +
                        "<hr />config: " + config);
                });

        }
    };

    // function to process the form
    $scope.processForm = function(isValid) {
        $('.loader').show();
        var appointment_date    = $scope.formData.appointment_date;
        var appointment_hour    = $scope.formData.appointment_hour;
        var column_id           = $scope.formData.column_id;
        var profile             = $scope.formData.profile;
        var license             = $scope.formData.license;

        $http.post('/api/agendainsert', { appointment_datetime: appointment_date + appointment_hour, profile: profile, column_id: column_id, license: license })
            .success(function(data, status, headers, config){
                $state.go('form.success');
            })
            .error(function(data, status, headers, config) {
                console.log("Data: " + data +
                    "<hr />status: " + status +
                    "<hr />headers: " + headers +
                    "<hr />config: " + config);
        });
    };

        ///////////////////////////
        // validation functions //
        /////////////////////////

        $scope.error = {};

        // STEP 1
        $scope.$watch('formData.license', function (newVal) {
            $scope.licenseValidated = validateLicense(newVal);
        });

        var validateLicense = function (newVal) {
            // check if valid license
            /*$http.post('/api/licensevalidate', { license: newVal })
                .success(function(data, status, headers, config){
                    if(data == 1)
                    {
                        $scope.licenseValidated = true;
                    }
                })
                .error(function(data, status, headers, config) {
                    console.log("Data: " + data +
                        "<hr />status: " + status +
                        "<hr />headers: " + headers +
                        "<hr />config: " + config);
                });*/
            // If you are only checking for content to be entered
            return (newVal !== '' && newVal !== undefined);
        };

        // STEP 2

        $scope.$watchGroup(['formData.profile.name', 'formData.profile.street', 'formData.profile.zipcode', 'formData.profile.city', 'formData.profile.email', 'formData.profile.telephone'], function (newVal) {
            $scope.infoValidated = validateInfo(newVal);
        });

        var validateInfo = function (newVal) {
            if (newVal.length > 0) {
                for (var i = 0, l = newVal.length; i < l; i++) {
                    if (newVal[i] === undefined || newVal[i] === '') {
                        return false;
                    }
                }
                return true;
            }
            return false;
        };
    });

问题是当您在第一步或第二步点击回车键时,它提交了表单,但数据没有正确发布。它调用函数 processForm。但是我只想在最后一步调用函数processForm。

我怎样才能使这个工作,当你在第一步点击回车键时,它会进入第二步?我已经尝试禁用回车按钮,但它无法使用指令。

您需要创建一个指令来监听表单上的 keydown 事件,并在按下的键为 enter keyCode == 13

时阻止默认操作

这是一个DEMO

包括jquery

创建指令 disable-enter-submit 并将其附加到表单

<form id="appointment-form" name="appointmentform" ng-submit="processForm()" disable-enter-submit>

执行指令

app.directive('disableEnterSubmit', function() {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {

            // listen to the keydown event on the form and if the pressed ..
            //..key is enter then prevent the default action/s which is submitting the form

            element.bind("keydown", function(event) {
                if(event.keyCode == 13) {
                  event.preventDefault();

                  return false;
                }

             });
        }
     }
});

这里稍微改进一下DEMO

-------------------------更新-------- ------------------

如果按enter键需要进入下一步

在控制器中创建变量

var total_steps = 2; // total steps of the form
var current_step = 1; // active step

$scope.next = function(step) {

    // if current step is equals to total steps then submit the form
    if(current_step == total_steps) {
      $scope.processForm();
      return;
    }

    // if step is defined then assign the current_step to step
    // else increment the current_step by 1
    if (!angular.isUndefined(step)) {
      current_step = step;
    } else {
      current_step++;
    }

    // load the proper template based on step
    $scope.form_fields = 'step' + current_step + '.html';
};

在指令中

element.bind("keydown", function(event) {
    if (event.keyCode == 13) {

      // if press enter then call the next() function on the scope
      // we need to trigger the digest cycle manually
      // because `element.bind("keydown"..` happens out of angular knowledge.
      // so i wrapped it in a `$timeout` or you can use `scope.$apply();` after the `scope.next();`
      $timeout(function() {
        scope.next();
      });

      event.preventDefault();
      return false;
    }
 });

这里是DEMO