AngularJS 入门 - 第一部分






4.72/5 (15投票s)
本文将演示如何开始使用 AngularJS 开发网站。
引言
在本文中,我们将了解如何使用 AngularJS 构建网站。本文面向希望开始使用 AngularJS 的初学者,并解释了 AngularJS 框架的一些基本功能。我将以一个简单的产品 CRUD 示例进行演示。我解释了本演示中使用的所有代码片段,然后您可以下载源代码,亲自体验 AngularJS 的实际应用。
背景
AngularJS 是当今广泛使用的 JavaScript MVC 框架,它提供了一种强大的机制,可以更快、更轻松地开发响应式网站。作为 MVC 框架,它为您提供了将网站代码分发到三个组件(即模型、视图和控制器)的方式。因此,数据模型、应用程序逻辑(控制器)和视图呈现之间有明确的分离,使您可以轻松专注于主要的开发领域。视图从模型接收数据以供显示。当用户通过单击或输入与应用程序交互时,控制器通过更改模型中的数据来响应。最后,视图会收到模型中发生更改的通知,以便它可以更新显示的内容。
在 Angular 应用程序中,视图是文档对象模型,控制器是 JavaScript 类,模型数据存储在对象属性中。AngularJS 将 HTML 模板和数据发送到浏览器,在客户端进行组装。使用 jQuery 等库,您必须与 DOM 交互才能使用新的模型状态更新 UI,即,每当您观察到模型中的任何更改时,您都必须与 DOM 交互以反映更改。而 AngularJS 为我们提供了数据绑定,我们不必将数据从一个部分移动到另一个部分,我们只需将 UI 部分与 JavaScript 属性进行映射,它就会自动同步。
Using the Code
让我们从代码开始,我们将从头开始逐步构建一个示例 CRUD 操作示例。
在 html
标签中添加 index.html 页面,并带有 ng-app
属性,如下所示:
<html ng-app="demoApp">
这将把我们的 html 页面定义为 angularjs 应用程序。由于 angularjs 主要是一个单页应用程序框架,它将使用 html 视图模板作为对特定路由的局部视图。为了渲染局部视图,我们对 div
标签使用 data-ng-view
属性,我们所有的局部视图都将在此 div
标签内渲染。
<div data-ng-view=""></div>
下载 angular.js 和 angular-route.js 两个文件,并在 index.html 页面中添加对这些文件的引用。或者,您可以直接添加对 https://angularjs.org/ 站点的引用,如下所示:
<script type="text/javascript"
src="http://code.angularjs.org/1.2.13/angular.js"></script>
<script type="text/javascript"
src="http://code.angularjs.org/1.2.13/angular-route.js"></script>
部分视图 (Partial Views)
现在转到我们 CRUD 示例的真实内容,我们将开发两个局部视图,ProductListing.html 和 ProductEdit.html。让我们回顾一下 ProductListing
html
<div class="container">
<h2 class="page-title">Products Listing</h2>
<div class="searchbar">
<ul class="entity-tabular-fields">
<li>
<label>Search: </label>
<span class="field-control">
<input type="text" data-ng-model="filter.productName" />
</span>
<label></label>
</li>
</ul>
</div>
<h2><a href="#/ProductEdit">Add new product</a></h2>
<table class="items-listing">
<thead>
<tr>
<th>Code
</th>
<th>Name
</th>
<th>Description
</th>
<th>Category
</th>
<th>Action
</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="product in products | filter:filter.productName">
<td><a href="#/ProductEdit?code=
{{product.code}}">{{product.code}}</a></td>
<td>{{product.name}} </td>
<td>{{product.description}} </td>
<td>{{product.category}} </td>
<td><a href="#/?code={{product.code}}">Delete</a></td>
</tr>
</tbody>
</table>
</div>
除了 html 或 CSS 内容之外,请关注 angular 指令,在 <tbody>
标签中,我们使用指令 data-ng-repeat="product in products | filter:filter.productName"
。其中“products
”是我们的 JavaScript 数组,在这里充当内存数据存储。此指令告诉 JavaScript 遍历每个数组项并为每个单独的项生成 <tr>
标签。然后,我们添加了一个过滤器,以根据任意过滤器对象中的 productName
过滤我们的项目,您可能已经注意到,这是我们使用数据绑定指令 data-ng-model="filter.productName"
将搜索文本框绑定到此 ProductListing
视图上半部分的那个对象属性,因此用户在此搜索框中键入的任何内容,产品数组都将根据匹配的字符串进行过滤。要将任何值输出到 html,我们使用 angular 的数据绑定语法 {{VAULE_TO_BE_OUTPUT}}
,就像我们在表格单元格中显示产品属性一样
<td>{{product.name}} </td>
对于第一个单元格,我们显示 product.code
并将其设为指向我们的第二个局部视图 ProductEdit.html 的链接。最后一个单元格还包含一个指向同一个列表页面的链接,查询字符串中包含产品代码。我们稍后将查看模型/控制器的 JavaScript 代码,让我们首先回顾 ProductEdit
局部视图。这是 ProductEdit.html 的内容
<div class="container">
<h2 class="page-title">Product Edit</h2>
<br />
<ul class="entity-tabular-fields">
<li class="entity-field-row">
<label>Product Code: </label>
<span class="field-control">
<input type="text" data-ng-model="currentProduct.code" />
</span>
<label></label>
</li>
<li class="entity-field-row">
<label>Product Name: </label>
<span class="field-control">
<input type="text" data-ng-model="currentProduct.name" />
</span>
<label></label>
</li>
<li class="entity-field-row">
<label>Description: </label>
<span class="field-control">
<input type="text"
data-ng-model="currentProduct.description" aria-multiline="true"/>
</span>
<label></label>
</li>
<li class="entity-field-row">
<label>Category: </label>
<span class="field-control">
<input type="text" data-ng-model="currentProduct.category" />
</span>
<label></label>
</li>
<li class="entity-field-row">
<label></label>
<span class="field-control">
<button data-ng-click="saveProduct()">Save</button>
</span>
<label></label>
</li>
</ul>
</div>
请注意我们为所有输入标签添加的 data-ng-model
属性,这会将模型(产品)属性与 UI 元素绑定。在这种情况下,它会自动创建一个名为 currentProduct
的新产品对象,并用用户在这些输入控件中输入的值填充其属性。最后,您会看到一个带有 data-ng-click
属性的按钮标签,它将按钮的点击事件与当前 $scope
内的 saveProduct()
函数绑定。现在您看到了 $scope
,您可以将其视为一个特殊的 JavaScript 对象,我们当前视图所需的所有对象/方法都绑定到 $scope
对象,我们可以在 $scope
对象中声明的任何内容都可以从我们的视图中访问。当我们继续使用模型/控制器代码时,您会更清楚。
JavaScript / Angular 部分
现在让我们转到代码的 JavaScript 部分,之前您添加了两个 AngularJS 库的引用,现在添加另一个 JavaScript 文件,比如说 main.js,并将其引用添加到同一个 index 页面。您可以下载所附代码,我在这里继续讨论小部分内容。
首先,通过调用 angular.module
声明我们的应用程序对象。
var demoApp = angular.module('demoApp', ['ngRoute']);
第一个参数 'demoApp'
是我们定义的模块名称,第二个参数是模块所需的任何依赖项数组,在本文的范围内,我们只有一个依赖项 'ngRoute'
。
接下来,我们为局部视图配置路由
// Setup config routes
demoApp.config(function ($routeProvider) {
$routeProvider
.when('/',
{
controller: 'ProductController',
templateUrl: 'partials/ProductListing.html'
})
.when('/ProductEdit',
{
controller: 'ProductController',
templateUrl: 'partials/ProductEdit.html'
})
.otherwise({
redirectTo: '/'
});
});
参数 $routeProvider
传递给路由配置函数,然后使用一系列 .when()
函数查找 URL 路径并传递一个对象来定义目标局部视图及其关联的控制器。controller
属性定义了 templateUrl
属性中指定的局部视图将使用哪个控制器。在我们的示例中,我们只使用一个控制器,即 ProductController
。otherwise()
函数定义了如果没有指定路径与请求的 URL 路径匹配时要渲染的默认局部视图。
是时候定义 ProductController
了,在 AngularJS 中,您可以通过多种方式定义控制器,我们正在使用一个 controllers
对象,将我们所有的 controllers
添加到此对象,然后最终将此对象设置到我们应用程序模块的控制器中。
// Define Controllers object
var controllers = {};
//add no.of controllers to this object, we are only using one controller: ProductController.
controllers.ProductController = function ($scope, $route, $routeParams, $location, ProductFactory) {
}
// Assign Controllers object to the app module
demoApp.controller(controllers);
这是我们 ProductController
的定义
controllers.ProductController = function ($scope, $route, $routeParams, $location, ProductFactory) {
$scope.products = [];
var init = function () {
$scope.products = ProductFactory.getProducts();
};
var initProductEdit = function () {
var code = $routeParams.code;
if (code == undefined) {
$scope.currentProduct = {};
}
else {
$scope.currentProduct = ProductFactory.loadProductByCode(code);
}
};
$scope.$on('$viewContentLoaded', function () {
var templateUrl = $route.current.templateUrl;
if (templateUrl == "partials/ProductEdit.html") {
initProductEdit();
}
else if (templateUrl == "partials/ProductListing.html") {
var code = $routeParams.code;
if (code != undefined) {
$scope.deleteProduct(code);
}
}
});
init();
$scope.saveProduct = function () {
ProductFactory.saveProduct($scope.currentProduct);
$location.search('code', null);
$location.path('/');
};
$scope.deleteProduct = function (code) {
ProductFactory.deleteProduct(code);
$location.search('code', null);
$location.path('/');
};
};
每个控制器都必须传递一个名为 $scope
的参数,视图使用它来访问数据模型。您在 $scope
对象中定义的任何内容都可以直接从视图中访问。还有一些其他可选参数 $route
、$routeParams
、$location
,如果您需要的话可以使用它们。
我们在该控制器中添加了支持函数,以便与 ProductFactory
交互,并通过将其添加到 $scope
对象来使这些函数可从视图访问。我们使用了一个事件 '$viewContentLoaded'
,当任何局部视图加载到我们的 div
标签中时,就会触发此事件。由于我使用 ProductEdit
页面执行添加/编辑任务,因此我为要编辑的产品代码放置了查询字符串参数。我在此事件中检查,如果存在查询参数 code
,则视图处于编辑模式,否则它将创建一个新产品。对于 ProductListing
视图上的删除任务也使用了类似的技术,即,如果查询字符串参数中存在 code
,则我们将删除该产品并刷新列表表格。
您可能在控制器函数中注意到的另一个参数是 ProductFactory
,这是我们需要从控制器访问数据的服务组件。Angular 框架允许以不同的方式创建服务组件。最常见的方式是使用工厂创建它。我们需要在应用程序模块中添加该服务,该模块传递两个参数:服务的名称和工厂函数。它是一个返回新对象的简单函数。这是 ProductFactory
的定义
// Define Factory
demoApp.factory('ProductFactory', function () {
//this is our proudcts array containing items, here we can change to get data from the server.
var products = [
{ code: 'P001', name: 'Laptop',
description: 'Laptop - description', category: 'Computers' },
{ code: 'P002', name: 'Smartphone',
description: 'Smartphone - description',
category: 'Computers' },
{ code: 'P003', name: 'Mobile',
description: 'Mobile - description', category: 'Computers' },
{ code: 'P004', name: 'Angular Basics',
description: 'Angular Basics - description',
category: 'Books' },
{ code: 'P005', name: 'ng-book - The Complete Book on AngularJS',
description: 'ng-book - The Complete Book on AngularJS - description',
category: 'Books' },
{ code: 'P006', name: 'AngularJS Essentials',
description: 'AngularJS Essentials - description',
category: 'Books' }
];
var factory = {};
factory.getProducts = function () {
return products;
};
factory.loadProductByCode = function (code) {
var productFound;
for (var i = 0; i < products.length; i++) {
if (products[i].code == code) {
productFound = products[i];
break;
}
}
return productFound;
};
factory.saveProduct = function (product) {
var tempProduct = factory.loadProductByCode(product.code);
if (tempProduct == null || tempProduct == undefined) {
tempProduct = {};
tempProduct.code = product.code;
tempProduct.name = product.name;
tempProduct.description = product.description;
tempProduct.category = product.category;
products.push(tempProduct);
}
else {
tempProduct.code = product.code;
tempProduct.name = product.name;
tempProduct.description = product.description;
tempProduct.category = product.category;
}
};
factory.deleteProduct = function (code) {
var tempProduct = factory.loadProductByCode(code);
if (tempProduct != null) {
products.remove(tempProduct);
}
};
return factory;
});
我们使用模块的 factory
函数来定义工厂服务组件。代码是自解释的,它只包含一些辅助属性/函数。代码列表末尾的 deleteProduct
函数使用的是数组函数 remove,它不是一个真正的默认数组函数,我使用了一个自定义函数将其添加到 Array.prototype
以便在网站中的任何位置都可以访问它。请确保在应用程序加载开始时,在任何相关代码执行之前完成此操作,对于本文,我将其放在 index.html 脚本标签中。
在工厂组件中,我们通常需要从服务器访问数据,但就本文而言,我们仅使用临时内存中的产品数组。希望在下一篇文章中,我将更改其代码以从服务器数据库访问数据。
关注点
我们已经完成了使用 AngularJS 的 CRUD 示例,我非常喜欢写下我从 Angular 学到的经验。我希望您也喜欢这篇文章并从中有所收获。
我计划写另一篇文章,并尝试使用服务器端数据存储并从 AngularJS 向服务器发出请求。我感谢您的反馈/评论或您希望在此主题中提出的任何改进,以帮助使文章更好并对其他人有所帮助。
历史
- 2015年10月14日:文章发布