边实战边理论,入手AngularJS(Ⅰ)

前言



钻研ABP框架的日子,遇到了很多新的知识,因为对自己而言是新知识,所以经常卡在很多地方,迟迟不能有所突破,作为一个稍有上进心的程序员,内心绝对是不服输的,也绝对是不畏困难的,心底必然有这样一股力量“I must conquer it!”。比如,以前没用过AutoMapper,那我就去学,最后将学到的写成了博客,和大家一起分享,争取让那些还没接触的人少走弯路。现在,在前端设计时,ABP很多用到的都是AngularJS,而之前做项目都是用的JQuery,所以继续研究ABP也是相当有阻力。但是,不能因为有阻力就拖延,最终导致半途而废吧!因而,还是得学,毕竟技多不压身嘛!况且,JQuery是MPA【经典】,而AngularJS是SPA【年轻】,这两种框架都掌握了,也算是挺圆满的吧。

现在回到正题,这篇博客,我们将会看到如何使用AngularJS创建Web应用。对初学者而言,涵盖并解释一些AngularJS框架的基本特征。我设计了一个简单的关于产品的CRUD的例子,解释了演示中所有的代码片段。

背景



当前,AngularJS作为Javascript的MVC(也有人说是MV*,暂且不纠结这个)框架被广泛使用,它为更快且更容易地开发响应式的Web提供了强大的机制。作为MVC框架,它将Web前端代码分成三个组件Model,View和Controller。因此,在data model,应用逻辑(Controllers)和view展示之间有明确的分离,让你更容易地关注关键的开发区域。view接收来自model的数据来展示。当用户通过点击或者敲击键盘和应用交互时,controller通过改变模型中的数据进行响应。最终,view得到了发生在model中的变化这个通知,从而它能更新展示的内容。

在Angular应用中,view是DOM(文档对象模型),controller是javascript类,model数据存储在对象属性中。AngularJS将集成在客户端的html和数据传输给浏览器。就像Jquery库一样,为了使用新鲜的model状态更新UI,你必须和DOM交互。例如,无论你何时观察到model中的任何改变,你都必须和DOM交互来影响这个改变(如果以前经常使用Jquery,那么这句话不难理解)。然而,AngularJS提供了数据绑定,我们不必将数据从一个地方移到另一个地方,只需要使用javascript属性映射UI部件,然后它就会自动同步了。

使用代码


源代码下载

现在开始写代码,让我们一步一步地创建这个简单的CRUD操作案例。

添加一个index.html页面,并添加一个属性ng-app属性,例如:

<html ng-app="demoApp">

这句代码就将我们的应用定义为AngularJS应用。因为AngularJS是SPA(单页面应用)框架,所以它会使用html视图模板作为分部视图来响应确定的路由。为了渲染一个分部视图,我们会给一个div标签添加一个ng-view属性,所有的分部视图都会渲染在这个div标签中。

<div ng-view=""></div>

下载两个文件angular.js 和angular-route.js,在index.html中添加这些文件的引用。或者你也可以用CDN的静态资源,比如百度CDN的静态资源http://apps.bdimg.com/libs/angular.js/1.4.6/angular.js

<script src="../Scripts/angular.min.js"></script>
<script src="../Scripts/angular-route.min.js"></script>

分部视图



现在来点我们CRUD案例的真实的内容,我们会开发两个分部视图,ProductList.html和ProductEdit.html。先来看一下ProductList.html的代码:

<div class="container">
    <h2 class="page-title">产品列表</h2>

    <div class="searchbar">
        <ul class="entity-tabular-fields">
            <li>
                <label>搜索:</label>
                <span class="field-control">
                    <input type="text" ng-model="filter.productName" value=" " />
                </span>
                <label></label>
            </li>
        </ul>
    </div>

    <h2><a href="#/ProductEdit">新增产品</a></h2>

    <table class="items-listing">
        <thead>
            <tr>
                <th>代码</th>
                <th>名称</th>
                <th>描述</th>
                <th>类别</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            <tr 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}}">删除</a></td>
            </tr>
        </tbody>
    </table>
</div>

效果图:

除了html和css,更要关注angular指令的使用。在<tbody>中,我们使用了指令ng-repeat="product in products | filter:filter.productName"。products是一个javascript数组,它是存储在内存中的。该指令告诉javascript循环遍历数组中的每个元素(如果你使用过C#的话,那么它就相当于C#中的foreach),并且为每个元素生成一个<tr>标签。接下来,我们添加了过滤器,该过滤器根据productName过滤出我们搜索条件中要求的一些元素,也许正如你看到的,它是一个对象属性,该属性通过数据绑定指令ng-model="filter.productName"绑定在搜索框上。因此无论你在搜索框中输入什么,产品数组都会使用匹配的字符串过滤。为了输出一些值到html上,我们使用angular的数据绑定语法{{output_Expression}},例如表格单元的产品属性:

<td>{{product.name}}</td>

对于第一个单元格,我们显示了product.code,它会链接到第二个分部视图ProductEdit.html。

最后一个单元格也包含了一个到列表展示页面的链接,它会使用产品代码product.code作为查询字符串。后面我们会看到models/controllers的Javascript代码。现在看一下ProductEdit.html代码:

<div class="container">
    <h2 class="page-title">产品编辑</h2>
    <br/>

    <ul class="entity-tabular-fields">
        <li class="entity-field-row">
            <label>产品代码:</label>
            <span class="field-control">
                <input type="text" ng-model="currentProduct.code"/>
                <label></label>
            </span>
        </li>
        <li class="entity-field-row">
            <label>产品名称:</label>
            <span class="field-control">
                <input type="text" ng-model="currentProduct.name" />
                <label></label>
            </span>
        </li>
        <li class="entity-field-row">
            <label>产品描述:</label>
            <span class="field-control">
                <input type="text" ng-model="currentProduct.description"/>
            </span>
            <label></label>
        </li>
        <li class="entity-field-row">
            <label>产品种类:</label>
            <span class="field-control">
                <input type="text" ng-model="currentProduct.category"/>
            </span>
            <label></label>
        </li>
        <li class="entity-field-row">
            <span class="field-control">
                <button ng-click="saveProduct()">保存</button>
            </span>
        </li>
    </ul>
</div>

效果图:

注意我们给所有的input标签加了ng-model属性,这个将model(product)属性和相应的UI元素绑定在一起。在这个案例中,会自动地创建一个新的叫做“currentProduct”的产品对象,并且使用用户在文本框中输入的值生成该对象的属性。最后,在button标签中添加了一个ng-click的属性,该属性绑定了按钮的点击事件saveProduct,该事件在当前的$scope中。这个$scope,你可以把它认为一个特殊的Javascript对象,我们当前的视图所有必须的对象或者方法都绑定在$scope上,我们可以从视图中访问在$scope中声明的任何东西。随着我们继续推进model或者controller代码,你会更清楚的。

Javascript/Angular 部分



现在,该看看Javascript部分的代码了。之前已经在index.html中添加了两个AngularJS库的引用,现在再添加一个Javascript文件,我们命名为“App.js”,并将该文件的引用添加到index页面中。

首先,通过调用angular.module声明我们的应用对象。

var demoApp = angular.module(‘demoApp‘, [‘ngRoute‘]);

第一个参数“demoApp”是我们定义的模块名称,第二参数是我们的模块必须要有的依赖数组,当前博客的案例中,我们只依赖“ngRoute”。

现在我们为分部视图配置路由:

//配置路由
demoApp.config(function($routeProvider) {
    $routeProvider
        .when(‘/‘, {
            controller: "ProductController",
            templateUrl: "partials/ProductList.html"
        })
        .when(‘/ProductEdit‘, {
            controller: "ProductController",
            templateUrl: "partials/ProductEdit.html"
        })
        .otherwise({
            redirectTo: ‘/‘
        });
});

参数$routeProvider传到路由配置的函数中,然后使用了一个链式的when函数来寻找url路径,并且传递一个对象来定义和controller相联系的目标视图。Controller属性定义了在templateUrl属性中指定的分部视图会使用哪一个控制器。我们的例子中只使用了一个控制器ProductController。otherwise()函数的作用是,如果没有指定的路径匹配到请求的url路径,就会匹配的默认视图。可以联想一下switch…case…default语法。

是时候定义ProductController了,在AngularJS中,有多种方式定义控制器。我么现在使用一个controllers对象,然后把所有的controller加到这个对象中,最后将这个对象配置给我们app模块的controllers属性。

每一个controller都必须传递一个$scope参数,视图要使用它来访问data model中的数据。在$scope中定义的任何数据都可以在视图中直接访问。如果需要的话也可以使用一些其他可选的参数$route, $routeParams, $location

在这个控制器中,我们添加了和ProductFactory交互的支持函数,通过添加到$scope对象中使得这些函数在视图中可以使用。我们用到了一个事件“$viewContentLoaded”,这个事件在分部视图加载到div标签中时触发。因为我们对于添加和编辑任务都是用的ProductEdit页面,因此我将被编辑的产品的code作为查询字符串参数。然后在这个事件中检测查询参数code,如果有此参数,视图就是编辑模式,否则就是一个创建新产品的页面。删除也是类似的原理,如果我们在查询字符串参数中检测到了code,那么就删除产品,然后刷新列表。

你可能会注意到控制器中的另一个参数ProductFactory,这是我们需要从控制器访问数据的服务组件。Angular框架允许用不同的方式创建服务组件。最常用的一种方式是使用工厂创建。我们需要在应用模块中添加该服务,该应用模块包含两个参数:服务名称和工厂函数。别害怕,它不过是一个返回新对象的简单函数。

  1 //定义controllers对象
  2 var controllers = {};
  3
  4 controllers.ProductController= function($scope,$route,$routeParams,$location,ProductFactory) {
  5     $scope.products = [];
  6
  7     var init= function() {
  8         $scope.products = ProductFactory.getProducts();
  9     }
 10
 11     var initProductEdit= function() {
 12         var code = $routeParams.code;
 13         if (code==undefined) {
 14             $scope.currentProduct = {};
 15         } else {
 16             $scope.currentProduct = ProductFactory.loadProductByCode(code);
 17         }
 18     }
 19
 20     $scope.$on(‘$viewContentLoaded‘, function() {
 21         var tempalteUrl = $route.current.templateUrl;
 22         if (tempalteUrl=="partials/ProductEdit.html") {
 23             initProductEdit();
 24         }else if (tempalteUrl=="partials/ProductList.html") {//大小写要和temlateUrl中的大小写保持一致
 25             var code = $routeParams.code;
 26             if (code!=undefined) {
 27                 $scope.deleteProduct(code);
 28             }
 29         }
 30     });
 31
 32     init();
 33
 34     $scope.saveProduct= function() {
 35         ProductFactory.saveProduct($scope.currentProduct);
 36         $location.search(‘code‘, null);
 37         $location.path(‘/‘);
 38     }
 39
 40     $scope.deleteProduct= function(code) {
 41         ProductFactory.deleteProduct(code);
 42         $location.search(‘code‘, null);
 43         $location.path(‘/‘);
 44     }
 45 }
 46
 47 //将所有的控制器赋值给app模块
 48 demoApp.controller(controllers);
 49
 50 //定义工厂
 51 demoApp.factory(‘ProductFactory‘, function () {
 52     //初始化产品数组
 53     var products = [
 54     {code:‘P001‘,name:‘Lumia 950XL‘,description:‘win10系统最好的手机,带有黑科技色彩‘,category:‘mobile‘},
 55     {code:‘P002‘,name:‘Lumia 950‘,description:‘win10系统次好的手机,相比XL低个档次‘,category:‘mobile‘},
 56     {code:‘P003‘,name:‘Surface Pro Book‘,description:‘微软最具创新的笔记本‘,category:‘Notebook‘},
 57     {code:‘P004‘,name:‘Surface Pro 4‘,description:‘微软最好的PC/平板二合一产品‘,category:‘Surface‘},
 58     { code: ‘P005‘, name: ‘Surface 4‘, description: ‘微软次好的PC/平板二合一产品‘, category: ‘Surface‘ },
 59     {code:‘P006‘,name:‘Surface Phone‘,description:‘传说中微软下一代win10系统超旗舰手机‘,category:‘mobile‘}
 60     ];
 61
 62     var factory = {};
 63     factory.getProducts= function() {
 64         return products;
 65     }
 66
 67     factory.loadProductByCode= function(code) {
 68         var productFound={};
 69         for (var i = 0; i < products.length; i++) {
 70             if (products[i].code==code) {
 71                 productFound = products[i];
 72                 break;
 73             }
 74         }
 75         return productFound;
 76     }
 77
 78     factory.saveProduct= function(product) {
 79         var tempProduct = factory.loadProductByCode(product.code);
 80
 81         if (tempProduct == null || tempProduct == undefined) {
 82             tempProduct = {};
 83             tempProduct.code = product.code;
 84             tempProduct.name = product.name;
 85             tempProduct.description = product.description;
 86             tempProduct.category = product.category;
 87         } else{
 88
 89             tempProduct.code = product.code;
 90             tempProduct.name = product.name;
 91             tempProduct.description = product.description;
 92             tempProduct.category = product.category;
 93
 94             products.push(tempProduct);
 95         }
 96     }
 97
 98     factory.deleteProduct= function(code) {
 99         var tempProduct = factory.loadProductByCode(code);
100
101         if (tempProduct!=null) {
102             products.remove(tempProduct);
103         }
104     }
105     return factory;
106 });

点击查看代码

如上面的代码,我们使用模块的工厂函数来定义工厂服务组件。代码很明显,它只包含了一些帮助属性和方法。代码末尾的deleteProduct函数使用了一个数组的remove函数,它不是默认的数组函数,我定义了一个函数,把它添加到Array.prototype中,使得它可以在网站中的任何地方都可以访问。但要确保在应用加载的开始处理这个,在一些相关的代码执行之前执行,比如我放到了index.html页面中的script标签中。

在工厂组件中,我们经常必须从server中存取数据,但在这篇博客中,我们只用到了临时的内存中的产品数组。在下一篇博客中,我将从server的数据库中存取数据。

注解:

$route:用于控制器和视图(html分部视图)的深度链接。查看更多。

$routeParams:该服务允许检索当前路由参数的集合。查看更多。

$location:该服务分析浏览器地址栏中的URL(基于window.location),使得该URL对应用可用。查看更多。

源代码下载

时间: 2024-10-07 06:45:36

边实战边理论,入手AngularJS(Ⅰ)的相关文章

angularJs项目实战!03:angularjs与其他类库的协作

引言:angularjs是一个中等重量级的前端开发框架 HTML是一门很好的为静态文本设计的语言,但要构建动态的web应用它就显的乏力了.通常,我们使用以下技术来解决静态网页技术在构建动态应用上的不足: 1.类库:类库是一类函数的集合,它能帮助你写web应用.这里起主导作用是你的代码,由你来决定何时使用类库.典型的类库,例如prototype.jquery等. 2.框架:框架式一种特殊的.已经实现的web应用,你只需要填充具体的业务逻辑.这里框架是起主导作用的,由它根据具体的逻辑来调用你的代码.

angularJs项目实战!03:angularjs与其他类库的协作(转)

angularjs,在我看来是个中等重量级的框架.即不像backbone那么简单,也不像dojo和Yui那么包罗万象.很多时候,妄图包罗万象,往往会出现很多子模块的质量高不成低不就,并且修改起来较为困难.过分精简,则框架内容单薄需要写的内容太多.angularjs这种相对中庸的风格,则非常符合我的需求. 目前,AngularJS三个我认为最为精妙的组件就是数据绑定(Scope),指令(Directive)和依赖注入(Dependency Injection),表现得非常好.相对而言,它的UI组件

REST:demo实战+RESTful理论

理解一个新的技术,无疑就是使用它了,下面我们就通过一个可执行的demo来展现REST的原理和使用. 一 Demo 1.1 服务器端 1 主程序MainServer.java负责启动一个REST服务组件,并设置端口号,创建主路径/threshold及子路径(见步骤2),最后start启动即可. public class MainServer extends Application {     public static void main(String[] args) throws Excepti

分布式系统(3)---Web Service实战--CXF理论篇

第一篇:CXF理论篇 在Java领域,WebService的框架很多,例如:AXIS,XFire,CXF等.AXIS,XFire相对比较成熟. Axis全程Apache Extensible Interaction System即Apache可扩展交互系统.是第三代Apache SOAP.本质上就是一个SOAP引擎,但不完全是一个SOAP引擎,它还是一个独立的SOAP服务器和一个嵌入Servlet引擎的服务器. XFire是新一代的Java Web服务引擎,可以非常容易地和Spring集成.是c

UI半自动化实战-从UI入手,新增多条业务数据

背景: 目前的项目中,需要在一个list里边有1000多条数据去测性能,截止目前为止,走正常的业务流程进来的数据只有20条,如何快速的新增1000多条数据呢? 思考: 目前在做UI自动化的测试,Airtest+python    有现成的代码 实践结果:----待改进  思考如何做到全自动化 目前:半自动化   需要手动替换身份证号和姓名 在airtest编辑器中,每次执行完成后,需要手动替换身份证号(网上搜一大堆)和姓名 ,同时在点击运行按钮 __author__ = "ThinkPad&qu

Node.js 切近实战(二) 之图书管理系统

上一篇Node.Js切近实战讲述了如何在VS上搭建Node.Js开发环境,相信看过那篇博客的同学,你已经对Node.Js有了好感,多了解些技术还是很有必要的.所谓实战见真功,还是要实战才行.我看博客园上一些专家荣誉的人写的博客,全是理论,没有实战,没有实战就没有发言权.熟能生巧,实战出理论. 我们看一下项目结构 典型的NodeJs三层架构,controller,model,view,这里每个部分什么职能,我就不多说了. 首先我们看一下登录界面,先上图,我看博客的时候,会首先看博客中有没有图,比如

【原创】IPSecVPN理论干货送上,大神勿扰

众所周知,无论是复杂深奥的理论干货,还是晦涩难懂的语言文字,不管对错与否,只是因为我们没有达到那种高度,不能转化为属于自己的东西罢了,于是真正能懂的人还是很少,而且每个人对懂的定义不一样,只能细细去体会,慢慢去品味,只要有着自己的理解角度就好,下面我就以简单的IPSecVPN理论入手,让大家去体验一下外行人中是如何入门的,菜鸟路过,大神勿扰! IPSecVPN两个阶段的协商过程:    第一阶段:    有主模式和积极模式2种.    通过协商让IKE对等体彼此验证对方并确定会话密钥,这个阶段用

【JavaScript】 2013年度最强AngularJS资源合集

http://www.iteye.com/news/28651-AngularJS-Google-resource AngularJS是Google开源的一款JavaScript MVC框架,弥补了HTML在构建应用方面的不足,其通过使用指令(directives)结构来扩展HTML词汇,使开发者可以使用HTML来声明动态内容,从而使得Web开发和测试工作变得更加容易.  AngularJS诞生以来,吸引了大量的目光,也迅速成为了Web开发领域的新宠.本文整理了2013年度一些非常有价值的Ang

拥抱AngularJS

文中一些地方AngularJS简称ng 简介: ng诞生于2009年,由Misko Hevery等创建,后被Google收购,为克服HTML在构建应用上的不足而设计. 是一款优秀的前端JS框架,核心特性:MVVM(Model-View-ViewModel).模块化.双向数据绑定.依赖注入. 官网:https://angularjs.org/ 中文网:http://www.apjs.net/ 入门: 图灵社区 书籍: 用AngularJS开发下一代Web应用  AngularJS权威教程 源码: