AngularJS 1.3 完整教程

什么是 AngularJS?

AngularJS 是一个由 Google 维护的开源 JavaScript 框架,它通过引入 MVC(Model-View-Controller) 架构模式来简化单页应用的开发,它的核心思想是:

angularjs 1.3 教程
(图片来源网络,侵删)
  • 数据绑定:自动同步 JavaScript 中的数据(Model)和 HTML 中的展示(View)。
  • 依赖注入:一种设计模式,使得组件之间的解耦变得更容易,代码更易于测试和维护。
  • 指令:允许你创建自定义的 HTML 标签和属性,扩展 HTML 的功能。

为什么是 AngularJS 1.3?

虽然 AngularJS 已经发展到了 2+ 版本(并重写为 TypeScript),但 1.3 版本因其成熟、稳定和庞大的社区生态,对于学习前端框架的基本思想或维护旧项目来说,仍然是一个非常好的起点。

准备工作

  1. 文本编辑器:推荐使用 Visual Studio Code, Sublime Text 或 Atom。
  2. 现代浏览器:推荐使用 Chrome 或 Firefox,它们有强大的开发者工具。
  3. 一个 HTML 文件:我们将在这个文件中编写所有代码。

第一部分:入门基础

引入 AngularJS

在 HTML 文件的 <head><body> 标签内,通过 <script> 标签引入 AngularJS 的库文件,你可以从 AngularJS 官网 下载,或者使用 CDN(推荐)。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">AngularJS 1.3 教程</title>
    <!-- 引入 AngularJS 1.3.20 版本 -->
    <script src="https://code.angularjs.org/1.3.20/angular.min.js"></script>
</head>
<body>
</body>
</html>

核心概念:ng-appng-model

AngularJS 应用总是通过一个 ng-app 指令来启动的,它告诉 AngularJS:“嘿,你负责管理这个 <div><body> 里面的所有东西。”

ng-model 指令用于将 HTML 元素(如输入框)与一个变量进行双向数据绑定。

angularjs 1.3 教程
(图片来源网络,侵删)

示例:数据绑定

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">AngularJS 1.3 教程 - Hello World</title>
    <script src="https://code.angularjs.org/1.3.20/angular.min.js"></script>
</head>
<body>
    <!-- 1. 声明一个 AngularJS 应用,并指定一个控制器 -->
    <div ng-app="myApp" ng-controller="myCtrl">
        <!-- 2. 使用 ng-model 将输入框的值绑定到 name 变量 -->
        <p>请输入你的名字:</p>
        <input type="text" ng-model="name">
        <!-- 3. {{ name }} 是 AngularJS 的表达式,它会自动显示 name 变量的值 -->
        <p>你好, <strong>{{ name }}</strong>!</p>
    </div>
    <script>
        // 4. 获取你的应用模块
        var app = angular.module('myApp', []);
        // 5. 定义控制器 myCtrl
        app.controller('myCtrl', function($scope) {
            // $scope 是控制器和视图之间的桥梁
            // 在这里初始化 name 变量
            $scope.name = "AngularJS";
        });
    </script>
</body>
</html>

代码解析:

  1. ng-app="myApp": 定义了一个名为 "myApp" 的 AngularJS 应用。
  2. ng-controller="myCtrl": 将这个 <div> 的控制权交给名为 "myCtrl" 的控制器。
  3. ng-model="name": 将输入框的值与 $scope 对象上的 name 属性绑定。
  4. {{ name }}: AngularJS 表达式,用于在页面上显示 $scope.name 的值,当你输入时,它会实时更新。
  5. angular.module('myApp', []): 创建或获取一个应用模块。[] 表示它没有依赖其他模块。
  6. app.controller('myCtrl', function($scope) { ... }): 定义控制器。
    • $scope: 这是 AngularJS 的核心对象,它是连接控制器和视图的“胶水”,在控制器中定义的所有属性和方法都会被附加到 $scope 上,从而在视图中可用。

第二部分:核心指令详解

ng-init (初始化数据)

ng-init 用于在作用域内初始化变量,虽然它很方便,但在实际开发中,推荐在控制器中初始化数据,以保持逻辑的清晰。

<div ng-app="myApp" ng-controller="myCtrl" ng-init="firstName='John', lastName='Doe'">
    <p>姓名: {{ firstName + " " + lastName }}</p>
</div>

ng-repeat (循环数组/对象)

这是最常用的指令之一,用于循环渲染一个列表。

angularjs 1.3 教程
(图片来源网络,侵删)
<div ng-app="myApp" ng-controller="namesCtrl">
    <ul>
        <!-- 循环 names 数组,为每个元素创建一个 <li> -->
        <li ng-repeat="x in names">
            {{ x.name + ', ' + x.country }}
        </li>
    </ul>
</div>
<script>
    var app = angular.module('myApp', []);
    app.controller('namesCtrl', function($scope) {
        $scope.names = [
            {name:'Jani',country:'Norway'},
            {name:'Hege',country:'Sweden'},
            {name:'Kai',country:'Denmark'}
        ];
    });
</script>

ng-repeat 中的特殊变量:

  • $index: 当前元素的索引 (0, 1, 2, ...)。
  • $first: 是否为第一个元素 (true/false)。
  • $middle: 是否为中间元素 (true/false)。
  • $last: 是否为最后一个元素 (true/false)。
<li ng-repeat="x in names">
    #{{$index+1}}: {{ x.name }}
    <span ng-if="$last">(最后一个)</span>
</li>

ng-if vs ng-show / ng-hide

这三个指令都用于控制元素的显示和隐藏,但原理不同。

  • ng-if="true/false":

    • 原理:它会根据表达式的值,从 DOM 中完全添加或移除 元素。
    • 特点:如果为 false,元素及其所有子元素和事件监听器都会被销毁,当变为 true 时,会重新创建和初始化,性能开销较大,但适合条件不频繁变化的场景。
  • ng-show="true/false":

    • 原理:它不会移除元素,而是通过 CSS 的 display: none; 来隐藏元素。
    • 特点:元素始终存在于 DOM 中,只是看不见,性能开销小,适合需要频繁切换显示/隐藏的场景。
  • ng-hide="true/false":

    • 原理:与 ng-show 相反,它通过 display: none; 来显示元素(当表达式为 false 时)。

示例:

<div ng-app="myApp" ng-controller="toggleCtrl">
    <button ng-click="showIt = !showIt">切换显示</button>
    <p ng-if="showIt">这个段落用 ng-if 控制,看不见时会被移除。</p>
    <p ng-show="showIt">这个段落用 ng-show 控制,看不见时只是隐藏。</p>
</div>
<script>
    var app = angular.module('myApp', []);
    app.controller('toggleCtrl', function($scope) {
        $scope.showIt = true;
    });
</script>

第三部分:控制器与作用域

控制器

控制器是 JavaScript 构造函数,用于向 $scope 添加功能(属性和方法)。

最佳实践:

  • 保持控制器精简:控制器只负责初始化数据和暴露行为给视图。
  • 将业务逻辑移至服务中:复杂的逻辑、数据访问、API 调用等应该放在 服务 中。

作用域

作用域是视图和控制器之间的数据桥梁,理解作用域的继承关系至关重要。

  • 根作用域:由 ng-app 创建,是所有 $scope 的祖先。
  • 子作用域:当在父元素内部使用 ng-controller 时,会创建一个子作用域,它会继承父作用域的属性。

示例:作用域继承

<div ng-app="myApp">
    <!-- 根作用域 -->
    <h1>根作用域的变量: {{ parentVar }}</h1>
    <!-- parentCtrl 的作用域是根作用域的子作用域 -->
    <div ng-controller="parentCtrl">
        <h2>父控制器作用域</h2>
        <p>可以访问父变量: {{ parentVar }}</p>
        <p>自己的变量: {{ childVar }}</p>
        <button ng-click="parentFunc()">调用父控制器方法</button>
    </div>
    <!-- anotherCtrl 的作用域也是根作用域的子作用域,与 parentCtrl 平级 -->
    <div ng-controller="anotherCtrl">
        <h2>另一个控制器作用域</h2>
        <p>可以访问父变量: {{ parentVar }}</p>
        <p>这个控制器有自己的变量: {{ anotherVar }}</p>
        <!-- 这个按钮会报错,因为 anotherCtrl 没有 parentFunc -->
        <button ng-click="parentFunc()">调用父控制器方法 (会报错)</button>
    </div>
</div>
<script>
    var app = angular.module('myApp', []);
    // 在根作用域上添加一个属性
    app.run(function($rootScope) {
        $rootScope.parentVar = "我是根作用域的变量";
    });
    app.controller('parentCtrl', function($scope) {
        $scope.childVar = "我是父控制器的变量";
        $scope.parentFunc = function() {
            alert("父控制器的方法被调用了!");
        };
    });
    app.controller('anotherCtrl', function($scope) {
        $scope.anotherVar = "我是另一个控制器的变量";
    });
</script>

第四部分:服务

服务是 AngularJS 中一个核心的单例对象,通过依赖注入 系统来使用,它们非常适合用来存放共享的数据、工具函数或与后端 API 交互的逻辑。

内置服务

AngularJS 提供了许多内置服务,如 $http (AJAX 请求), $timeout (延迟), $interval (定时器) 等,使用时,只需在控制器的函数参数中声明它们,AngularJS 会自动注入。

示例:使用 $http 服务获取数据

<div ng-app="myApp" ng-controller="httpCtrl">
    <ul>
        <li ng-repeat="user in users">
            {{ user.name }} - {{ user.email }}
        </li>
    </ul>
    <p ng-if="loading">加载中...</p>
    <p ng-if="error">{{ error }}</p>
</div>
<script>
    var app = angular.module('myApp', []);
    app.controller('httpCtrl', function($scope, $http) {
        $scope.loading = true;
        $scope.error = null;
        // 使用 $http 服务从 JSONPlaceholder 获取模拟数据
        $http.get('https://jsonplaceholder.typicode.com/users')
            .then(function(response) {
                // 成功回调
                $scope.users = response.data;
                $scope.loading = false;
            }, function(error) {
                // 失败回调
                $scope.error = "无法获取数据: " + error.statusText;
                $scope.loading = false;
            });
    });
</script>

创建自定义服务

你可以创建自己的服务来封装特定功能。

<div ng-app="myApp" ng-controller="mainCtrl">
    <p>{{ currentTime }}</p>
</div>
<script>
    var app = angular.module('myApp', []);
    // 创建一个名为 'timeService' 的自定义服务
    app.service('timeService', function() {
        this.getTime = function() {
            return new Date().toString();
        };
    });
    // 在控制器中注入并使用这个服务
    app.controller('mainCtrl', function($scope, timeService) {
        $scope.currentTime = timeService.getTime();
    });
</script>

第五部分:路由

单页应用通常需要在不刷新整个页面的情况下切换视图,AngularJS 1.3 的核心模块不包含路由功能,你需要引入 angular-route.js 模块。

  1. 引入 angular-route.js

    <script src="https://code.angularjs.org/1.3.20/angular-route.min.js"></script>
  2. 配置路由

    <!DOCTYPE html>
    <html lang="en" ng-app="myApp">
    <head>
        <meta charset="UTF-8">
        <title>AngularJS 路由示例</title>
        <script src="https://code.angularjs.org/1.3.20/angular.min.js"></script>
        <script src="https://code.angularjs.org/1.3.20/angular-route.min.js"></script>
    </head>
    <body>
        <!-- 导航链接 -->
        <a href="#/">首页</a>
        <a href="#/about">lt;/a>
        <!-- ng-view 是一个占位符,匹配的模板会被加载到这里 -->
        <div ng-view></div>
        <script>
            var app = angular.module('myApp', ['ngRoute']); // 依赖 'ngRoute' 模块
            // 配置路由
            app.config(function($routeProvider) {
                $routeProvider
                    .when('/', {
                        templateUrl: 'home.html', // 指向视图模板
                        controller: 'homeCtrl'    // 指向控制器
                    })
                    .when('/about', {
                        templateUrl: 'about.html',
                        controller: 'aboutCtrl'
                    })
                    .otherwise({ // 其他所有请求都重定向到首页
                        redirectTo: '/'
                    });
            });
            // 首页控制器
            app.controller('homeCtrl', function($scope) {
                $scope.message = "欢迎来到首页!";
            });
            // 关于页面控制器
            app.controller('aboutCtrl', function($scope) {
                $scope.message = "这是关于我们页面。";
            });
        </script>
    </body>
    </html>

你需要创建两个 HTML 文件:

  • home.html:

    <h2>首页</h2>
    <p>{{ message }}</p>
  • about.html:

    <h2>关于我们</h2>
    <p>{{ message }}</p>

第六部分:创建自定义指令

自定义指令是 AngularJS 最强大的特性之一,它允许你创建可重用的 UI 组件。

示例:创建一个显示当前时间的指令

<div ng-app="myApp">
    <!-- 使用自定义指令 -->
    <my-current-time></my-current-time>
</div>
<script>
    var app = angular.module('myApp', []);
    app.directive('myCurrentTime', function($interval, dateFilter) {
        // 返回指令的定义对象
        return {
            // E: Element (元素名) <my-current-time></my-current-time>
            // A: Attribute (属性名) <div my-current-time></div>
            // C: Class (类名) <div class="my-current-time"></div>
            // M: Comment (注释) <!-- directive: my-current-time -->
            restrict: 'E',
            // scope: true, // 创建一个子作用域,隔离外部作用域
            template: '<span>当前时间是: {{ time }}</span>',
            link: function(scope, element, attrs) {
                var format = 'HH:mm:ss';
                var update_time = function() {
                    scope.time = dateFilter(new Date(), format);
                };
                // 立即更新一次
                update_time();
                // 每秒更新一次
                var interval_id = $interval(update_time, 1000);
                // 当指令作用域被销毁时,清除定时器,防止内存泄漏
                scope.$on('$destroy', function() {
                    $interval.cancel(interval_id);
                });
            }
        };
    });
</script>

第七部分:最佳实践与总结

  1. 命名约定:

    • 模块名: myApp, userApp (小写,可以加点号)
    • 控制器名: myCtrl, userListCtrl (驼峰命名)
    • 服务名: userService, dataService (驼峰命名)
    • 指令名: myDirective, userAvatar (驼峰命名,在 HTML 中使用时用 分隔,如 my-directive)
  2. 模块化: 将你的应用拆分成多个模块,每个模块负责一个功能区域。

  3. 控制器: 保持控制器“瘦”,只做 $scope 的初始化和视图交互。

  4. 服务: 将业务逻辑、数据访问、工具函数都放到服务中。

  5. 依赖注入: 始终使用数组形式的语法来声明依赖,这样可以避免代码压缩后因变量名改变而出错。

    // 不推荐 (压缩后会报错)
    app.controller('myCtrl', function($scope, $http) { ... });
    // 推荐 (安全)
    app.controller('myCtrl', ['$scope', '$http', function($scope, $http) { ... }]);
  6. 性能优化:

    • 合理使用 ng-ifng-show
    • 对于大型列表,考虑使用 track by 来提高 ng-repeat 的性能。
      <li ng-repeat="item in items track by item.id">
    • 使用 $watch 的第三个参数 (objectEquality) 要谨慎,因为它开销很大。

这份教程涵盖了 AngularJS 1.3 的核心概念,通过学习和实践这些内容,你将能够构建功能完善的单页应用,AngularJS 1.3 虽然有些年头,但其核心思想(数据绑定、依赖注入、指令)深刻影响了后来的前端框架,学习它对理解现代前端开发非常有帮助。