首先,未来自 Meetup.com 的即将举办的活动信息添加到 UGLI 主页。css
若是访问 Meetup.com 上的 HTML5 DenverUsers Group,您会看到一个相似图 1 的网页。html
能够看到,使用即将举办的活动信息进一步自定义 UGLI 主页所需的全部原始材料,都在 Meetup.com 页面上(即将发表的演讲的标题、会议时间和地点,等等),但Meetup.com 页面的设计与该数据紧密耦合。您想要提取并在 MEAN 应用程序中重用的信息,与 HTML 元素杂乱地混在一块儿,如清单 1 所示。(为了清晰性和简洁性,我对HTML 进行了编辑。)angularjs
<ul> <li itemscope="" itemtype="http://data-vocabulary.org/Event"> <span itemprop="eventType" style="display:none;">Meetup</span> <h3> <a href="http://www.meetup.com/HTML5-Denver-Users-Group/events/160326502/" itemprop="url"> <span itemprop="summary">"Developing Offline Applications" and "HTML 5 Animations"</span> </a> </h3> <!-- snip --> </li> </ul>
活动的标题(“Developing Offline Applications” 和 “HTML 5 Animations”)深刻嵌套在 HTML中的多层中。就 HTML 文档(和您的 Web 浏览器)而言,这个任意的字符串只是嵌套在一个无序列表(<ul>
) 内的几个列表项(<li>
) 之一。这个列表项有一个三级标题(<h3>
),大概二级和一级标题都已在文档分层结构中定义。在标题内是一个超连接 (<ahref>
),其中进而包含一个任意的文本 <span>
。web
信息要按 Meetup.com 想要的格式显示,全部这些 HTML标记必不可少。您的任务是以一种彻底不一样的方式在您应用程序中显示信息:您须要找到一种方式来从显示中分离出信息。chrome
须要使用两个不一样的语义层,如清单 1 所示。最基本的语义层我刚才已讨论:一项是一个列表项,另外一项是一个超连接。这就是文档 语义。另外一层是事件语义,由 eventType
、url
和 summary
等关键字表示。这些关键字与文档的呈现方式毫无关系。它们向搜索引擎(和查看 HTML 源代码的初学者)暗示信息的 “更高级含义”。这些属性是 HTML Microdata 规范的一部分。(有关微数据及其元数据前身的更多信息,请参阅 Microformats、microdata 和 RDFa!哦天哪! 边栏。)json
这个特定的列表项中仅包含一个 event
的信息。搜索引擎知道此事实,是由于设计该页面的人向该列表项添加了itemtype="http://data-vocabulary.org/Event"
。该页面包含许多任意超连接,其中带有itemprop="url"
的超连接是 event
自己的连接。eventType
为 Meetup
— 一个任意字符串,但 Meetup.com会在其全部网页上一致地使用它。活动的 summary
由带 itemprop="summary"
属性的<span
> 来标识。segmentfault
<span>
元素是浏览器在呈现页面时忽略的一些 HTML 元素之一。<b>
元素内的文本呈现为加粗字体;<h1>
文本的显示字号比 <h2>
文本更大;<a>
文本可单击,一般使用蓝色且带下划线。固然,全部这些默认样式规则均可使用 CSS 覆盖。但<span>
标记的存在仅用于添加您本身的 CSS 样式 — 或者对于 清单 1中的 Meetup.com HTML 代码段,用于将 "Developing Offline Applications" and "HTML5 Animations"
字符串包装在 itemprop="summary"
语义标签中。设计模式
有关可添加到 MEAN 标记中来进一步描述事件的完整元数据项列表,可首先访问 Meetup.com 用于定义事件的 URL:http://data-vocabulary.org/Event。api
大致了解 Meetup.com 如何显示事件信息后,可对 uGLI 应用程序执行相同操做。数组
在测试应用程序的 root 中键入 mongod
来启动 MongoDB,而后键入 grunt
启动 Web 应用程序。在 Web浏览器中访问 http://localhost:3000 时,您应看到在上一期中自定义的主页(如图 2 所示)。
我想向主页添加即将举办的 HTML5 Denver User Group活动。本教程的剩余部分将迭代式地完成此工做。首先,添加一些静态占位符数据来描绘页面的基本外观的线框图。
在文本编辑器中打开 public/modules/core/views/home.client.view.html。在标语下,添加活动的新标记,如清单 2 所示。
<section data-ng-controller="HomeController"> <div class="jumbotron text-center"> <!-- snip --> </div> <div class="row"> <div>Monday, September 22, 2014</div> <h3><a href="http://www.meetup.com/HTML5-Denver-Users-Group/events/160326502/"> "Developing Offline Applications" and "HTML 5 Animations"</a></h3> <div class="col-md-4"> <h4>When</h4> <p>6pm</p> <h4>Where</h4> <address> <span>Rally Software</span><br> 1550 Wynkoop<br> Denver, CO<br> </address> </div> <div class="col-md-8"> <p><b>6 pm : "Developing Offline Applications with HTML 5" by Venkat Subramaniam</b></p> <p><b>7 pm: Dinner and Networking</b></p> <p><b>7:30 pm: "HTML 5 Animations - building true richness on the web" by Venkat Subramaniam</b></p> </div> </div> </section>
在浏览器中查看更新的主页时,它应相似于图 3。
有了基本的 HTML
默认样式更美观。为此,须要添加一些语义。
Bootstrap 提供了您使用的默认结构化 HTML 元素(好比 <div>
和<h3>
)的内置样式。它还提供了一些不属于默认 HTML 元素的附加的结构化类,好比 row
和col
。
初学的 Web 开发人员经常会考虑网页的结构,而不是显示的信息。结果是,他们使用像 big-red-italic
和left-column-header
这样的名称来编写自定义 CSS 类。从语法上讲该方法并无错,但我发现使用像event-date
和 event-location
这样的语义名称时,网站的长期维护更容易。这样,当客户在一年后返回要求将全部分类汇总标为绿色而不是红色时,我可编写一个 CSS 类来识别显示了哪些内容
(subtotals
),而不是 如何显示(green-body-text
)。我也不太可能不经意地更改页面上其余刚好也使用了 green-body-text
CSS规则的元素。
返回到您刚编写的 HTML,添加一些具备合适的语义的 CSS 类,好比 event
、event-date
和event-title
:
<div class="row center-block event"> <div class="event-date">Monday, September 22, 2014</div> <h3 class="event-title"><a href="http://www.meetup.com/HTML5-Denver-Users-Group/events/160326502/"> "Developing Offline Applications" and "HTML 5 Animations"</a></h3>
center-block
结构类来自 上一期中介绍的 Bootstrap 库。稍后将会看到,将 event
类的 width
减少到 75%
时,center-block
将确保左侧和右侧的空白区域均等。
这很是适合在同一个元素上混合使用结构和语义类。事实上,从长远上来说,它使我更容易快速识别哪些类是特定于应用程序的(event-*
),哪些类是通用的(row
、center-block
)。
将这些类添加到 HTML 后,就能够定义一些自定义 CSS 规则了。在文本编辑器中打开 public/modules/core/css/core.css。由于每一个模块都拥有本身的CSS,因此可保持 “面向组件” 的理念。并且经过抓取整个子目录树,更容易在项目间共享模块。
添加 CSS 样式规则,如清单 3 所示。
.event { width: 75%; } .event-date { font-style: italic; } .event-title { margin-top: 0; }
如今您的主页看起来没那么粗糙,更加美观了,如图 4 所示。
最后,再次返回添加微数据元数据。可否跳过一步,在 CSS 规则中使用微数据元素,而不定义自定义元素?固然能够。但我想将它们分开。毕竟,它们具备两种不一样的用途。一个用于 CSS样式,另外一个用于搜索引擎优化 (SEO)。假设您 5年前遇到一个用户案例,要求您将微数据迁移到另外一个更新的规范。若是在知足案例要求的同时,带来了影响网站外观的反作用,就太遗憾了。这两个特性应是彻底不一样的。
再一次在文本编辑器中打开 public/modules/core/views/home.client.view.html。为该活动添加新的语义微数据标记,如清单 4所示。(有关使用微数据标记活动的范例,请参阅 富代码段 - 活动。)
<div class="row center-block event" itemscope itemtype="http://data-vocabulary.org/Event"> <span itemprop="eventType" style="display:none;">Meetup</span> <time class="event-date" itemprop="startDate" datetime="2014-09-22T18:00-06:00">Monday, September 22, 2014</time> <h3 class="event-title"><a href="http://www.meetup.com/HTML5-Denver-Users-Group/events/160326502/" itemprop="url"><span itemprop="summary">"Developing Offline Applications" and "HTML 5 Animations"</span></a></h3> <div class="col-md-4"> <h4>When</h4> <p>6pm</p> <h4>Where</h4> <address itemprop="location" itemscope itemtype="http://data-vocabulary.org/?Organization"> <span itemprop="name">Rally Software</span><br> <span itemprop="address" itemscope itemtype="http://data-vocabulary.org/Address"> <span itemprop="street-address">1550 Wynkoop</span><br> <span itemprop="locality">Denver</span>, <span itemprop="region">CO</span><br> </span> </address> </div> <div class="col-md-8" itemprop="description"> <p><b>6 pm : "Developing Offline Applications with HTML 5" by Venkat Subramaniam</b></p> <p><b>7 pm: Dinner and Networking</b></p> <p><b>7:30 pm: "HTML 5 Animations - building true richness on the web" by Venkat Subramaniam</b></p> </div> </div>
确定须要大量工做,最终才能在浏览器中获得与以前彻底相同的显示结果,是否是?但值得高兴的是,因为您费力添加的全部语义数据,您的网站上升到了搜索结果的最前面。
咱们不会逐行地详细解释清单 4,我仅指出一些重要的地方。
与 清单 1 中的 Meetup.com 示例同样,咱们向一个没有显示的元素添加了eventType
(由于它仅用于 SEO):
<span itemprop="eventType" style="display:none;">Meetup</span>
接下来,向活动添加一个日期以供人和机器使用:
<time class="event-date" itemprop="startDate" datetime="2014-09-22T18:00-06:00">Monday, September 22, 2014</time>
做为人类,您可当即分析字符串 Monday, September 22, 2014
,认识到它是一个日期。您还可以将 9/22/2014 和2014-09-22 识别为同一个日期。但计算机更加注重字面内容,像这样的细小的格式更改可能致使重大的故障。在这个示例中,您执行了多处更改来消除歧义:
<div>
升级为更具体的 <time>
(一个新的 HTML5元素)。event-date
类识别数据的内容,而不是外观。* itemprop="startDate"
微数据属性将此日期识别为 event
的startDate
。* datetime
属性(HTML5 <time>
元素的一部分)清晰地代表该时间为 ISO 8601格式。这样,既可提供一种机器可以使用的时间,又可提供一种用于显示和人类使用的格式良好的时间。对 HTML 的最大更改包括活动的地址,如清单 5 所示。您在 Event
模式内嵌套了多种新模式—Organization
and Address
。
<h4>Where</h4> <address itemprop="location" itemscope itemtype="http://data-vocabulary.org/?Organization"> <span itemprop="name">Rally Software</span><br> <span itemprop="address" itemscope itemtype="http://data-vocabulary.org/Address"> <span itemprop="street-address">1550 Wynkoop</span><br> <span itemprop="locality">Denver</span>, <span itemprop="region">CO</span><br> </span> </address>
如今您已向网站添加一些微数据,如何肯定您已正确添加?最简单的方法是为浏览器使用一种微数据扩展。在多种非官方的微数据扩展中,我喜欢在 Chrome 中使用的是 Semantic inspector。
安装以后,此浏览器扩展一般会保持隐藏,直到您访问一个使用微数据的网站。Semantic inspector 在当前网页中找到微数据时,它会在地址栏显示一个红色的 m图标。您可能对这个小图标弹出的频率很吃惊;您会在许多流行、主流的网站上看到它,包括 Google、Time.com 和 Walmart.com 等。单击该图标可显示详细信息,如图 5
所示:
如今您已有标注了微数据属性的基本的 HTML 线框图,是时候从这些连线中填入全新的 JSON 了。为此,建立一个新的事件模块,其中包含控制器、视图、模型和服务。
在 上一期 中,您使用 Yeoman 搭建了一个完整的 CRUD 模块,包括 Express 路由和一个 Mongoose模型。对于此用户案例,不须要服务器端基础架构,由于原始 JSON 数据来自外部 Web 应用程序。幸运的是,MeanJS Yeoman生成器的建立者预见到了这一需求,单单为应用程序的客户端 AngularJS 部分提供了另外一个生成器。
键入 yo meanjs:angular-module events
来建立一个名为 events
的新 AngularJS模块。AngularJS 模块是特定于您应用程序中一种特定数据类型的文件的逻辑分组。根据官方 AngularJS 文档的描述,“可将模块视为您应用程序的不一样部分(控制器、服务、过滤器、指令等)的容器。”
收到提示时,选择列表中的全部元素,如清单 6 所示。
[?] Which folders would you like your module to include? ? config ? controllers ? css ? directives ? filters ? img ? services ? tests ? views create public/modules/events/events.client.module.js
根据刚才的描述,您的模块只是一个空目录集合。您不会在编写的每一个 AngularJS
模块中使用每一个目录,但知道有一个容易记住、容易理解的地方来在时机成熟时放入模块的各部分,也很不错。
下一步是向模块添加一个控制器。
搭建一个模块后,搭建一个控制器也很是容易。键入 yo meanjs:angular-controller events
并选择events
模块,如清单 7 所示。
[?] Which module does this controller belongs to? articles core ? events talks users create public/modules/events/controllers/events.client.controller.js create public/modules/events/tests/events.client.controller.test.js
能够看到,Yeoman 生成器将该控制器放在 controllers 目录中,将关联的测试放在您指定的模块的 tests 目录中。
此刻要问的一个合理的问题是 “为何我建立了一个控制器,为何我应该关注它?”回想一下,AngularJS 是一个客户端 模型-视图-控制器(MVC) 框架。您最终将获得一个视图(在本教程前面建立的 HTML <div>
元素),其中填入了模型数据(一个填入了来自Meetup.com 的活动数据的 JSON 结构)。该视图如何访问该模型?控制器的工做是将各部分集合起来,为视图提供它须要的模型数据。
如下这个简单示例演示了 MVC 的各部分如何融合在一块儿。在文本编辑器中打开
public/modules/events/controllers/events.client.controller.js,如清单 8 所示。
'use strict'; angular.module('events').controller('EventsController', ['$scope', function($scope) { // Events controller logic // ... } ]);
稍后,咱们会将此控制器绑定到一个特定的 DOM 元素。$scope
变量将负责将模型传递给视图的重要工做。
向 $scope
添加一个 title
变量(模型),如清单 9 所示。
$scope
变量添加到 AngularJS控制器'use strict'; angular.module('events').controller('EventsController', ['$scope', function($scope) { $scope.title = 'High Performance WebSocket'; } ]);
接下来,将 EventsController
添加到 public/modules/core/views/home.client.view.html 中的Event
DOM 元素(视图)中:
<div class="row center-block event" itemscope itemtype="http://data-vocabulary.org/Event" ng-controller="EventsController">
您可能已猜到,此代码将控制器绑定到 DOM 元素。$scope
变量仅对此 <div>
和它的子元素有效。若是愿意,可将一个控制器绑定到许多不一样的 DOM 元素。每一个元素会得到一个新控制器的惟一实例和它本身的惟一 $scope
。
接下来,向您的线框 HTML 添加一个 {{title}}
占位符以取代硬编码的文本:
<h3 class="event-title"><a href="http://www.meetup.com/HTML5-Denver-Users-Group/events/160326502/" itemprop="url"><span itemprop="summary">{{title}}</span></a></h3>
在 Web 浏览器中查看结果时,您应看到 {{title}}
占位符替换为了经过 EventsController
提供的文本,如图 6 所示。
如今您已有一个简单、有效的示例,是时候详细分析分析它了。(换句话说,您的应用程序已能正常工做,是时候再次破坏它了。)若是愿意,可向 $scope
添加许多变量,这些变量能够是简单的单个值,或者完整的 JSON 对象。
很快,您将会看到如何向 Meetup.com 发出一个 HTTP 请求,以检索下一个即将举办的活动的 JSON。到那时,在 $scope
中添加一些简化的模拟数据,以模拟您将从实际的 Ajax 调用获取的数据,如清单 10 所示。
'use strict'; angular.module('events').controller('EventsController', ['$scope', function($scope) { $scope.title = 'High Performance WebSocket'; $scope.event = { 'name': '"Developing Offline Applications" and "HTML 5 Animations"', 'time': 1411430400000, 'event_url': 'http://www.meetup.com/HTML5-Denver-Users-Group/events/160326502/', 'description': '<p><b>6 pm : "Developing Offline Applications with HTML 5" by Venkat Subramaniam</b></p>', 'venue': { 'name': 'Rally Software', 'address_1': '1550 Wynkoop', 'city': 'Denver', 'state': 'CO', } } } ]);
能够看到,$scope.event
变量包含一个复杂、嵌套的 JSON 对象。编辑您的视图来利用这个新模型数据,如清单 11 所示。
<h3 class="event-title"><a href="{{event.event_url}}" itemprop="url"><span itemprop="summary">{{event.name}}</span></a></h3> <div class="col-md-4"> <h4>When</h4> <p>{{event.time}}</p> <h4>Where</h4> <address itemprop="location" itemscope itemtype="http://data-vocabulary.org/Organization"> <span itemprop="name">{{event.venue.name}}</span><br> <span itemprop="address" itemscope itemtype="http://data-vocabulary.org/Address"> <span itemprop="street-address">{{event.venue.address_1}}</span><br> <span itemprop="locality">{{event.venue.city}}</span>, <span itemprop="region">{{event.venue.state}}</span><br> </span> </address> </div> <div class="col-md-8" itemprop="description"> {{event.description}} </div>
在 Web 浏览器中查看结果时,只要向模板视图添加了占位符,就应显示来自 $scope.event
的值,如图 7 所示。
建立 AngularJS 服务来获取实际、实时的数据以前,必须完成两个与视图相关的简单任务:添加一些 AngularJS 过滤器来格式化日期,并在{{event.description}}
占位符中显示所呈现的 HTML — 而不是原始的、转义的 HTML 代码。 —
AngularJS 过滤器(与相机或 Instagram
滤镜很像)可改变数据的外观。向视图添加过滤器,由于它们会影响模型数据的外观,而不更改内容自己。
经过在数据元素后添加一个竖线 (|
) 和一个过滤器名称,如 {{product_code | uppercase}}
所示,可向模板占位符应用一个过滤器。AngularJS 提供了许多内置的过滤器,包括uppercase
、lowercase
、currency
和number
。您甚至可编写本身的自定义过滤器。
我一直使用的一个过滤器是 date
过滤器,它使您可以使用自定义模式来格式化日期值的外观。
例如,向以前建立的 time
元素应用一个 date
过滤器:
<time class="event-date" itemprop="startDate" datetime="{{event.time | date:'yyyy-MM-ddTHH:mm:ss:Z'}}">{{event.time | date:'EEEE, MMMM, d, yyyy'}}</time>
能够注意到,您为两个不一样的过滤器使用了同一个 event.time
字段。EEEE
代码显示星期几的完整英文,好比Monday。EEE
代码将星期几缩写为 Mon;EE
缩写为 Mo;E
缩写为
M。M
代码一样适用于月份名称。d
代码适用于一月中的某一天,y
代码适用于年。
event.time
字段会在主页上出现屡次。更改 When
的外观以显示小时和 AM/PM 后缀:
<h4>When</h4> <p>{{event.time | date:'h a'}}</p>
MEAN 应用程序中对 AngularJS 过滤器的大量使用,突出了 MVC 设计模式的一个重要原则:模型数据应与任何视图内容独立。
有了正确的 event.time
格式以后,仅剩下 event.description
外观须要修复了。为此,必须让AngularJS 知道它可安全地显示此字段的未转义 HTML。
目前为止使用的全部 JSON 数据都是纯数据,即没有嵌套的 HTML 元素。event.description
字段是一个例外。
任什么时候候您从外部来源收到 HTML(不管是否值得信任)时,都面临着一种潜在的安全风险。包含的 HTML 可带来不想要的 JavaScript库,它们可能向其余网站公开您的数据。
为了防护此风险,AngularJS 会自动清理模板化数据,将它遇到的任何 HTML 元素进行转义,将 “真实的” 尖括号替换为转义的等效表示>
和 <
。此行为不是像前一节中看到的那样的显式过滤,可是一种相似的理念。
对于 event.description
字段,必须告诉 AngularJS,一块儿显示外部 HTML 和本地 HTML是没有问题的。为此,调整您的模板,删除 {{event.description}}
占位符并将它替换为 ng-bind-html
属性:
<div class="col-md-8" itemprop="description" ng-bind-html="event.description"></div>
在浏览器中查看主页时,可见的、转义的 <b>
和 <p>
HTML元素应消失,取而代之的是呈现的文本。
有了控制器、模型和视图,还剩最后一步:将控制器中的模拟 JSON 内容替换为从 Ajax 请求返回的实时数据。要执行这一步,须要向模块添加另外一个元素:一个服务。
使用了一个 AngularJS 服务 来发出 Ajax请求,这是与外部 Meetup.com API 进行交互的完美解决方案。
您可能想要直接从控制器发出 Ajax请求,但这么作有点目光短浅。若是其余控制器中须要该活动数据,该怎么办?您确定不想在控制器之间复制和粘贴源代码,对吧?为了方便跨多个控制器共享相同数据,可建立一个服务。
键入 yo meanjs:angular-service events
来建立一个 events
服务,如清单 12所示。在提示时选择 events
模块。
$ yo meanjs:angular-service events [?] Which module does this service belongs to? articles core events talks users create public/modules/events/services/events.client.service.js
AngularJS 提供了一个名为 $http
的预构建服务来发出 HTTP/Ajax 请求。(全部 AngularJS 服务都有一个$
前缀。)要使用 $http
,需将它注入到您的服务中。在 events
服务能正常运行后,将它注入到EventsController
中。(AngularJS 处处都使用了依赖性注入。)
还记得您在 EventsController
中使用的 $scope
对象吗?$scope
是一个注入到控制器中的服务。如清单 13 所示,注入 $scope
服务的方式是,声明它,而后将它以参数形式传递给函数。
$scope
服务'use strict'; angular.module('events').controller('EventsController', ['$scope', function($scope) { $scope.title = 'High Performance WebSocket'; } ]);
两次键入服务名称彷佛有点多余,但这么作方便了在准备将 MEAN 应用程序部署到生产环境中时发生的精简和串联过程。
您已看到如何注入一个服务,是时候应用这一知识了。打开 public/modules/events/services/events.client.service.js,如清单 14
所示。
'use strict'; angular.module('events').factory('Events', [ function() { // Events service logic // ... // Public API return { someMethod: function() { return true; } }; } ]);
注入 $http
服务,如清单 15 所示。
$http
服务angular.module('events').factory('Events', ['$http', function($http) { // Events service logic // ... // Public API return { someMethod: function() { return true; } }; } ]);
接下来,将 someMethod
更改成 getNextEvent
并删除一些基本功能的存根,如清单 16 所示。
'use strict'; angular.module('events').factory('Events', ['$http', function($http) { // Public API return { getNextEvent: function() { var url = 'http://api.meetup.com/2/events?status=upcoming&order= time&limited_events=False&group_urlname=HTML5-Denver-Users-Group&desc= false&offset=0&photo-host=public&format=json&page=1&fields= &sig_id=13848777&sig=7aa5d53f450ee5449945e8ee89b8cba8968d9e30&callback=JSON_CALLBACK'; var request = $http.jsonp(url); return request; } }; } ]);
(详细的)URL 将返回 HTML5 Denver User Group 的下一场即将举办的活动。(Meetup.com 提供了一个很好的 沙箱 来使用其 API。)若是将该 URL
复制到您的浏览器中,您将得到完整的 JSON 响应。为清楚起见,我编辑了该响应,如清单 17 所示。
{ "results": [ { "status": "upcoming", "visibility": "public", "venue": { "id": 21506832, "name": "Rally Software", "state": "CO", "address_1": "1550 Wynkoop", "city": "Denver" }, "id": "160326502", "time": 1411430400000, "event_url": "http:\/\/www.meetup.com\/HTML5-Denver-Users-Group\/events\/160326502\/", "description": "<p><b>6 pm : \"Developing Offline Applications with HTML 5\" by Venkat Subramaniam<\/b><\/p> ", "name": "\"Developing Offline Applications\" and \"HTML 5 Animations\"" } ], "meta": { "count": 1, "total_count": 3, "next": "http:\/\/api.meetup.com\/2\/events?status=upcoming&sig_id=13848777& order=time&limited_events=False&group_urlname=HTML5-Denver-Users-Group& desc=false&sig=7aa5d53f450ee5449945e8ee89b8cba8968d9e30&photo-host=public&offset=1& format=json&page=1&fields=" } }
结果数组中的 JSON 对象看起来应很熟悉。您删除了 EventsController
中的相似数据。但请注意,完整的 JSON响应包含其余对在主页上呈现数据没有必要的信息(好比 meta
)。幸运的是,在传递 JSON 响应以前可对它执行转换。将转换逻辑添加到events
服务,如清单 18 所示。
// Public API return { getNextEvent: function() { var url = 'http://api.meetup.com/2/events?status=upcoming&order= time&limited_events=False&group_urlname=HTML5-Denver-Users-Group&desc= false&offset=0&photo-host=public&format=json&page=1&fields= &sig_id=13848777&sig=7aa5d53f450ee5449945e8ee89b8cba8968d9e30&callback=JSON_CALLBACK'; var returnFirstElement = function (data, headers) { return data.results[0]; }; var request = $http.jsonp(url, {transformResponse: returnFirstElement}); return request; } }; } ]);
有了转换逻辑,JSON 将仅包含来自结果数组的第一个元素。全部其余额外的 JSON 信息都会丢弃。
为了在开发过程当中提供帮助,可添加 success
和 error
处理函数,如清单 19所示。此代码会将响应数据记录到控制台。您可自由地定义此代码,或者彻底忽略它。
success
和 error
处理函数// Public API return { getNextEvent: function() { var url = 'http://api.meetup.com/2/events?status=upcoming&order= time&limited_events=False&group_urlname=HTML5-Denver-Users-Group&desc= false&offset=0&photo-host=public&format=json&page=1&fields= &sig_id=13848777&sig=7aa5d53f450ee5449945e8ee89b8cba8968d9e30&callback=JSON_CALLBACK'; var returnFirstElement = function (data, headers) { return data.results[0]; }; var request = $http.jsonp(url, {transformResponse: returnFirstElement}); request.success(function(data, status, headers, config) { console.log('SUCCESS'); console.log(data); }); request.error(function(data, status, headers, config) { console.log('ERROR'); console.log(data); }); return request; } }; } ]);
如今 events
服务已完成,能够将它注入到 EventsController
中。修改EventsController
,如清单 20 所示。
events
服务注入到EventsController
中'use strict'; angular.module('events').controller('EventsController', ['$scope', 'Events', function($scope, Events) { $scope.event = undefined; Events.getNextEvent().success(function(data){ $scope.event = data; }); } ]);
若是全部功能都按预期运行,您应在主页上看到一段完整的活动描述,如图 8 所示。若是在演讲描述中看到了比以前模拟的更多的细节,就会知道一切正常。
在主页首次呈现到它向 Meetup.com 发出 Ajax 请求的时间间隔里,您可能注意到了讨厌的 FOUC(Flash of Unstyled Content,无样式内容闪烁)。若是没有看到,刷新浏览器两次,就应该会看到。
FOUC 不是特别重大的错误,但它们无疑会使您的应用程序看起来不太专业。所幸,AngularJS 开发人员为这个常见问题提供了一个简洁的解决方案。
使用 ng-show
对 home.client.view.html 执行最后一次更改,以隐藏视图,直到模型数据就位:
<div class="row center-block event" itemscope itemtype="http://data-vocabulary.org/Event" ng-controller="EventsController" ng-show="event">
将 ng-show
属性添加到 <div>
中,会致使整个 <div>
隐藏,直到填充了 $scope.event
变量。对 Meetup.com 的 Ajax 请求返回 JSON(模型)时,将显示<div>
(视图)。
UGLI 应用程序真正开始成形了。您从外部 API 拉入了 JSON 数据,并使用微数据格式化了获得的视图,以便搜索引擎和其余自动化流程可与查看网页的人访问到相同的信息。
示例代码 wa-mean4.zip
【全栈开发】精通 MEAN: MEAN 堆栈
【全栈开发】精通 MEAN: 了解一个 MEAN 应用程序
【全栈开发】精通 MEAN: 使用 MEAN 和 UGLI CRUD 实现响应式 Web 设计