65.9K
CodeProject 正在变化。 阅读更多。
Home

学习 AngularJS 入门

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.77/5 (12投票s)

2016年9月18日

CPOL

9分钟阅读

viewsIcon

59153

本文介绍了 AngularJS 的基本概念,如模块、控制器、表达式、指令、作用域和作用域继承。

先决条件

要理解和学习 AngularJS,需要具备 HTML、CSS 和 JavaScript 知识。

引言

我们知道,静态网页是使用 HTML5 和 CSS 开发的,而要使页面动态化或具有用户交互性,我们还需要使用 JavaScript 框架。现在有很多可用的 JavaScript 框架,如 jQuery、KnockoutJS、BackboneJS、KendoUI、emberJS,AngularJS 也是其中之一。虽然框架的选择取决于项目需求的多种条件和要求,但 AngularJS 是最常被选择的框架。因此,在深入研究之前,让我们了解一下 AngularJS 是什么,以及它有什么特别之处。

什么是 AngularJS

AngularJS 是一个由 Google 开发和维护的开源 JavaScript 框架。它实现了 MVC(模型-视图-控制器)模式,用于分离数据、表示和逻辑组件。它就像一个增强型的 HTML,用于 Web 应用程序,因为它通过在 HTML 标签上使用不同的属性来扩展传统的 HTML。在 Angular 术语中,这些属性被称为指令。

为什么选择 AngularJS

  1. Angular 的标记生活在 DOM 中。

    例如,如果我们考虑 HTML,浏览器会根据 HTML 标签显示内容,例如,如果存在 <h1 /> 标签,浏览器就会理解它是一个标题并相应地显示页面。同样,HTML 中有很多预定义的标签用于显示静态内容。现在,如果我们想让我们的网页具有用户交互性或成为一个动态页面,我们就会选择 JavaScript 框架。其他 JavaScript 框架提供插件或函数,这些插件或函数会在 HTML 元素上调用。例如,如果我们想在页面上显示 datepicker,从 jQuery 的角度来看,我们首先在 HTML 中添加一个普通的输入字段,然后在 jQuery 中,我们编写 $(element).datePicker(); 来将其实际转换为日期选择器。那么,当我们看标记时,能否立即猜出该输入字段实际上是什么?它是一个普通的输入字段还是一个日期选择器?我们需要查看 jQuery 代码来确认。

    因此,Angular 的方法是使用 HTML 标签上的属性或指令,使 DOM 更易于理解和维护。这样,Angular 的标记或指令就生活在 DOM 中。

  2. 双向数据绑定。这意味着如果模型(后端)有任何更改,视图就会更新,反之亦然。我们可以使用这种方法以更少的精力构建高性能的 Web 应用程序。要在 jQuery 中实现相同的绑定,我们需要在模型和视图层编写逻辑。但在 Angular 中,我们只需要使用 $scope 绑定变量,Angular 会处理其余的事情。

  3. 在提交表单和输入字段之前可以对其进行验证。

  4. 允许我们控制完整的 DOM 结构,即使用 Angular 显示/隐藏、更改一切。

  5. 我们可以创建模板用于可重用块并多次使用。这是通过在 Angular 中创建自定义指令来实现的。

  6. 允许我们编写基本的端到端测试、单元测试。

包含 AngularJS 参考

要开始使用 Angular 创建应用程序,请从 https://angularjs.org/ 下载 angular.js 文件,并在将使用 Angular 标记的 HTML 页面中引用它。另一种包含 angular.js 文件的方法是从 CDN(内容分发网络) https://ajax.googleapis.ac.cn/ajax/libs/angularjs/1.4.8/angular.min.js。如果我们正在创建独立的应用程序,最好将文件包含在项目中,而不是从 CDN 使用。

预定义指令和表达式

到目前为止,我们已经知道指令是 AngularJS 的特殊关键概念,这使其在其他 JavaScript 框架中脱颖而出。

指令是 HTML 标签上的标记,告诉 Angular 运行或引用一些 JavaScript 代码。预定义指令以 ng- 为前缀。让我们来看一些预定义指令。

ng-app 指令定义了一个 Angular 应用程序。使用此指令的元素成为 Angular 应用程序的所有者。也就是说,在此元素内部,可以在 HTML 上使用 Angular 指令或标记。为了避免在 ng-app 范围之外使用 Angular 指令,我们在 HTML 标签或 HTML 的 body 标签上使用 ng-app

示例 1

<div ng-app="">
    <div>Employee Name: <input type="text" ng-model="empName" /></div>
    <p> {{ 4 + 5}} </p>
</div>

在上面的示例中,我们还可以看到在 input 元素上使用了另一个指令 ng-model。此指令用于保存用户输入的值。它用于输入类型元素,如复选框、单选框、文本元素、下拉列表等,以读取和存储用户输入的值。

同样,Angular 还提供了 ng-bindng-initng-repeatng-showng-hide 等其他指令,每种指令都与预定义的操作相关联。

在上面的示例中,我们可以看到 {{ }}(花括号)的使用。这些括号称为表达式。也就是说,在 Angular 中,我们可以通过将任何表达式包含在这些括号中来对其进行求值。表达式 {{ 4 + 5 }} 求值为 9,并在输出页面上显示 9。类似地,我们可以使用 Angular 的表达式对数字或 字符串 进行任何数学运算。此外,我们可以在表达式中使用变量(存储某些数据的变量)。让我们通过一个例子来看看。

示例 2

<div ng-app="" ng-init="items=5; price=5">
     <div>Total price is {{  items * price }}</div>
</div>

输出:总价是 25

解释

ng-init 预定义指令用于在 HTML DOM 中初始化变量。在示例中,items、price 变量使用 ng-init 指令初始化为值 5。Angular 表达式 {{ items * price }} 求值为 25

模块和依赖注入

var app = angular.module('myCompApp');

AngularJS 的模块定义了一个应用程序。它是一个容器,用于存放应用程序的不同部分,如控制器、指令、服务、工厂。这里,模块名是 myCompApp,创建了模块对象并存储在 app 变量中。如果此模块依赖于其他模块,我们将依赖项包含在空括号 [ ] 中。例如,如果模块 ‘myCompApp’ 依赖于外部模块,比如 ‘chart.js’,那么它被写成

var app = angular.module('myCompApp', ['chart.js']);

这种注入依赖的方式称为“依赖注入”,Angular 大量利用依赖注入。在接下来的示例中,我们将看到更多依赖注入的用法。

在 HTML 中

<div ng-app="myCompApp">
         ----
<div>

我们知道 ng-app 定义了一个 Angular 应用程序。通过将模块分配给 ng-app,它会在页面/文档加载时运行该模块(此处为 myCompApp)。

现在,让我们假设我们有如下 JSON 格式的数据。

personData = {name: “Tom”, age: “50”, moreInfo: “some more information about person”} 

我们想将这些数据打印到页面上。我们如何使用 AngularJS 实现这一点?这时就引入了控制器的概念。

控制器

控制器是我们通过定义函数和值来定义应用程序行为的地方。

让我们用 ‘PersonController’ 这个名字创建我们的第一个控制器。最好使用驼峰命名法,首字母大写来命名控制器。

app.controller("PersonController", function($scope){
      -----
});

在这里,我们可以注意到控制器被附加到模块(app)上,因为模块是所有 Angular 关键项(控制器、指令、服务)的容器。

定义的控制器使用 Angular 指令 ng-Controller 在 HTML 中附加。当 Angular 编译器遇到 ng-Controller 指令时,它会找到关联控制器的定义并执行它。

<div ng-controller="PersonController">
   -----
</div>

现在,我们已经创建了控制器,并且有了数据。所以要打印这些数据,我们使用 Angular 的作用域概念。

$scope

$scope 是“视图”(即 HTML)和“控制器”(即 JavaScript)之间的绑定部分。它是一个具有属性和方法的对象。附加到此对象上的属性和方法可以在 HTML(视图)中使用或调用。

当定义一个控制器时,会创建其关联的 $scope 对象。此对象的范围是包含 ng-controller 指令的 HTML 元素上创建的范围。

让我们看看如何使用 $scope

让我们向 $scope 添加一个属性。我们向此属性分配数据,以便可以在 HTML 页面中用于打印我们的数据。

$scope.employeeData = personData;

在向 scope 对象添加属性时,我们可以在 HTML 或视图页面中访问这些属性,并且在视图页面中,我们再次不使用前缀 $scope。我们只需在表达式中写属性名,例如 {{ employeeData .name}}

将控制器写在 JavaScript 文件中,将 HTML 部分写在 HTML 页面中,就变成了如下所示。

示例 3

personData = {name: 'Tom', age:'50', moreInfo:'some more information about person'};

app.controller("PersonController", function($scope){
    $scope.employeeData = personData;
});
<div ng-app="myCompApp">
       <div ng-controller="PersonController">
            <p> Name of employee is {{ employeeData.name }} </p>
       </div>
</div>

输出:Tom

示例 4

我们还向我们的 $scope 附加方法/函数。

在脚本文件

app.controller("PersonController", function($scope){
      $scope.employeeData = personData;
      $scope.employeeMethod = function(){
           console.log("Hello, I am an Employee");
      }
});

在 HTML 文件

<div ng-app ="myCompApp">
     <div ng-controller="PersonController">
           <p> Name of employee is {{ employeeData.name}} </p>
           <button ng-click="employeeMethod()"> Get Details </button>
     </div>
</div>

输出

Name of employee is Tom.

并且在按钮点击时,会在控制台输出文本“Hello I am an Employee”。

RootScope

在上面的示例 4 中,只有一个作用域,所以知道作用域很容易。但对于大型应用程序,HTML DOM 可能有只能访问特定作用域的部分。

所有应用程序都有一个 $rootScope,它是创建在包含 ng-app 指令的 HTML 元素上的作用域。$rootScope 在整个应用程序中都可用。它可以用如下方式使用:

app.run(function($rootScope){
     $rootScope.personName = "Jerry";
});

在 HTML 中

<div ng-app ="myCompApp">
       <p> Name of person from rootScope : {{ personName }}</p>
       <div ng-controller ="PersonController">
            <p> Name of person from Controller's scope: {{ employeeData.name }} </p>
       </div>
</div>

输出

Name of person from rootScope: Jerry

Name of person from controller's scope: Tom

解释

在此示例中,personName 不在 PersonController 的作用域中,并且虽然它写在带有 ng-controllerdiv 之上,但我们得到了输出。假设我们现在尝试在 ng-appdiv 之前使用 {{ personName }},那么我们将无法获得所需的输出。因为 rootScope 的作用域是在 ng-app 指令内的。

以上是关于控制器作用域和 rootScope 的内容。现在让我们学习作用域继承。

作用域继承

在 DOM 层次结构的不同级别附加控制器是很常见的。由于 ng-controller 指令会创建一个新的子作用域,因此我们会得到一个作用域层次结构,每个子作用域都继承自其父级1。每个子控制器的 $scope 都可以访问其父控制器属性和方法。

示例 5

在脚本文件里,

app.controller("MainController", ['$scope', function($scope){
    $scope.name = "Donald"; 
    $scope.color = "White";
}]);

让我们用如下方式创建一个名为 ChildController 的新控制器……

app.controller("ChildController", ['$scope', function($scope){
      $scope.name = "Scrooz";
      $scope.color = "Black";
}]);

……再创建一个……

app.controller("GrandChildController", ['$scope', function($scope){
     $scope.name = "Julie";
}]);

现在在 HTML 中

<div ng-controller="MainController">
      <p> Name from Main Controller : {{ name }}</p>
      <p> Color from Main Controller : {{ color }}</p>
       <div ng-controller="ChildController">
            <p> Name from Child Controller : {{ name }}</p>
            <p> Color from Child Controller : {{ color }}</p>
            <div ng-controller="GrandChildController">
                <p> Name from Grand Child Controller : {{ name }}</p>
                <p> Color from Grand Child Controller : {{ color }}</p>
            </div>
        </div>
</div>

输出

Name from Main Controller: Donald

Color from Main Controller: White

Name from Child Controller: Scrooz

Color from Child Controller: Black

Name from GrandChild Controller: Julie

Color from GrandChild Controller: Black

解释

在上面的示例中,虽然每个控制器中附加到 $scope 的属性名相同,但我们在输出中得到了不同的名称。这是因为 ChildController$scope 覆盖了同名的父属性。如果属性未在 ChildController 中定义,则从其父控制器中获取。正如在 GrandChildController 的情况下所注意到的,我们没有定义属性名 ‘color’,但当我们尝试通过在 HTML 中使用属性名 “color” 来打印它时,我们得到了输出 White,这意味着它继承了来自其直接父级 ChildController 的值。

结论

因此,我们已经了解了 AngularJS 在各种 JavaScript 框架中为何如此特别。此外,我们还学习了指令、模块、表达式、控制器。

简而言之

指令是触发 JavaScript 函数或行为的 HTML 标记。模块是控制器、服务、工厂等的容器。表达式用于在页面内显示值,而控制器是我们添加应用程序行为的地方。

参考文献

  1. AngularJS 控制器文档 - https://docs.angularjs.org/guide/controller

其他参考文献

  1. https://angularjs.org/
  2. https://w3schools.org.cn/angular/
© . All rights reserved.