在掌握AngularJS核心概念(模塊、控制器、指令)后,入門應(yīng)用的關(guān)鍵是“解決實際業(yè)務(wù)場景”——比如用戶注冊表單、數(shù)據(jù)列表加載、本地數(shù)據(jù)持久化等。本文以“高頻應(yīng)用場景”為核心,通過三個完整應(yīng)用案例,帶你掌握AngularJS在實際開發(fā)中的應(yīng)用方法,同時梳理應(yīng)用開發(fā)中的通用思路與避坑技巧。
一、應(yīng)用場景1:用戶注冊表單(表單驗證+數(shù)據(jù)提交)
表單是前端與用戶交互的核心載體,AngularJS提供了完善的內(nèi)置表單驗證能力,無需大量手動JS代碼,即可實現(xiàn)“必填項校驗、格式校驗、實時提示”等功能。以下以“用戶注冊表單”為例,落地表單應(yīng)用開發(fā)。
1. 需求拆解
實現(xiàn)用戶名、手機號、密碼、確認(rèn)密碼的輸入校驗;
實時顯示錯誤提示(如“用戶名不能為空”“手機號格式錯誤”);
所有校驗通過后,才能提交表單;
提交時顯示加載狀態(tài),避免重復(fù)提交。
2. 完整應(yīng)用代碼
<!DOCTYPE html>
<html lang="zh-CN">
<head>
? ? <meta charset="UTF-8">
? ? <meta name="viewport" content="width=device-width, initial-scale=1.0">
? ? <title>AngularJS入門應(yīng)用 - 用戶注冊表單</title>
? ? <script src="https://apps.bdimg.com/libs/angular.js/1.8.2/angular.min.js"></script>
? ? <style>
? ? ? ? .form-container {
? ? ? ? ? ? width: 450px;
? ? ? ? ? ? margin: 50px auto;
? ? ? ? ? ? padding: 25px;
? ? ? ? ? ? border: 1px solid #eee;
? ? ? ? ? ? border-radius: 8px;
? ? ? ? ? ? box-shadow: 0 2px 10px rgba(0,0,0,0.05);
? ? ? ? }
? ? ? ? .form-title {
? ? ? ? ? ? margin-top: 0;
? ? ? ? ? ? margin-bottom: 20px;
? ? ? ? ? ? color: #333;
? ? ? ? ? ? text-align: center;
? ? ? ? }
? ? ? ? .form-group {
? ? ? ? ? ? margin-bottom: 20px;
? ? ? ? }
? ? ? ? .form-group label {
? ? ? ? ? ? display: block;
? ? ? ? ? ? margin-bottom: 8px;
? ? ? ? ? ? color: #666;
? ? ? ? ? ? font-size: 14px;
? ? ? ? }
? ? ? ? .form-control {
? ? ? ? ? ? width: 100%;
? ? ? ? ? ? padding: 10px;
? ? ? ? ? ? border: 1px solid #ddd;
? ? ? ? ? ? border-radius: 4px;
? ? ? ? ? ? font-size: 14px;
? ? ? ? ? ? box-sizing: border-box;
? ? ? ? }
? ? ? ? /* 輸入框校驗狀態(tài)樣式:成功(綠色邊框)、錯誤(紅色邊框) */
? ? ? ? .form-control.ng-valid.ng-dirty {
? ? ? ? ? ? border-color: #43a047;
? ? ? ? }
? ? ? ? .form-control.ng-invalid.ng-dirty {
? ? ? ? ? ? border-color: #e53935;
? ? ? ? }
? ? ? ? .error-message {
? ? ? ? ? ? margin-top: 5px;
? ? ? ? ? ? color: #e53935;
? ? ? ? ? ? font-size: 12px;
? ? ? ? ? ? height: 16px; /* 固定高度,避免提示顯示/隱藏時表單跳動 */
? ? ? ? }
? ? ? ? .submit-btn {
? ? ? ? ? ? width: 100%;
? ? ? ? ? ? padding: 12px;
? ? ? ? ? ? border: none;
? ? ? ? ? ? background: #4285f4;
? ? ? ? ? ? color: white;
? ? ? ? ? ? border-radius: 4px;
? ? ? ? ? ? font-size: 16px;
? ? ? ? ? ? cursor: pointer;
? ? ? ? ? ? transition: background 0.3s ease;
? ? ? ? }
? ? ? ? .submit-btn:disabled {
? ? ? ? ? ? background: #90caf9;
? ? ? ? ? ? cursor: not-allowed;
? ? ? ? }
? ? ? ? .submit-success {
? ? ? ? ? ? margin-top: 15px;
? ? ? ? ? ? padding: 10px;
? ? ? ? ? ? background: #e8f5e9;
? ? ? ? ? ? color: #2e7d32;
? ? ? ? ? ? border-radius: 4px;
? ? ? ? ? ? text-align: center;
? ? ? ? ? ? display: none;
? ? ? ? }
? ? </style>
</head>
<!-- 掛載AngularJS模塊 -->
<body ng-app="registerApp">
? ? <div class="form-container" ng-controller="registerCtrl">
? ? ? ? <h3 class="form-title">用戶注冊</h3>
? ? ? ? <!-- 表單:ng-submit綁定提交事件,novalidate禁用瀏覽器默認(rèn)驗證 -->
? ? ? ? <form name="registerForm" ng-submit="submitForm()" novalidate>
? ? ? ? ? ? <!-- 用戶名輸入框 -->
? ? ? ? ? ? <div class="form-group">
? ? ? ? ? ? ? ? <label for="username">用戶名 *</label>
? ? ? ? ? ? ? ? <input type="text"
? ? ? ? ? ? ? ? ? ? ? id="username"
? ? ? ? ? ? ? ? ? ? ? name="username"
? ? ? ? ? ? ? ? ? ? ? class="form-control"
? ? ? ? ? ? ? ? ? ? ? ng-model="user.username"
? ? ? ? ? ? ? ? ? ? ? ng-required="true" <!-- 必填校驗 -->
? ? ? ? ? ? ? ? ? ? ? ng-minlength="3" <!-- 最小長度3 -->
? ? ? ? ? ? ? ? ? ? ? ng-maxlength="16"> <!-- 最大長度16 -->
? ? ? ? ? ? ? ? <!-- 錯誤提示:根據(jù)不同校驗結(jié)果顯示不同信息 -->
? ? ? ? ? ? ? ? <div class="error-message">
? ? ? ? ? ? ? ? ? ? <span ng-show="registerForm.username.$dirty && registerForm.username.$error.required">用戶名不能為空</span>
? ? ? ? ? ? ? ? ? ? <span ng-show="registerForm.username.$dirty && registerForm.username.$error.minlength">用戶名至少3個字符</span>
? ? ? ? ? ? ? ? ? ? <span ng-show="registerForm.username.$dirty && registerForm.username.$error.maxlength">用戶名最多16個字符</span>
? ? ? ? ? ? ? ? </div>
? ? ? ? ? ? </div>
? ? ? ? ? ? <!-- 手機號輸入框 -->
? ? ? ? ? ? <div class="form-group">
? ? ? ? ? ? ? ? <label for="phone">手機號 *</label>
zhiq.zhaopin.com/moment/86391611
zhiq.zhaopin.com/moment/86391621
zhiq.zhaopin.com/moment/86391629
zhiq.zhaopin.com/moment/86391638
zhiq.zhaopin.com/moment/86391675
zhiq.zhaopin.com/moment/86391696
zhiq.zhaopin.com/moment/86391700
zhiq.zhaopin.com/moment/86391702
zhiq.zhaopin.com/moment/86417690
zhiq.zhaopin.com/moment/86417943
zhiq.zhaopin.com/moment/86418006
zhiq.zhaopin.com/moment/86418083
zhiq.zhaopin.com/moment/86418152
zhiq.zhaopin.com/moment/86418206
zhiq.zhaopin.com/moment/86418366
zhiq.zhaopin.com/moment/86418682
zhiq.zhaopin.com/moment/86419393
zhiq.zhaopin.com/moment/86419459
zhiq.zhaopin.com/moment/86419610
zhiq.zhaopin.com/moment/86419708
? ? ? ? ? ? ? ? <input type="tel"
? ? ? ? ? ? ? ? ? ? ? id="phone"
? ? ? ? ? ? ? ? ? ? ? name="phone"
? ? ? ? ? ? ? ? ? ? ? class="form-control"
? ? ? ? ? ? ? ? ? ? ? ng-model="user.phone"
? ? ? ? ? ? ? ? ? ? ? ng-required="true"
? ? ? ? ? ? ? ? ? ? ? ng-pattern="/^1[3-9]\d{9}$/"> <!-- 手機號格式正則校驗 -->
? ? ? ? ? ? ? ? <div class="error-message">
? ? ? ? ? ? ? ? ? ? <span ng-show="registerForm.phone.$dirty && registerForm.phone.$error.required">手機號不能為空</span>
? ? ? ? ? ? ? ? ? ? <span ng-show="registerForm.phone.$dirty && registerForm.phone.$error.pattern">請輸入正確的11位手機號</span>
? ? ? ? ? ? ? ? </div>
? ? ? ? ? ? </div>
? ? ? ? ? ? <!-- 密碼輸入框 -->
? ? ? ? ? ? <div class="form-group">
? ? ? ? ? ? ? ? <label for="password">密碼 *</label>
? ? ? ? ? ? ? ? <input type="password"
? ? ? ? ? ? ? ? ? ? ? id="password"
? ? ? ? ? ? ? ? ? ? ? name="password"
? ? ? ? ? ? ? ? ? ? ? class="form-control"
? ? ? ? ? ? ? ? ? ? ? ng-model="user.password"
? ? ? ? ? ? ? ? ? ? ? ng-required="true"
? ? ? ? ? ? ? ? ? ? ? ng-minlength="6"
? ? ? ? ? ? ? ? ? ? ? ng-maxlength="20">
? ? ? ? ? ? ? ? <div class="error-message">
? ? ? ? ? ? ? ? ? ? <span ng-show="registerForm.password.$dirty && registerForm.password.$error.required">密碼不能為空</span>
? ? ? ? ? ? ? ? ? ? <span ng-show="registerForm.password.$dirty && registerForm.password.$error.minlength">密碼至少6個字符</span>
? ? ? ? ? ? ? ? ? ? <span ng-show="registerForm.password.$dirty && registerForm.password.$error.maxlength">密碼最多20個字符</span>
? ? ? ? ? ? ? ? </div>
? ? ? ? ? ? </div>
? ? ? ? ? ? <!-- 確認(rèn)密碼輸入框 -->
? ? ? ? ? ? <div class="form-group">
? ? ? ? ? ? ? ? <label for="confirmPwd">確認(rèn)密碼 *</label>
? ? ? ? ? ? ? ? <input type="password"
? ? ? ? ? ? ? ? ? ? ? id="confirmPwd"
? ? ? ? ? ? ? ? ? ? ? name="confirmPwd"
? ? ? ? ? ? ? ? ? ? ? class="form-control"
? ? ? ? ? ? ? ? ? ? ? ng-model="user.confirmPwd"
? ? ? ? ? ? ? ? ? ? ? ng-required="true"
? ? ? ? ? ? ? ? ? ? ? ng-match="user.password"> <!-- 自定義指令:匹配密碼 -->
? ? ? ? ? ? ? ? <div class="error-message">
? ? ? ? ? ? ? ? ? ? <span ng-show="registerForm.confirmPwd.$dirty && registerForm.confirmPwd.$error.required">確認(rèn)密碼不能為空</span>
? ? ? ? ? ? ? ? ? ? <span ng-show="registerForm.confirmPwd.$dirty && registerForm.confirmPwd.$error.match">兩次輸入的密碼不一致</span>
? ? ? ? ? ? ? ? </div>
? ? ? ? ? ? </div>
? ? ? ? ? ? <!-- 提交按鈕:表單驗證通過(registerForm.$valid)且未加載時才啟用 -->
? ? ? ? ? ? <button type="submit" class="submit-btn" ng-disabled="!registerForm.$valid || isSubmitting">
? ? ? ? ? ? ? ? <span ng-if="!isSubmitting">注冊</span>
? ? ? ? ? ? ? ? <span ng-if="isSubmitting">注冊中...</span>
? ? ? ? ? ? </button>
? ? ? ? ? ? <!-- 提交成功提示 -->
? ? ? ? ? ? <div class="submit-success" ng-show="submitSuccess">
? ? ? ? ? ? ? ? 注冊成功!即將跳轉(zhuǎn)到登錄頁...
? ? ? ? ? ? </div>
? ? ? ? </form>
? ? </div>
? ? <script>
? ? ? ? // 1. 創(chuàng)建應(yīng)用模塊
? ? ? ? const registerApp = angular.module('registerApp', []);
? ? ? ? // 2. 自定義指令ngMatch:用于確認(rèn)密碼與密碼匹配校驗
? ? ? ? registerApp.directive('ngMatch', function() {
? ? ? ? ? ? return {
? ? ? ? ? ? ? ? require: 'ngModel', // 依賴ngModel指令
? ? ? ? ? ? ? ? scope: {
? ? ? ? ? ? ? ? ? ? ngMatch: '=' // 綁定要匹配的目標(biāo)值(這里是user.password)
? ? ? ? ? ? ? ? },
? ? ? ? ? ? ? ? link: function(scope, element, attrs, ngModel) {
? ? ? ? ? ? ? ? ? ? // 監(jiān)聽目標(biāo)值(密碼)和當(dāng)前值(確認(rèn)密碼)的變化,觸發(fā)校驗
? ? ? ? ? ? ? ? ? ? scope.$watchGroup(['ngMatch', ngModel.$viewValue], function() {
? ? ? ? ? ? ? ? ? ? ? ? ngModel.$setValidity('match', ngModel.$viewValue === scope.ngMatch);
? ? ? ? ? ? ? ? ? ? });
? ? ? ? ? ? ? ? }
? ? ? ? ? ? };
? ? ? ? });
? ? ? ? // 3. 創(chuàng)建控制器
? ? ? ? registerApp.controller('registerCtrl', function($scope, $timeout) {
? ? ? ? ? ? // 初始化用戶數(shù)據(jù)
? ? ? ? ? ? $scope.user = {
? ? ? ? ? ? ? ? username: '',
? ? ? ? ? ? ? ? phone: '',
? ? ? ? ? ? ? ? password: '',
? ? ? ? ? ? ? ? confirmPwd: ''
? ? ? ? ? ? };
? ? ? ? ? ? $scope.isSubmitting = false; // 提交加載狀態(tài)
? ? ? ? ? ? $scope.submitSuccess = false; // 提交成功標(biāo)記
? ? ? ? ? ? // 表單提交方法
? ? ? ? ? ? $scope.submitForm = function() {
? ? ? ? ? ? ? ? // 再次校驗表單(防止繞過前端校驗提交)
? ? ? ? ? ? ? ? if (!$scope.registerForm.$valid) {
? ? ? ? ? ? ? ? ? ? // 若表單無效,觸發(fā)所有輸入框的臟值狀態(tài)(顯示錯誤提示)
? ? ? ? ? ? ? ? ? ? angular.forEach($scope.registerForm.$error.required, function(field) {
? ? ? ? ? ? ? ? ? ? ? ? field.$setDirty();
? ? ? ? ? ? ? ? ? ? });
? ? ? ? ? ? ? ? ? ? return;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? // 顯示加載狀態(tài)
? ? ? ? ? ? ? ? $scope.isSubmitting = true;
? ? ? ? ? ? ? ? // 模擬API提交(實際項目中替換為$http請求)
? ? ? ? ? ? ? ? console.log('提交的用戶數(shù)據(jù):', $scope.user);
? ? ? ? ? ? ? ? // 模擬提交成功(2秒后)
? ? ? ? ? ? ? ? $timeout(function() {
? ? ? ? ? ? ? ? ? ? $scope.isSubmitting = false;
? ? ? ? ? ? ? ? ? ? $scope.submitSuccess = true;
? ? ? ? ? ? ? ? ? ? // 模擬3秒后跳轉(zhuǎn)(實際項目中用$location.path('/login'))
? ? ? ? ? ? ? ? ? ? $timeout(function() {
? ? ? ? ? ? ? ? ? ? ? ? alert('跳轉(zhuǎn)到登錄頁');
? ? ? ? ? ? ? ? ? ? }, 3000);
? ? ? ? ? ? ? ? }, 2000);
? ? ? ? ? ? };
? ? ? ? });
? ? </script>
</body>
</html>
3. 應(yīng)用核心要點
內(nèi)置表單驗證指令:ng-required(必填)、ng-minlength(最小長度)、ng-maxlength(最大長度)、ng-pattern(正則匹配),無需手動寫校驗邏輯;
表單狀態(tài)變量:AngularJS自動為表單和輸入框添加狀態(tài)變量(如$dirty:是否修改過、$valid:是否有效、$error:錯誤類型),可直接用于錯誤提示控制;
自定義指令擴展:通過ngMatch自定義指令實現(xiàn)“確認(rèn)密碼匹配”功能,體現(xiàn)AngularJS的擴展性;
防重復(fù)提交:用isSubmitting標(biāo)記加載狀態(tài),禁用提交按鈕,避免用戶重復(fù)點擊。
二、應(yīng)用場景2:商品列表(Ajax數(shù)據(jù)請求+列表渲染)
實際應(yīng)用中,數(shù)據(jù)通常來自后端API,AngularJS提供$http服務(wù)用于發(fā)送Ajax請求,結(jié)合ng-repeat可快速實現(xiàn)“數(shù)據(jù)加載→列表渲染→狀態(tài)提示”的完整流程。以下以“商品列表”為例,落地數(shù)據(jù)請求類應(yīng)用開發(fā)。
1. 需求拆解
頁面加載時,發(fā)送請求獲取商品數(shù)據(jù);
加載過程中顯示“加載中”提示;
加載失敗時顯示錯誤提示,支持重試;
加載成功后,用列表渲染商品(包含圖片、名稱、價格、銷量);
支持按“價格升序/降序”篩選商品。
2. 完整應(yīng)用代碼
<!DOCTYPE html>
<html lang="zh-CN">
<head>
? ? <meta charset="UTF-8">
? ? <meta name="viewport" content="width=device-width, initial-scale=1.0">
? ? <title>AngularJS入門應(yīng)用 - 商品列表</title>
? ? <script src="https://apps.bdimg.com/libs/angular.js/1.8.2/angular.min.js"></script>
? ? <style>
? ? ? ? .product-container {
? ? ? ? ? ? width: 1200px;
? ? ? ? ? ? margin: 30px auto;
? ? ? ? ? ? padding: 20px;
? ? ? ? ? ? box-sizing: border-box;
? ? ? ? }
? ? ? ? .header {
? ? ? ? ? ? display: flex;
? ? ? ? ? ? justify-content: space-between;
? ? ? ? ? ? align-items: center;
? ? ? ? ? ? margin-bottom: 20px;
? ? ? ? }
? ? ? ? .filter-select {
? ? ? ? ? ? padding: 8px;
? ? ? ? ? ? border: 1px solid #ddd;
? ? ? ? ? ? border-radius: 4px;
? ? ? ? ? ? font-size: 14px;
? ? ? ? }
? ? ? ? .loading, .error {
? ? ? ? ? ? text-align: center;
? ? ? ? ? ? padding: 50px;
? ? ? ? ? ? color: #666;
? ? ? ? }
? ? ? ? .error {
? ? ? ? ? ? color: #e53935;
? ? ? ? }
? ? ? ? .retry-btn {
? ? ? ? ? ? padding: 8px 16px;
? ? ? ? ? ? border: none;
? ? ? ? ? ? background: #4285f4;
? ? ? ? ? ? color: white;
? ? ? ? ? ? border-radius: 4px;
? ? ? ? ? ? cursor: pointer;
? ? ? ? ? ? margin-top: 10px;
? ? ? ? }
? ? ? ? .product-list {
? ? ? ? ? ? display: flex;
? ? ? ? ? ? flex-wrap: wrap;
? ? ? ? ? ? gap: 20px;
? ? ? ? }
? ? ? ? .product-item {
? ? ? ? ? ? width: calc(25% - 15px);
? ? ? ? ? ? border: 1px solid #eee;
? ? ? ? ? ? border-radius: 8px;
? ? ? ? ? ? overflow: hidden;
? ? ? ? ? ? transition: box-shadow 0.3s ease;
? ? ? ? }
? ? ? ? .product-item:hover {
? ? ? ? ? ? box-shadow: 0 5px 15px rgba(0,0,0,0.08);
? ? ? ? }
? ? ? ? .product-img {
? ? ? ? ? ? width: 100%;
? ? ? ? ? ? height: 200px;
? ? ? ? ? ? object-fit: cover;
? ? ? ? }
? ? ? ? .product-info {
? ? ? ? ? ? padding: 15px;
? ? ? ? }
? ? ? ? .product-name {
? ? ? ? ? ? margin: 0 0 10px;
? ? ? ? ? ? font-size: 16px;
? ? ? ? ? ? color: #333;
? ? ? ? ? ? display: -webkit-box;
? ? ? ? ? ? -webkit-line-clamp: 2;
? ? ? ? ? ? -webkit-box-orient: vertical;
? ? ? ? ? ? overflow: hidden;
? ? ? ? ? ? height: 40px;
? ? ? ? }
? ? ? ? .product-price {
? ? ? ? ? ? margin: 0 0 5px;
? ? ? ? ? ? font-size: 18px;
? ? ? ? ? ? color: #e53935;
? ? ? ? ? ? font-weight: bold;
? ? ? ? }
? ? ? ? .product-sales {
? ? ? ? ? ? margin: 0;
? ? ? ? ? ? font-size: 12px;
? ? ? ? ? ? color: #999;
? ? ? ? }
? ? ? ? .no-data {
? ? ? ? ? ? text-align: center;
? ? ? ? ? ? padding: 50px;
? ? ? ? ? ? color: #999;
? ? ? ? ? ? width: 100%;
? ? ? ? }
? ? </style>
</head>
<body ng-app="productApp">
? ? <div class="product-container" ng-controller="productCtrl">
? ? ? ? <div class="header">
? ? ? ? ? ? <h2>商品列表</h2>
? ? ? ? ? ? <!-- 價格排序篩選 -->
? ? ? ? ? ? <select class="filter-select"
? ? ? ? ? ? ? ? ? ? ng-model="sortType"
? ? ? ? ? ? ? ? ? ? ng-change="sortProducts()">
? ? ? ? ? ? ? ? <option value="">默認(rèn)排序</option>
? ? ? ? ? ? ? ? <option value="priceAsc">價格升序</option>
? ? ? ? ? ? ? ? <option value="priceDesc">價格降序</option>
? ? ? ? ? ? </select>
? ? ? ? </div>
? ? ? ? <!-- 加載中狀態(tài) -->
? ? ? ? <div class="loading" ng-show="isLoading">
? ? ? ? ? ? <div>加載中...</div>
? ? ? ? </div>
? ? ? ? <!-- 加載錯誤狀態(tài) -->
? ? ? ? <div class="error" ng-show="isError">
? ? ? ? ? ? <div>加載失敗,請重試!</div>
? ? ? ? ? ? <button class="retry-btn" ng-click="loadProducts()">重試</button>
? ? ? ? </div>
? ? ? ? <!-- 商品列表 -->
? ? ? ? <div class="product-list" ng-show="!isLoading && !isError">
? ? ? ? ? ? <div class="product-item" ng-repeat="product in products">
? ? ? ? ? ? ? ? <img src="{{ product.imgUrl }}" alt="{{ product.name }}" class="product-img">
? ? ? ? ? ? ? ? <div class="product-info">
? ? ? ? ? ? ? ? ? ? <h3 class="product-name">{{ product.name }}</h3>
? ? ? ? ? ? ? ? ? ? <p class="product-price">¥{{ product.price.toFixed(2) }}</p>
? ? ? ? ? ? ? ? ? ? <p class="product-sales">銷量:{{ product.s</doubaocanvas>