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

AngularJS 入门 - 第一部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.72/5 (15投票s)

2015年10月15日

CPOL

8分钟阅读

viewsIcon

24057

downloadIcon

562

本文将演示如何开始使用 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.jsangular-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.htmlProductEdit.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 属性中指定的局部视图将使用哪个控制器。在我们的示例中,我们只使用一个控制器,即 ProductControllerotherwise() 函数定义了如果没有指定路径与请求的 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日:文章发布
AngularJS 入门 - 第一部分 - CodeProject - 代码之家
© . All rights reserved.