使用 ES6 JavaScript 和模块创建 AngularJS 应用程序
如何使用 ES6 JavaScript 和模块创建 AngularJS 应用程序
引言
去年年底,我一直在思考未来。未来已来。但有时很难迈出这一步并拥抱未来。学习一项新技能,并掌握到足以用于新项目需要花费大量时间和精力。这就是我一直不愿意学习node.js的原因。它支持的项目开发类型往往需要花费太长时间才能精通。很可能,我需要同时学习五件不同的事情,结果也不会好。学习将分散对开发的注意力。但我希望继续前进,并在开发中使用现代 JavaScript 语言。所以我想,是否可以在不处理 node.js 的情况下,使用基于 ES6 语法的 JavaScript 和 AngularJS 来开发应用程序?事实证明,这是可能的。本教程将讨论如何实现。
这是我想要做的。我想要创建一个单页 AngularJS 应用程序,并使用基于 ES6 的 JavaScript 语法,不使用 node.js。如前所述,这是可行的,而且很容易实现。我确实花了几小时进行研究。总之,本教程将逐步指导你完成实现,并介绍如何使用 ES6 脚本语法编写 AngularJS。
示例应用程序架构
本教程的示例应用程序使用 Spring Boot 创建了一个基于 Web 的应用程序。它所做的只是向用户提供静态内容,如 HTML 页面、样式表和 JavaScript 文件。这是一个单页应用程序,有四个可导航的子页面。第一个子页面是 index,它只是一个静态显示页面。第二个页面将显示控制器中的一个 string
值,以及一个按钮,该按钮的 click
事件由控制器对象的某个方法处理。另外两个页面是 static
的,也仅用于显示目的。
整个想法是展示如何使用 ES6 脚本创建一个 JavaScript 对象。这个控制器(通俗地说)连接到视图(页面模板),并可以在视图和控制器之间交换数据。此外,该应用程序还将展示如何设置页面模板的导航。最后,将这两者结合到一个应用程序中。这些是 AngularJS 应用程序最基本的功能。如果这些可以使用 ES6 脚本完成,那么就可以使用相同的语法编写一个完整的应用程序。难点在于弄清楚如何实现。事实证明,这些都不难。所有需要的就是一点研究。
入口页面
我将从入口页面开始。有一点需要强调。为了在我的应用程序中使用 ES6 脚本语法,我需要将 JavaScript 文件添加为模块,而不是普通脚本文件。让我向你展示区别。这是一个我添加到 HTML 页面的普通脚本文件。
...
<script type="text/javascript" src="/assets/jquery/js/jquery.min.js"></script>
...
为了将 JavaScript 文件添加为模块,我必须这样做。
...
<script type="module" src="/assets/app/js/TestController.js"></script>
...
这是必需的,因为根据文档,Chrome 和 FireFox 可以执行 ES6 语法兼容的脚本,如果该文件被添加为模块。好了。这是使用基于 ES6 语法的第一个步骤。
让我向你展示这个入口页面的其余部分。我们之所以称这些 AngularJS 应用程序为单页应用程序,是因为只有一个页面,这个页面的内容会根据放置在这个页面上的模板而改变。这个入口页面是位于 src/main/resources/static/ 子目录下的 index.html 文件。有一个非常基本的导航菜单。
<div class="row">
<div class="col-0xs-12">
<ul class="nav nav-pills">
<li role="presentation"><a href="./#!/">Home</a></li>
<li role="presentation"><a href="./#!/red">Red Page</a></li>
<li role="presentation"><a href="./#!/green">Green Page</a></li>
<li role="presentation"><a href="./#!/blue">Blue Page</a></li>
</ul>
</div>
</div>
这是一个非常简单的导航菜单,只有 4 个链接。如果你是第一次使用 AngularJS,导航的工作方式是使用像这样的链接:“./#!/<angularjs-links>”,即“./#!/blue”。这些是 AngularJS 路由可以识别并正确渲染页面的 URL 重写规则。
由于我们使用的是 AngularJS 路由而不是花哨的 ui-router(一个与 AngularJS 配合使用的第三方组件),我们需要指定一个区域来渲染子页面。这是如何做到的。
<div class="row">
<div class="col-xs-12">
<div class="panel panel-default">
<div class="panel-body">
<ng-view></ng-view>
</div>
</div>
</div>
</div>
页面将作为 <ng-view/>
标签的子元素渲染。
每个 AngularJS 应用程序都有一个入口点,一个被指定为 ngApp
的 ngModule
。这在 HTML 页面的顶部定义,如下所示。
<div class="container top-margin" ng-app="startup">
...
</div>
最后,我将 JavaScript 文件和模块添加到这个网页。就像这样。
...
<script type="text/javascript" src="/assets/jquery/js/jquery.min.js"></script>
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript" src="/assets/angularjs/1.7.5/angular.min.js"></script>
<script type="text/javascript" src="/assets/angularjs/1.7.5/angular-resource.min.js"></script>
<script type="text/javascript" src="/assets/angularjs/1.7.5/angular-route.min.js"></script>
<script type="module" src="/assets/app/js/app.js"></script>
...
前几行是为 AngularJS 源代码添加的 JavaScript 文件。最后一行是我 AngularJS 应用程序入口的模块。我不用列出所有其他模块,因为这个文件和随后的模块文件都是相互连接的。它们相互导入并创建引用链接,所以不需要在这里添加每个 JS 文件。
除了这个基础 HTML 文件,还有四个 HTML 文件,每个文件代表一个子页面。其中一个也称为 index.html。它是默认显示的页面,其他页面的名称是一个颜色,名为“red”的页面包含一些交互式元素,以演示如何为页面创建控制器。另外两个页面是静态的。它们用于演示路由功能。
在下一节中,我将向你展示我的 JavaScript 模块代码是如何工作的。这包括应用程序入口、路由配置和测试控制器。
应用程序模块
每个 AngularJS 应用程序都必须有一个定义的模块,并被指定为 ngApp
的值。我已经展示了如何做到这一点。让我再次向你展示。
<div class="container top-margin" ng-app="startup">
...
</div>
这个“startup
” ngModule
定义在名为 app.js 的文件中。其内容如下。
import { appRouting } from '/assets/app/js/app-routing.js';
import { TestController } from '/assets/app/js/TestController.js';
let app = angular.module('startup', ["ngRoute"]);
app.config(appRouting);
app.controller("TestController", TestController);
正如你所见,这看起来与我过去编写的 JavaScript 代码有些不同。这个文件代表了一个 ES6 意义上的模块。我使用新的 ES6 语法将对象(类对象或函数)从一个模块导入到当前模块。前两行是从其他模块导入单个对象。第一行从模块“/assets/app/js/app-routing.js”导入一个名为 appRouting
的函数。第二行导入一个类 TestController
的对象。最后三行是将这些对象注入到 AngularJS 的启动调用过程中。第一行创建一个名为“app
”的对象。它是一个 AngularJS 模块。该模块名为“startup
”。这是 HTML 页面上 ngApp
引用的名称。“app
”调用了两个方法。第一个称为 .config()。这是我传递 appRouting
函数(从 app-routing.js 导入)的地方。这基本上是让 AngularJS 调用我的函数来设置子路径路由。最后一行是让“app
”对象调用 .controller()
函数来注册名为“TestController
”的控制器。这就像一个 IOC 容器,将该对象添加为一个控制器,以便视图(HTML 页面)和 AngularJS 内部能够正确引用该控制器。这个模块没有什么特别之处,所有内容都是应用程序启动的配置。
接下来,我将向你展示 appRouting
函数是如何定义的。为了为应用程序设置子页面导航,我需要将 ngRoute
模块中的一个 AngularJS 组件 $routeProvider
注入到我的 appRouting()
函数中。在定义模块 app
时,该模块已在 app.js 中注入。这里是。
...
let app = angular.module('startup', ["ngRoute"]);
...
我的“appRouting
”的整个源代码如下。
export function appRouting($routeProvider) {
$routeProvider.when("/", {
templateUrl : "/assets/app/pages/index.html"
});
$routeProvider.when("/red", {
templateUrl : "/assets/app/pages/page1.html",
controller: "TestController",
controllerAs: "vm"
});
$routeProvider.when("/green", {
templateUrl : "/assets/app/pages/page2.html"
});
$routeProvider.when("/blue", {
templateUrl : "/assets/app/pages/page3.html"
});
}
除了依赖注入的工作方式之外,这个函数非常简单。我所做的就是创建一个名为 appRouting
的函数。它接受一个名为 $routeProvider
的参数。这个参数看起来有点奇怪。我特意这样声明它,这里就是依赖注入发生的地方。如果我将参数命名为它本身,那么依赖注入就会正确发生。如果我使用不同的名称,当这个参数第一次使用时,将会发生一个 null
引用异常。
我调用 $routeProvider.when()
方法来设置子页面导航。此方法接受一个对象作为参数。该对象的属性决定了子页面的行为。有四个调用,用于设置四个不同的子页面。其中三个看起来相似。所有对象都只包含一个名为“templateUrl
”的属性。我在这里做的是指定 AngularJS 将这些三个页面显示为静态页面。还有另一个调用,参数包含三个属性:“templateUrl
”、“controller
”和“controllerAs
”。“templateUrl
”属性指定了模板页面的文件位置。“controller
”属性指定了该页面的 AngularJS 控制器的名称。“controllerAs
”属性指定了在模板页面中引用控制器时使用的别名。 AngularJS 的最新版本倾向于使用别名来访问控制器类的模型属性。如果你不想使用这个,那么替代方法是使用“$scope
”来访问控制器模型属性。这已经不再推荐了。
export
关键字用于导出函数,以便其他 JavaScript 模块可以导入它。现在子页面导航配置已经讨论完毕,接下来,我们将讨论 TestController
的设计。
对于这个应用程序,我只需要创建一个非常简单的应用程序,其中有一个页面与一个控制器相关联。控制器应该有几个属性,它们是模型属性(可以在页面上使用),可以在页面上进行操作。然后会有一个按钮,用户可以点击该按钮并在页面上看到响应。这些应该足以演示使用 ES6 语法设计控制器的想法。
对于这个控制器,我将设计一个简单的加法计算器。将有两个输入字段供用户输入整数值。然后将有另一个输入字段显示这两个输入值的总和。如果用户输入了无效值,那么显示总和的输入字段将重置为 null
,然后将显示警告。为了进行输入验证和计算总和,用户将点击名为“Calculate”的按钮。
这是控制器的完整源代码。
export class TestController {
constructor() {
this._value1 = "";
this._value2 = "";
this._sumValue = null;
}
get value1 () {
return this._value1;
}
set value1 (val) {
this._value1 = val;
}
get value2 () {
return this._value2;
}
set value2 (val) {
this._value2 = val;
}
get sumValue () {
return this._sumValue;
}
set sumValue (val) {
this._sumValue = val;
}
clickIt() {
let intVal1 = parseInt(this.value1);
let intVal2 = parseInt(this.value2);
if (isNaN(intVal1) || isNaN(intVal2)) {
alert("The integer values entered is/are invalid. Please correct.");
this.sumValue = null;
return;
}
this.sumValue = intVal1 + intVal2;
}
}
这个类非常简单。它有三个属性和一个 public
方法。让我向你展示它们是什么。首先,这些是属性,它们在类的构造函数中声明和初始化。
...
constructor() {
this._value1 = "";
this._value2 = "";
this._sumValue = null;
}
...
暴露这些属性的一种好方法是通过 getter 和 setter(也称为访问器)。这些 getter 和 setter 是。
...
get value1 () {
return this._value1;
}
set value1 (val) {
this._value1 = val;
}
get value2 () {
return this._value2;
}
set value2 (val) {
this._value2 = val;
}
get sumValue () {
return this._sumValue;
}
set sumValue (val) {
this._sumValue = val;
}
...
最后,这个类的 public
方法用于处理视图上的按钮点击事件。这是。
...
clickIt() {
let intVal1 = parseInt(this.value1);
let intVal2 = parseInt(this.value2);
if (isNaN(intVal1) || isNaN(intVal2)) {
alert("The integer values entered is/are invalid. Please correct.");
this.sumValue = null;
return;
}
this.sumValue = intVal1 + intVal2;
}
...
仔细看看这个方法。它首先尝试将文本字段的值解析为整数。然后它检查解析后的值,确保它们是有效的数字。如果它们不是有效的数字,将会弹出一个警报框,总和值将被设置为空,并且不会执行进一步的操作。如果值有效,将计算两个值的总和并分配给 sumValue
属性。一旦成功分配值,总和的文本字段将立即更新。
如所示,类本身很简单。那么,它如何在 AngularJS 示例应用程序的视图中使用呢?事实证明,使用也非常简单。这是视图页面的源代码。
<div class="row">
<div class="col-xs-12 col-sm-offset-2 col-sm-8 col-md-offset-3 col-md-6">
<p><input ng-model="vm.value1"> + <input ng-model="vm.value2"> =
<input ng-model="vm.sumValue" readonly> <button class="btn btn-primary"
ng-click="vm.clickIt()">Calculate</button></p>
</div>
</div>
<div class="row">
<div class="col-xs-3" style="background-color: red;">
</div>
</div>
这些属性的引用方式与旧方式相同。我已经将控制器对象指定为“vm
”。因此,这些属性可以在视图上作为“vm.<propertyName>
”来引用。这里是将属性绑定到 Value #1、Value #2 和 Sum 的文本字段的方式。
...
<input ng-model="vm.value1"> + <input ng-model="vm.value2"> =
<input ng-model="vm.sumValue" readonly>
...
这里的标记是为了设置值 #1 加上值 2 的显示,以获得这两个值的总和。还有一个按钮可以点击来计算这两个值的总和。点击后,这两个值将被转换为整数,然后这两个值的总和存储在 sumValue
属性中。这将导致显示总和的文本字段。此按钮的定义如下。
...
<button class="btn btn-primary" ng-click="vm.clickIt()">Calculate</button>
...
如何测试示例应用程序
下载示例源代码后,请将其解压缩到安全位置。然后将所有 JavaScript 文件从 *.sj 重命名为 *.js。然后就可以编译和打包了。请注意,自今年年初以来,我已从 Java 8 切换到 Java 14。请使用 Java 14 或修改 POM 文件,以便项目可以使用 Java 8 进行编译。
使用以下命令编译和打包应用程序。
mvn clean install
编译和打包成功后,使用以下命令启动应用程序。
java -jar target/hanbo-angular-es6sample2-1.0.1.jar
应用程序成功启动后,您可以使用以下 URL 在浏览器中运行该应用程序。请使用 Chrome 或 Firefox 来运行此应用程序。我相信这也可以在基于 WebKit 的 Internet Explorer 中运行。这个从未经过测试。页面显示后,您将看到与第一个截图相同的页面显示。我在这里再次发布这张截图。
截图显示的是此应用程序的 index 子页面。还有三个页面,“Red Page”、“Green Page”和“Blue Page”。“Green Page”和“Blue Page”与 index 页面类似,它们是静态的。“Red Page”是包含加法计算逻辑的页面。这是“Red Page”的截图。
这是“Green Page”的截图。
这是“Blue Page”的截图。
就是这样。玩得开心!
摘要
又一篇教程完成了。我很享受这一篇。这是一个简单的 Web 应用程序。我设计它的目的是演示 ES6 的新语法,并将其与 AngularJS 集成。对于这个应用程序,我有两个关注点,一是弄清楚如何设置导航路由,二是弄清楚如何使用新语法配置和使用 AngularJS 控制器。经过一些研究,这两者都得到了解决,而且麻烦不大,正如我在本教程中所展示的。
然而,可以从本教程衍生出更多的教程。最常见的用法是在 AngularJS 中使用 factory 来创建服务对象。我可能会为此做一个小型教程。使用 AngularJS 创建 directive/component 也是一种常见的用法,这可以成为另一篇教程。我想我可以在今年晚些时候推出的一个教程中结合这两者,并使用 ES6 模块。总之,从现在到今年年底,我将发布许多优质教程,请经常查看我的 CodeProject 个人资料页面以获取新教程。感谢您的阅读!
历史
- 2021 年 3 月 9 日:初稿