AngularJS是为了克服HTML在构建应用上的不足而设计的。HTML是一门很好的为静态文本展示设计的声明式语言,但要构建WEB应用的话它就显得乏力了,所以AngularJS做了一些工作来来解决静态网页技术在构建动态应用上的不足。
AngularJS的初衷是为了简化web APP开发,精髓是简单。但是国内外有很多AngularJS的教程,都着重讲AngularJS的强大之处,从而增加了AngularJS学习的难度,本教程试图用通俗的语言讲解最为基础、最为实用的内容,简化学习进程、降低学习难度是本教程的初衷。
本系列教程以翻译Chris Smith的Angualr Basics为梗概,融合博主自己的理解,为大家提供一个简单明了的学习教程。
本文为系列教程第3篇基础,翻译自Basics。
AngularJS是一个通过增强HTML实现客户端web应用的框架。
我们来充分解读下。如果您是一个有经验的web开发者,您很可能拿Angular和其他熟悉的javascript框架(库)做个对比,诸如jQuery、Knockout、Backbone、Ember甚至是React;同样如果您了解GUI编程,您会从Angular想到MVC(Model View Controller)、MVVM(Model View ViewModel);这些想法极为自然,但是会影响到对Angular的理解。仅对本章而言,我建议您暂时不去深层理解它的工作原理,仅将Angular当做一个Javascript框架,将之当做对HTML的功能增强。
我们将讲解Angular的三个基础知识,表达式、指令、作用域。但是在进入具体的案例之前,让我们简单了解下如何让Angular在页面中工作(牛X的童鞋请阅读表达式部分)。
从哪里可以得到Angular?除了官网下载之外,从 Google Hosted Libraries使用CDN方式加载既方便又便捷(当然,老外没有被墙过,不接地气),本页正是使用script标签引入CDN的方式实现的。google被墙,国内程序员可以使用百度静态资源公共库的CDN。
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.js"></script>
<!-- or -->
<script src="//apps.bdimg.com/libs/angular.js/1.2.16/angular.min.js"></script>
引入AngularJS之后,您需要告诉Angular需要管理哪部分HTML。记住Angular是面向html的而不是面向javascript的,所以我们使用非标准的的html供Angular识别(给DOM中的任何元素添加ng-app属性)而不是书写一些javascript语句去加载它。在本章中,我们在body元素上添加该属性(我们在html或body元素上添加属性可以让Angular应用控制整个页面),如下面代码所示。
<body ng-app>
<!-- Other examples in this chapter will be inserted here. -->
</body>
如果您愿意,可以选择一个较窄的作用域,允许您同时使用其他框架,加载其它Angular应用。相对于单页面应用(SPA),这种情况更适用于传统的文档中心网站客户端代码。
使用这个神奇的属性,我们就能让Angular工作了,接下来需要做什么呢?
注意:如果您接受过不唐突的Javascript训练,下面的代码也许会敲响警钟,因为Angular的第一条原则是允许在html中混合使用JS样式的表达式。Angular中可以使用简单、正确的JS,不允许使用类似于循环等的控制结构。在我们做这些古老的、流行的实验时,暂不考虑是否唐突的问题。
让我们来相加两个数。像本书的其他案例一样,编辑器里面的代码可以修改,然后可以看到相应的结果(博客技术角度考虑,本教程没有实现该功能,需要实验的同学请移步原教程)。
<p>The number {{3 + 4}}.</p>
结果为:The number 7.
再进一步,如果你修改3+4为新的数学表达式,Angular也会计算结果。看看有没有Angular不能计算的,如果输入的不是一个正确的表达式,它要不直接输出字符串,要不什么都不显示(出错的情况下),如下面的动图所示。
成对的大括号是模板界定符,如果您了解Mustache或Handlebars,这种方式就不陌生。有人把AngularJS当做以中国精密的模板库,当页面加载的时候把ng-app标识的元素作为一个模板,当数据改变时重新渲染(不要担心不熟悉模板,我们会在后面简要介绍)。
我们可以判断两个值相等吗?当然,请看下面代码。
<p>Are strings and numbers equal? {{‘2‘ == 2}}</p>
结果为:Are strings and numbers equal? true
对于javascript来说,结果是正确的。如果您有所怀疑,你可以参考javascript comparison operators,然后尝试将”==”修改成”===”看看结果(严格比较更好,是吧)。我们同样截个动图,请看下图。
下面的案例演示字符串连接,JS标准方法的调用(例如String的toUpperCase方法)。
<p>{{"Angular" + "js".toUpperCase()}}</p>
结果为:AngularJS
你是不是感觉我们可以在表达式中作任何事情,不一定哟,伙计。
<!-- 错误代码! 表达式中不允许出现函数调用 -->
<p>{{alert("Hello world")}}</p>
你不能在表达式中使用alert,同样不能使用javascript的全局对象,例如Math、Number、Date等等。请试着把alert("Hello world")
替换成parseInt("1")
、 Date.now()
、 Number.NaN
、 Math.random()
等看看结果,AngularJS拒绝解析这些表达式,如下动图所示。
尽管Angular表达式有局限性,但我猜你会兴奋的混合code和html,直到你感觉到框架的种种限制。让我们时刻注意这些限制。
你觉得可以定义变量吗?
<p>{{a = 1; a + a}}</p>
结果为:2
可以正常工作,但是如果我们在变量名前加上var关键字,产生了一个错误吧?如果输出中包含了原始的表达式,并且输出了界定符“{{ …… }}”,说明发生了一个错误。有点失望吧,别担心,我们有收获。其实试误是一个很好的学习方法。
实验同样是一个不错的学习方法,我不知道一个界定符中定义的变量能不能用在其他界定符中,为什么不试试呢?
<p>{{a = 1}} remains {{a}}</p>
结果为:1 remains 1
yeah,没问题。如果我们把变量初始化放到第二个表达式中可以吗?试试看?原因又是什么?
表达式支持三元运算符吗?我们试试看。
<p>There {{count = 1; (count == 1 ? "is " : "are ") + count}} of them.</p>
结果为:There is 1 of them.
幸运的是三元运算符可以正常工作,三元运算符的简明语法将在模板中非常有用。
那么,自增自减可以吗?
<p>{{a = 1; ++a}}</p>
结果为:{{a = 1; ++a}}
看来不行(自增自减)。好吧,我们必须得在我们的表达式中去掉这些失效的元素,然后for循环可以吗?
<p>{{for (i = 0; i < 10; i = i + 1) {i}}}</p>
同样不行(for循环),并且在控制台中产生了一个语法错误,这个语句作为javascript语句没有错误,但是作为表达式产生了错误。所以,我们悟到了一些界限:Angular表达式不是Javascript,它不支持条件语句、循环、抛出错误。
Angular表达式我们应该已经讲得比较充分了,就我个人而言,并不排斥在标记中适度使用简明、视图相关的javascript代码,因此我喜欢Angular 表达式的部分许可特性。但是模板中过多的javascript代码容易造成混乱,表达式仅仅在Angular中担任支持的角色,所以不要过多使用javascript。
AngularJS真正令人兴奋的关键是指令(directive)。
指令是Angular的核心和精髓,我强烈建议你把指令认作自定义HTML,大部分时候,指令以正常的html标签的属性出现。内置指令以”ng-“作为前缀,现在也有很多第三方指令,当您看到表达式中冗长的javascript代码时,“一定有相应的指令可以做到这个”应该成为您的第一反应。
其实,我们已经使用过一个指令了,还记得吗? 当我们想让我们的代码动态展示的时候,我们在body标签上添加了一个属性ng-app,我们没有为这个指令指定任何参数,如果需要指定参数,需要在指令后面跟等号和引号,像普通的html属性那样。例如,`ng-app=”myApp”(应用名称实际就是模块module名称,模块是Angular架构中非常重要的一个部分,请参阅本教程的第7部分)。
一些指令可以接受解析字符串表达式作为参数(你可以从AngularJS API中获取查询内置指令的参数),例如ng-bind指令仅仅像前面“{{ }}”表达式一样解析表达式,恰如下列代码所示。
<p>The number <span ng-bind="3 + 4"></span>.</p>
结果为:The number 7.
运行结果和上面的第一个案例结果一致。使用ng-bind有一个重要的不同,应用ng-bind的元素内容会在模板渲染时重置。因此要**优先使用**ng-bind,但是需要确保该元素内容为空(在angular替换原始html之前保证内容为空)。依赖于用户环境、您的Angular应用的大小和行为,可能会产生一些延迟导致用户看到双大括号界定符(”{{……}}”)或编译前的表达式,在空元素上使用ng-bind可以避免这个问题。
随着您的应用日益庞大,您可能需要通过加载完毕前隐藏信息来较高水平的解决这些闪烁问题, ng-cloak指令(碰墙,看这个)可以轻松搞定。明白“一定有个指令可以搞定这些”是多么好的习惯了吧?
还记得如何在表达式中初始化变量吗?一定有个指令也可以初始化变量吧?是的,就是ng-init,您可以在绑定ng-init的元素内部任何地方初始化变量。
<div ng-init="sum = 3 + 2">
<p>
{{sum + 1}} is more than
<span ng-bind=‘sum - 1‘></span>
</p>
</div>
结果为:6 is more than 4
使用分号分隔,您可以声明初始化多个变量。
<div ng-init="count = 7; units = ‘days‘; collection = ‘week‘">
<p>
There are {{count}} {{units}} in a {{collection}}.
</p>
</div>
结果为:There are 7 days in a week.
有些时候,您可能觉得使用一个对象的属性组织变量更好。
<div ng-init="time = {count: 12, units: ‘months‘, collection: ‘year‘}">
<p>
There are {{time.count}} {{time.units}} in a {{time.collection}}.
</p>
</div>
结果为:There are 12 months in a year.
数组也非常的有用。
<div ng-init="months = [‘January‘,‘February‘,‘March‘,‘April‘]">
<p>
{{months[3]}} follows {{months[2]}} in {{months}}.
</p>
</div>
结果为:April follows March in [“January”,”February”,”March”,”April”].
编写Angular模板时,仔细核对属性名称(be careful about property names)非常重要。出现问题时,您只能自己定位问题所在,而别想从Angular哪里获得任何帮助,它只是默默的容忍所有的属性访问错误,包括不存在父对象的嵌套属性、数组出界访问等。
expression-property-failures.html
<div ng-init="paragraph = {sentence: {words: [‘first‘,‘second‘,‘third‘]}}">
<p>
"{{typo.sentence.words[0]}}",
"{{paragraph.typo.words[1]}}",
"{{paragraph.sentence.typo[2]}}",
"{{paragraph.sentence.words[3]}}".
</p>
</div>
结果为:”“, “”, “”, “”.
就我个人而言,我希望Angular放弃这种容忍,借鉴CoffeeScript的精彩简介的存在操作符(existential operator -?)把这个静默访问错误作为一个选项而非一个规则。
截止到目前为止,你觉得可以在ng-init中定义函数吗?猜猜看?
<div ng-init="count = function() { return 12; }">
<p>
There are {{count()}} months in a year.
</p>
</div>
结果为:There are months in a year.
显然,函数表达式不支持。在试图给ng-init传递参数时,Angular抛出了一个$parse:syntax错误(浏览器的Javascript控制台可以看到)。声明表达式中可用函数的正确的地方是控制器(controller,我们会在后续章节里进行学习)。实际上,控制器是为视图准备数据的最好的地方。我们在本章开心学习的ng-init指令,实际上是为了重叠变量(aliasing variables)以应付变量阴影(variable shadowing),我们会在集合(Collections )章节进行学习。
顺便说一句,如果你需要在从AngularJS的处理文档中屏蔽一部分,可以使用ng-non-bindable编译时屏蔽该元素。
<p ng-non-bindable>
{{2 * 2}} is the same as <span ng-bind=‘2 + 2‘>?</span>
</p>
结果为:{{2 * 2}} is the same as ?
如果去掉ng-non-bindable属性会发生什么?请看动图。
是时候跳出简单渲染表达式,上点难度来点特别的。对于初学者,我们如何实现根据条件显示、隐藏网页元素呢?
给大家分享我首次使用Angular实现隐藏显示元素的搞笑经历。第一次使用Angular时,我需要在用户成功提交表单后隐藏该表单。在处理用户行为的控制器里,我添加了jQuery.hide()
,当然可以起到效果。“一定有个指令可以搞定这些”,是的,实际上有两个ng-hide和ng-show。
<p ng-init="authorized = true">
The secret code is
<span ng-show="authorized">0123</span>
<span ng-hide="authorized">not for you to see</span>
</p>
结果为:The secret code is 0123
把true改为false,看看会发生什么。因为Angular随时侦听变化,您可以在任何地方切换authorized值,视图将为用户进行同步更新。
此时,我们可以在变量、变量如何工作方面更进一步了。
Scopes在您的应用里提供了唯一正确信息源(single source of truth)。无论您在视图的什么地方展示数据,都应该一个统一的地方(scope)改变数据(管理数据),您做出的任何改变都会迅速的反馈到整个视图中去。
这种自动渲染、重新渲染需要技术基础,就是在scopes中使用的看起来非常普通的javascript对象。您可能熟悉简单域模型对象(plain old domain model objects)的概念,它来源于POJO(Plain Old Java Object),当Martin Fowler第一次阐释POJO的概念时,相对于可以从超类中继承特殊能力的庞大的域模型对象,他更倾向于语言运行时直接提供的简单、普通的对象。而且,概念的意义进行了扩展,看似简单,功能却在特殊运行时能力、经典的持久力方面进行了增强。
附加到Angular scopes中的对象时Javascript POJOs,因为Angular使用运行时精密变动检测以便能够传送改变。当Object.observer语言特性成为主流javascript技术时,Angular实际上成为一种plain old javascript 框架。
在本章中,我们已经用到过scope属性。还记得上面的使用ng-init初始化变量的案例吗,就是使用了scope。还记得我让你在表达式中声明变量前使用var关键字吗?使用var发生错误,主要是因为我之前调用的变量实际上scope对象的一个属性。下一章将会深度解析scope,现在让我们亲手做些案例体会一下如何使用scope属性。
截止到目前,我们已经看到过很多单向绑定的案例,scope属性动态更新到视图中。当你利用html修改数据,这些数据同步更新到视图中,这就叫双向绑定。
第一个案例,我们使用ng-click指令动态修改一个boolean值。
<p ng-init="authorized = true">
The secret code is
<span ng-show="authorized">0123</span>
<span ng-hide="authorized">not for you to see</span>
<br>
<input type="button" value="toggle" ng-click="authorized = !authorized">
</p>
当我们单击按钮时,authorized的值将发生改变,同时根据authorized值的变化视图也发生改变,如下动图所示。
ng-click的参数可以是任何表达式,尽管它是一个多才多艺的指令,非常简单的实现翻转变量。但是如果我们需要绑定表单和数据呢?
几乎每一个教程讲解双向绑定时都会举例“输入框绑定字符串”,现在轮到我们了。
<input type="text" ng-model="title">
is bound to
"<strong ng-bind="title"></strong>".
Type something in the box!
运行结果我们还是搞个动图。
再让我们看看其他类型的表单,如何表现。
<input type=‘checkbox‘ ng-init=‘optIn = true‘ ng-model=‘optIn‘>
is bound to
<strong ng-bind="optIn"></strong>.
Change it!
如果您使用ng-true-value和ng-false-value,您可以把字符串绑定到checkbox。
<input type=‘checkbox‘ ng-init=‘feeling = "love it"‘ ng-model=‘feeling‘
ng-true-value=‘"love it"‘ ng-false-value=‘"hate it"‘>
is bound to
"<strong ng-bind="feeling"></strong>".
Change it!
结果恰如动图所示。
下拉列表select的案例,如下所示。
<select ng-model=‘answer‘>
<option value=‘yes‘ selected>Yes</option>
<option value=‘no‘>No</option>
<option value=‘maybe‘>Maybe</option>
</select>
is bound to
"<strong ng-bind="answer"></strong>".
Change it!
如果改成单选按钮(radio)也是可以的。
现在您已经拥有了利用指令操作scope数据的实战经验,Angular拥有许许多多的内置的指令,我们会在接下来的章节中进行逐步学习。
在本章开始的时候,我建议您不要将Angular看做JS框架,而是把它作为HTML的扩展。正如Angular之父Mi?ko Hevery在一次访谈中所言,Angular初衷是帮助前端开发者快速、有效地增强web页面功能。
Angular采用声明式语言格式以更好地适应快速GUI开发,基于开发者熟知的HTML构建Angular,没有在另辟蹊径(启用新的DSL,加重开发者负担)。
至少是现在,离开自定义的Javascript就没有Angular,现实中的Angular应用需要利用控制器、服务、路由、模块、依赖注入、Ajax基础,所有这些都会在本书中有所涉及。但是最好的方式还是,开发者主要在声明空间工作,坚信“一定有个指令可以搞定这个”,书写自定义指令是一个颇有挑战性的工作,这里稍微提醒下大家。
现在明白了,我们的终极目标是有效地、有力地扩展HTML,而不是在一个框架内书写JS代码,您准备好深入细节了吗?
前端开发whqet,关注前端开发,分享相关资源。csdn专家博客,王海庆希望能对您有所帮助,限于作者水平有限,出错难免,欢迎拍砖!
欢迎任何形式的转载,烦请注明装载,保留本段文字。
本文原文链接,http://blog.csdn.net/whqet/article/details/44648511
欢迎大家访问独立博客http://whqet.github.io
原文:http://blog.csdn.net/whqet/article/details/44648511