AngularJS 进阶应用:企业级场景与综合解决方案

在掌握 AngularJS 进阶开发技术(自定义指令、拦截器、路由)后,进阶应用的核心是 “将技术融入实际业务,解决企业级复杂需求”—— 比如处理多步骤表单、实现可编辑列表、解决跨组件通信、优化大数据渲染性能等。本文围绕企业项目高频核心场景,提供 “需求拆解 - 技术选型 - 完整实现” 的全流程方案,帮你具备独立开发中大型 AngularJS 应用的能力。

一、场景一:企业级多步骤表单(分步提交 + 数据回显)

后台管理系统中,“多步骤表单”(如用户注册、商品发布)是常见需求,需实现 “步骤导航、数据暂存、校验联动、提交回显” 功能。结合 AngularJS 的表单验证、服务封装、本地存储技术,可构建稳定易用的多步骤表单方案。

1. 需求拆解

分 3 步完成 “商品发布”:基础信息(名称、分类)→ 规格配置(价格、库存)→ 内容设置(描述、图片);

每步表单单独校验,校验通过才能进入下一步;

支持 “上一步 / 下一步” 导航,暂存每步数据(刷新页面不丢失);

最终提交时,整合所有步骤数据,提交后清空暂存并重置表单。

2. 技术选型

表单校验:AngularJS 内置表单验证(ng-required/ng-pattern)+ 自定义指令(如规格动态字段);

数据暂存:localStorage存储每步数据,服务封装存储逻辑;

步骤管理:控制器维护步骤状态,通过ng-show/ng-hide控制步骤切换;

数据提交:拦截器统一处理请求,服务封装提交 API。

3. 完整实现代码

<!DOCTYPE html>

<html lang="zh-CN">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>AngularJS进阶应用 - 多步骤商品发布表单</title>

    <script src="https://apps.bdimg.com/libs/angular.js/1.8.2/angular.min.js"></script>

    <style>

        .form-container {

            width: 800px;

            margin: 50px auto;

            padding: 20px;

            border: 1px solid #eee;

            border-radius: 8px;

        }

        .step-nav {

            display: flex;

            margin-bottom: 30px;

            position: relative;

        }

        .step-nav::before {

            content: '';

            position: absolute;

            top: 15px;

            left: 0;

            width: 100%;

            height: 2px;

            background: #eee;

            z-index: 1;

        }

        .step-item {

            flex: 1;

            text-align: center;

            position: relative;

            z-index: 2;

        }

        .step-icon {

            width: 32px;

            height: 32px;

            line-height: 32px;

            border-radius: 50%;

            background: #eee;

            color: #999;

            margin: 0 auto 8px;

            font-weight: bold;

        }

        .step-item.active .step-icon {

            background: #4285f4;

            color: white;

        }

        .step-item.completed .step-icon {

            background: #43a047;

            color: white;

        }

        .step-item.completed .step-text {

            color: #43a047;

        }

        .step-content {

            margin-bottom: 30px;

        }

        .form-group {

            margin-bottom: 20px;

        }

        .form-group label {

            display: block;

            margin-bottom: 8px;

            color: #666;

        }

        .form-control {

            width: 100%;

            padding: 10px;

            border: 1px solid #ddd;

            border-radius: 4px;

            box-sizing: border-box;

        }

        .form-control.ng-valid.ng-dirty {

            border-color: #43a047;

        }

        .form-control.ng-invalid.ng-dirty {

            border-color: #e53935;

        }

        .error-message {

            color: #e53935;

            font-size: 12px;

            margin-top: 5px;

            height: 16px;

        }

        .spec-group {

            display: flex;

            gap: 10px;

            margin-bottom: 10px;

            padding: 10px;

            background: #f9fafb;

            border-radius: 4px;

        }

        .spec-group input {

            flex: 1;

        }

        .btn {

            padding: 10px 20px;

            border: none;

            border-radius: 4px;

            cursor: pointer;

            font-size: 14px;

        }

        .btn-prev {

            background: #f5f5f5;

            color: #333;

            margin-right: 10px;

        }

        .btn-next, .btn-submit {

            background: #4285f4;

            color: white;

        }

        .btn:disabled {

            background: #90caf9;

            cursor: not-allowed;

        }

        .submit-success {

            padding: 20px;

            background: #e8f5e9;

            color: #2e7d32;

            border-radius: 4px;

            text-align: center;

            margin-top: 20px;

        }

    </style>

</head>

<body ng-app="multiStepFormApp">

    <div class="form-container" ng-controller="ProductFormCtrl">

        <!-- 步骤导航 -->

        <div class="step-nav">

            <div class="step-item" ng-class="{ active: currentStep === 1, completed: currentStep > 1 }">

                <div class="step-icon">1</div>

                <div class="step-text">基础信息</div>

            </div>

            <div class="step-item" ng-class="{ active: currentStep === 2, completed: currentStep > 2 }">

                <div class="step-icon">2</div>

                <div class="step-text">规格配置</div>

            </div>

            <div class="step-item" ng-class="{ active: currentStep === 3, completed: currentStep > 3 }">

                <div class="step-icon">3</div>

                <div class="step-text">内容设置</div>

            </div>

        </div>

        <!-- 步骤内容:基础信息 -->

        <div class="step-content" ng-show="currentStep === 1">

            <form name="basicForm" novalidate>

                <div class="form-group">

                    <label for="productName">商品名称 *</label>

<"zhiq.zhaopin.com/question/11229716">

<"zhiq.zhaopin.com/question/11229718">

<"zhiq.zhaopin.com/question/11229720">

<"zhiq.zhaopin.com/question/11229723">

<"zhiq.zhaopin.com/question/11229726">

<"zhiq.zhaopin.com/question/11229727">

<"zhiq.zhaopin.com/question/11229728">

<"zhiq.zhaopin.com/question/11229742">

<"zhiq.zhaopin.com/question/11229779">

<"zhiq.zhaopin.com/question/11229840">

<"zhiq.zhaopin.com/question/11229876">

<"zhiq.zhaopin.com/question/11229893">

<"zhiq.zhaopin.com/question/11229927">

<"zhiq.zhaopin.com/question/11229944">

<"zhiq.zhaopin.com/question/11229958">

<"zhiq.zhaopin.com/question/11229966">

<"zhiq.zhaopin.com/question/11229969">

<"zhiq.zhaopin.com/question/11229973">

<"zhiq.zhaopin.com/question/11229980">

<"zhiq.zhaopin.com/question/11229981">

                    <input type="text" id="productName" name="productName" class="form-control"

                          ng-model="formData.basic.name" ng-required="true" ng-maxlength="50">

                    <div class="error-message">

                        <span ng-show="basicForm.productName.$dirty && basicForm.productName.$error.required">商品名称不能为空</span>

                        <span ng-show="basicForm.productName.$dirty && basicForm.productName.$error.maxlength">商品名称最多50个字符</span>

                    </div>

                </div>

                <div class="form-group">

                    <label for="productCategory">商品分类 *</label>

                    <select id="productCategory" name="productCategory" class="form-control"

                            ng-model="formData.basic.categoryId" ng-required="true">

                        <option value="">请选择分类</option>

                        <option value="1">服装</option>

                        <option value="2">电子产品</option>

                        <option value="3">食品</option>

                    </select>

                    <div class="error-message">

                        <span ng-show="basicForm.productCategory.$dirty && basicForm.productCategory.$error.required">请选择商品分类</span>

                    </div>

                </div>

            </form>

        </div>

        <!-- 步骤内容:规格配置(动态字段) -->

        <div class="step-content" ng-show="currentStep === 2">

            <form name="specForm" novalidate>

                <div class="form-group">

                    <label>规格配置 *</label>

                    <div id="specGroups" ng-repeat="spec in formData.specs">

                        <div class="spec-group">

                            <input type="text" class="form-control" placeholder="规格名称(如红色-XL)"

                                  ng-model="spec.name" ng-required="true">

                            <input type="number" class="form-control" placeholder="单价"

                                  ng-model="spec.price" ng-required="true" min="0" step="0.01">

                            <input type="number" class="form-control" placeholder="库存"

                                  ng-model="spec.stock" ng-required="true" min="0">

                        </div>

                    </div>

                    <button type="button" class="btn btn-prev" ng-click="addSpec()">+ 添加规格</button>

                    <div class="error-message" ng-show="specForm.$submitted || isSpecCheck">

                        <span ng-if="formData.specs.length === 0">至少添加1组规格</span>

                        <span ng-if="formData.specs.length > 0 && !isAllSpecValid()">请完善所有规格的必填项</span>

                    </div>

                </div>

            </form>

        </div>

        <!-- 步骤内容:内容设置 -->

        <div class="step-content" ng-show="currentStep === 3">

            <form name="contentForm" novalidate>

                <div class="form-group">

                    <label for="productDesc">商品描述 *</label>

                    <textarea id="productDesc" name="productDesc" class="form-control" rows="5"

                              ng-model="formData.content.desc" ng-required="true" ng-minlength="20"></textarea>

                    <div class="error-message">

                        <span ng-show="contentForm.productDesc.$dirty && contentForm.productDesc.$error.required">商品描述不能为空</span>

                        <span ng-show="contentForm.productDesc.$dirty && contentForm.productDesc.$error.minlength">商品描述至少20个字符</span>

                    </div>

                </div>

                <div class="form-group">

                    <label for="productImage">商品主图 *</label>

                    <input type="text" id="productImage" name="productImage" class="form-control"

                          ng-model="formData.content.imageUrl" ng-required="true" ng-pattern="/^(http|https):\/\/.+\.(jpg|png|jpeg)$/">

                    <div class="error-message">

                        <span ng-show="contentForm.productImage.$dirty && contentForm.productImage.$error.required">请输入商品主图URL</span>

                        <span ng-show="contentForm.productImage.$dirty && contentForm.productImage.$error.pattern">请输入合法的图片URL(http/https开头,jpg/png格式)</span>

                    </div>

                </div>

            </form>

        </div>

        <!-- 操作按钮 -->

        <div class="form-actions" ng-hide="submitSuccess">

            <button type="button" class="btn btn-prev" ng-click="goToPrevStep()" ng-disabled="currentStep === 1">上一步</button>

            <button type="button" class="btn btn-next" ng-click="goToNextStep()" ng-disabled="currentStep === 3">下一步</button>

            <button type="button" class="btn btn-submit" ng-show="currentStep === 3" ng-click="submitForm()">提交商品</button>

        </div>

        <!-- 提交成功提示 -->

        <div class="submit-success" ng-show="submitSuccess">

            <h3>商品发布成功!</h3>

            <p>商品名称:{{ formData.basic.name }}</p>

            <p>规格数量:{{ formData.specs.length }} 组</p>

            <button type="button" class="btn btn-prev" ng-click="resetForm()">重新发布</button>

        </div>

    </div>

    <script>

        // 1. 创建应用模块

        const multiStepFormApp = angular.module('multiStepFormApp', []);

        // 2. 封装本地存储服务:暂存表单数据

        multiStepFormApp.service('StorageService', function() {

            const STORAGE_KEY = 'productFormData';

            // 保存数据到localStorage

            this.save = function(data) {

                localStorage.setItem(STORAGE_KEY, JSON.stringify(data));

            };

            // 从localStorage获取数据

            this.get = function() {

                const data = localStorage.getItem(STORAGE_KEY);

                return data ? JSON.parse(data) : null;

            };

            // 清空数据

            this.clear = function() {

                localStorage.removeItem(STORAGE_KEY);

            };

        });

        // 3. 控制器:多步骤表单逻辑

        multiStepFormApp.controller('ProductFormCtrl', function($scope, StorageService) {

            // 初始化表单数据(优先从本地存储获取,无则初始化)

            const initFormData = function() {

                const savedData = StorageService.get();

                if (savedData) {

                    $scope.formData = savedData;

                } else {

                    $scope.formData = {

                        basic: { name: '', categoryId: '' }, // 基础信息

                        specs: [{ name: '', price: '', stock: '' }], // 规格配置(默认1组)

                        content: { desc: '', imageUrl: '' } // 内容设置

                    };

                }

            };

            // 初始化步骤状态

            $scope.currentStep = 1; // 当前步骤(1-3)

            $scope.isSpecCheck = false; // 规格校验标记

            $scope.submitSuccess = false; // 提交成功标记

            // 初始化

            initFormData();

            // 添加规格(动态字段)

            $scope.addSpec = function() {

                $scope.formData.specs.push({ name: '', price: '', stock: '' });

                // 保存数据到本地存储

                StorageService.save($scope.formData);

            };

            // 校验当前步骤表单

            $scope.validateCurrentStep = function() {

                let isValid = true;

                switch ($scope.currentStep) {

                    case 1:

                        // 基础信息表单校验

                        $scope.basicForm.$submitted = true;

                        isValid = $scope.basicForm.$valid;

                        break;

                    case 2:

                        // 规格表单校验(动态字段需手动校验)

                        $scope.isSpecCheck = true;

                        isValid = $scope.formData.specs.length > 0 && $scope.isAllSpecValid();

                        break;

                    case 3:

                        // 内容设置表单校验

                        $scope.contentForm.$submitted = true;

                        isValid = $scope.contentForm.$valid;

                        break;

                }

                return isValid;

            };

            // 校验所有规格是否填写完整(动态字段)

            $scope.isAllSpecValid = function() {

                return $scope.formData.specs.every(spec => {

                    return spec.name.trim() && !isNaN(spec.price) && spec.price >= 0 && !isNaN(spec.stock) && spec.stock >= 0;

                });

            };

            // 下一步

            $scope.goToNextStep = function() {

                if ($scope.validateCurrentStep()) {

                    // 保存当前步骤数据到本地存储

                    StorageService.save($scope.formData);

                    $scope.currentStep++;

                    // 重置当前步骤的校验标记

                    $scope.basicForm.$submitted = false;

                    $scope.isSpecCheck = false;

                    $scope.contentForm.$submitted = false;

                }

            };

            // 上一步

            $scope.goToPrevStep = function() {

                $scope.currentStep--;

                // 重置当前步骤的校验标记

                $scope.basicForm.$submitted = false;

                $scope.isSpecCheck = false;

                $scope.contentForm.$submitted = false;

            };

            // 提交表单

            $scope.submitForm = function() {

                if ($</doubaocanvas>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容