Angular 教程 - 第 3 部分: 理解和使用指令






4.84/5 (40投票s)
理解 Angular 指令的基本概念——指令在 Angular 中的用法以及使用一些 Angular 内置指令。
引言
在本文中,我们将尝试理解 Angular 指令的基本概念。我们将了解指令在 Angular 中的用法,并尝试使用一些 Angular 内置的指令。
完整系列链接
- AngularJS 教程 - 第一部分:AngularJS 简介
- Angular 教程 -
第 2 部分: 理解模块和控制器 - Angular 教程 -
第 3 部分: 理解和使用指令 - Angular 教程 -
第 4 部分: 理解和实现过滤器 - Angular 教程 - 第 5 部分:
理解和实现服务 - Angular 教程 -
第 6 部分: 构建和验证数据录入表单 - Angular 教程 -
第 7 部分: 理解单页应用程序和 Angular 路由
背景
传统上,Web 只包含相互链接的静态 HTML 页面。服务器端技术的兴起使我们能够动态地在服务器端创建 HTML 并将其推送到客户端。但在两种情况下,生成的 HTML 都包含 HTML 规范中定义的元素和属性。通过指令,Angular 将事物提升到了一个新的水平。
Angular 指令为我们提供的功能是,它们允许我们编写自己的 HTML 元素和属性。这意味着我们现在有机会扩展 HTML 词汇表,并以类似 HTML 的元素和属性形式定义自己的逻辑和行为。我们可以创建自定义元素、属性甚至 HTML 注释的指令。
Using the Code
Angular 中的指令主题可以分为两部分。首先,我们可以讨论 Angular 提供的内置指令以及如何在我们的应用程序中使用它们。其次,如何自己创建自定义指令,以指令的形式创建可重用的功能。
Angular 中的内置指令
在本文中,我们将讨论最常用的 Angular 内置指令。创建自定义指令是一个相当高级的主题,我们将在该系列的第九篇文章中讨论,届时我们将研究 Angular 的其他方面,并更好地深入研究创建自定义指令的细节。所以,让我们一个接一个地开始介绍 Angular 中的内置指令。
ng-app
在本系列的上一篇文章中,我提到我们需要将 `ng-app` 放在我们想要使用 Angular 应用的 HTML 元素中。这个 `ng-app` 是什么?`ng-app` 是一个 Angular 内置指令。`ng-app` 指令的作用是定义 Angular 应用程序的根。
定义它的元素,以及该元素包含的所有元素,都将成为 Angular 应用的一部分。通常,我们将其放在 `` 元素本身上,以便完整的 HTML 页面成为 Angular 应用程序的一部分。可以只写 `ng-app` 而不带任何参数,这将以自动模式引导 Angular 应用程序。但是,这样做并不好,因为它在管理依赖关系和指定应用程序的起点时有点棘手。
相反,我们应该做的是传递应为此页面使用的 Angular 模块名称。所以,如果我们有一个名为 `myAngularApplication` 的模块。那么 `ng-app` 指令应该如下所示
上面的代码将做的是,它会查看 `ng-app` 指令并初始化 Angular 应用程序。然后它会查找 Angular 模块 `myAngularApplication` 并将其视为应用程序的起点。手动引导应用程序的 `ng-app` 指令是有益的,因为它使我们有机会指定可以作为入口点的模块,并且可以轻松使用依赖于指定模块的所有组件(控制器、服务、过滤器等)。
ng-controller
在上一篇文章中,我们也看过 `ng-controller` 指令。每当我们想将任何视图与任何控制器关联时,我们都可以使用 `ngController` 指令将控制器附加到视图。
`ngController` 指令将视图与指令中定义的控制器关联起来。因此,在上面的代码中,`booksController` 将与此 HTML 文档的主体相关联。包含 `ngController` 指令的 HTML 元素可以访问控制器中定义的所有属性。我们在上面的代码中访问的 `{{message}}`,如果它定义在 `booksController` 中,那么 `message` 属性的值将在这里显示。让我们看一下控制器代码,看看 `message` 属性是如何在我们的控制器中的 `$scope` 上定义的。
(function () {
var booksController = function ($scope) {
$scope.message = "Hello from booksController";
this.greeting = "This is a greeting message using controller as syntax";
$scope.books = [];
$scope.fetchBooks = function () {
$scope.books = [
{ ID: 1, BookName: "Test Books 1", AuthorName: "Test Author 1", ISBN: "TEST1" },
{ ID: 2, BookName: "Test Books 2", AuthorName: "Test Author 2", ISBN: "TEST2" },
{ ID: 3, BookName: "Test Books 3", AuthorName: "Test Author 3", ISBN: "TEST3" },
{ ID: 4, BookName: "Test Books 4", AuthorName: "Test Author 4", ISBN: "TEST4" },
{ ID: 5, BookName: "Test Books 5", AuthorName: "Test Author 5", ISBN: "TEST5" }
];
}
}
angular.module('myAngularApplication').controller('booksController', ["$scope", booksController]);
}());
我们在上一篇文章中简要讨论了“控制器即语法”。我们可以使用 `ngController` 指令,通过“控制器即语法”让视图使用控制器代码。“控制器即语法”的作用是让视图可以访问控制器对象上定义的所有属性。
因此,上面的代码将使用控制器的 `greeting` 属性并在视图中显示其值。使用这种方法意味着我们不必将值推送到 `$scope`,而是可以让控制器拥有属性,并通过“控制器即语法”,视图应该能够使用它。
注意:在某些情况下,我们不必显式地在视图中使用 `ngController` 指令来将其与控制器绑定。一个例子是使用路由时,我们可以在路由定义中将控制器与视图关联起来。但我们将在后面的文章中详细介绍单页应用程序(SPA)如何使用 `$route` 或 `uiRouter` 而无需在视图中使用 `ngController` 指令。但对于非 SPA,`ngController` 是将视图与控制器关联的方式。
ng-init
`ngInit` 指令用于初始化 `$scope` 中的数据。`ng-init` 的作用是查看表达式并初始化 `$scope` 变量的值。因此,假设我们想使用 `ng-init` 而不是在控制器中初始化 `$scope.message2` 的值,我们可以这样做:
现在,如果我们运行应用程序,可以看到 `$scope.message2` 是使用 `ng-init` 初始化其值的,并且它的值显示在屏幕上。
但是,我们永远不应该以这种方式使用 `ng-init`,因为它会导致意大利面条式代码。我们将很难确定某个范围变量的值是在控制器中初始化还是在视图中使用 `ng-init` 初始化。`ng-init` 的推荐用法仅用于别名在视图中才需要的代码。一个例子可能是,我们可能在视图中有一个很长的过滤器表达式,并且想在多个地方使用这个过滤器表达式。在这种情况下,我们可以使用 `ng-init` 为该表达式设置别名,并在所有需要的地方在视图中使用该别名。
ng-bind
`ngBind` 指令可用于在视图上显示 `$scope` 的属性。当我们使用表达式 `{{ ... }}` 时,它也等同于 `ng-bind`。因此,如果我们想使用 `ngBind` 指令来显示消息,我们需要在视图中这样做:
如果我们运行页面,仍然可以看到消息像以前一样显示,但这次我们显式地使用了 `ngBind` 指令而不是 `{{ .. }}` 语法。使用 `ngBind` 时要记住的重要一点是,它是一次性绑定,即如果我们使用 `ng-Bind` 将一个 `scope` 属性绑定到一个输入字段,更改输入字段不会改变 `scope` 属性的值。如果我们还需要这种行为,则需要改用 `ngModel`。
注意:如果我们想将原始 HTML 文本绑定到视图,有一个名为 `bgBindHtml` 的指令可供使用。
ng-model
`ngModel` 也与 `ngBind` 非常相似,因为它将作用域属性附加到视图。但最重要区别和也许的好处是,`ngModel` 提供了双向绑定。因此,如果我们使用 `ngModel` 将输入字段附加到作用域属性,更改输入字段的值也会改变作用域属性的值。让我们看看如何在我们的视图中使用 `ngModel`:
现在,如果我们更改 `textbox` 的值,我们也可以看到 `scope` 属性的值也在变化。
ng-repeat
有时我们可能希望遍历一个集合并在视图中显示每个单独的元素。`ngRepeat` 在这种情况下很有用。`ngRepeat` 可以遍历集合的所有项,并在指定的视图格式中显示每个项。让我们看看如何使用它来遍历我们 `$scope.books` 中定义的书籍列表:
上面的代码将使用 `ng-repeat` 指令遍历书籍列表,并将每本书显示在一行中。
ng-options
我们上面看到的 `ngRepeat` 指令可以用来填充下拉列表中的值。但是 Angular 提供了一个专门用于此目的的指令。`ngOptions` 可以轻松地填充下拉列表的选项。假设我们在 `$scope.countries` 变量上有一个国家列表数组:
$scope.countries = [
"India",
"Denmark",
"USA",
"Singapore",
"Germany"
];
现在,如果我们想将此列表显示为下拉列表的选项,我们可以这样做:
上面的代码正在做的是,它使用 `ng-model` 指令将 `select` 列表与 `countries` 属性绑定。然后,我们使用 `ng-options` 指令从 `countries` 集合填充 `select` 列表的选项。如果我们运行它,我们可以看到值作为下拉列表的选项显示出来。
ng-click
当使用 Angular 时,我们不必显式地将点击事件附加到 DOM 元素来处理点击行为。Angular 提供了一个名为 `ng-click` 的指令,它使我们能够轻松处理 DOM 元素上的点击事件。我们可以做的是使用 `ng-click` 指令将一个 `controller` 函数与 `click` 事件关联起来,并且每当发生 `click` 时,`controller` 函数都会被调用。
首先,让我们在作用域(在控制器中)上定义一个简单的函数,该函数将在点击按钮时调用。
$scope.fetchBooks = function () {
$scope.books = [
{ ID: 1, BookName: "Test Books 1", AuthorName: "Test Author 1", ISBN: "TEST1" },
{ ID: 2, BookName: "Test Books 2", AuthorName: "Test Author 2", ISBN: "TEST2" },
{ ID: 3, BookName: "Test Books 3", AuthorName: "Test Author 3", ISBN: "TEST3" },
{ ID: 4, BookName: "Test Books 4", AuthorName: "Test Author 4", ISBN: "TEST4" },
{ ID: 5, BookName: "Test Books 5", AuthorName: "Test Author 5", ISBN: "TEST5" }
];
}
目前,此函数使用硬编码值填充 `$scope.books`。现在让我们看看视图中的 `ng-click` 指令:
现在,当我们单击按钮时,`fetchBooks` 函数将被调用,`$scope.books` 将用预定义的值填充,这些值将显示在屏幕上。
注意:类似于 `ngClick`,还有一些其他指令可以使 DOM 事件处理非常容易。其中一些指令是:`ngDblClick, ngMousedown, ngMouseenter, ngMouseleave, ngMousemove, ngMouseover, ngMouseup, ngKeyPress, ngKeyDown, ngKeyUp, ngBlur, ngFocus`。
ng-show & ng-hide
有两个指令,`ngShow` 和 `ngHide`,它们会评估提供的表达式,然后选择性地显示或隐藏 UI 元素。因此,假设在我们当前的应用程序中,我们想显示一条消息“`没有可用的书籍`”,直到用户单击按钮。我们可以通过使用 `ng-hide` 指令来实现。
上面的代码正在做的是,当书籍数量大于 `0` 时隐藏消息。因此,当我们启动应用程序时,页面将看起来像
现在,假设一旦单击按钮,数量将大于 `0`,现在我们需要以表格形式显示书籍详细信息。让我们看看如何使用 `ng-show` 指令来做到这一点:
现在,当我们单击按钮时,表格将显示书籍列表。
注意:需要注意的是,`ng-show` 和 `ng-hide` 只影响 DOM 元素的可见性。元素仍会渲染在页面上,只是被隐藏了。如果我们想阻止元素本身的渲染,那么我们应该改用 `ngIf` 指令。
ng-style 和 ng-class
如果我们想将 CSS 样式应用于任何 DOM 元素,那么我们可以使用 `ng-style` 指令,例如 `ng-style="{color: mycolor}"`,其中 `mycolor` 是在作用域上定义的变量。`ng-style` 使用实际值来应用 CSS 样式,值来自作用域上的属性。有一个类似的指令称为 `ng-class`,可用于将 CSS 类应用于 DOM 元素。`ng-class` 的工作原理是评估一个布尔表达式,如果表达式为 `true`,则应用该类。因此,如果我们执行类似“`ng-class={myclass: books.length > 0}`”的操作,如果 `books.length > 0` 的计算结果为 `true`,则 `myclass` 将应用于 DOM 元素。然后可以在 CSS 文件中定义此类的实际样式。
看点
在本文中,我们尝试了了解 Angular 指令是什么。我们还了解了 Angular 中的一些内置指令。Angular 中有许多内置指令是我们在这里没有讨论的。此外,还有许多社区创建的指令可供使用。因此,在尝试以老式方式解决任何问题之前,最好检查是否有任何内置或第三方指令可供使用。
本文的目的是通过展示一些常用内置指令的用法,让读者熟悉 Angular 指令的工作方式。在本系列的后续部分,我们还将研究如何自己创建自定义指令。本文是从初学者的角度编写的,希望它能提供信息。
历史
- 2015 年 7 月 6 日:第一版