【全棧開發】精通 MEAN: 當 MEAN 遇到 Meetup.com 和微數據

首先,未來自 Meetup.com 的即將舉辦的活動信息添加到 UGLI 主頁。css

HTML 和微數據

若是訪問 Meetup.com 上的 HTML5 DenverUsers Group,您會看到一個相似圖 1 的網頁。html

圖 1. 一場即將舉辦的 HTML5 Denver User Group 會議的 Meetup.com 信息

一場即將舉辦的 HTML5 Denver User Group 會議的 Meetup.com 信息的屏幕截圖.png

能夠看到,使用即將舉辦的活動信息進一步自定義 UGLI 主頁所需的全部原始材料,都在 Meetup.com 頁面上(即將發表的演講的標題、會議時間和地點,等等),但Meetup.com 頁面的設計與該數據緊密耦合。您想要提取並在 MEAN 應用程序中重用的信息,與 HTML 元素雜亂地混在一塊兒,如清單 1 所示。(爲了清晰性和簡潔性,我對HTML 進行了編輯。)angularjs

清單 1. Meetup.com HTML源代碼

<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 所示。最基本的語義層我剛纔已討論:一項是一個列表項,另外一項是一個超連接。這就是文檔 語義。另外一層是事件語義,由 eventTypeurlsummary等關鍵字表示。這些關鍵字與文檔的呈現方式毫無關係。它們向搜索引擎(和查看 HTML 源代碼的初學者)暗示信息的 「更高級含義」。這些屬性是 HTML Microdata 規範的一部分。(有關微數據及其元數據前身的更多信息,請參閱 Microformats、microdata 和 RDFa!哦天哪! 邊欄。)json

這個特定的列表項中僅包含一個 event 的信息。搜索引擎知道此事實,是由於設計該頁面的人向該列表項添加了itemtype="http://data-vocabulary.org/Event"。該頁面包含許多任意超連接,其中帶有
itemprop="url" 的超連接是 event自己的連接。eventTypeMeetup— 一個任意字符串,但 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/Eventapi

向主頁添加一個佔位符事件

大致瞭解 Meetup.com 如何顯示事件信息後,可對 uGLI 應用程序執行相同操做。數組

在測試應用程序的 root 中鍵入 mongod 來啓動 MongoDB,而後鍵入 grunt 啓動 Web 應用程序。在 Web瀏覽器中訪問 http://localhost:3000 時,您應看到在上一期中自定義的主頁(如圖 2 所示)。

圖 2. UGLI 主頁

UGLI 主頁.png

我想向主頁添加即將舉辦的 HTML5 Denver User Group活動。本教程的剩餘部分將迭代式地完成此工做。首先,添加一些靜態佔位符數據來描繪頁面的基本外觀的線框圖。

在文本編輯器中打開 public/modules/core/views/home.client.view.html。在標語下,添加活動的新標記,如清單 2 所示。

清單 2. public/modules/core/views/home.client.view.html

<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。

圖 3. UGLI 主頁線框圖

UGLI 主頁線框圖.png

有了基本的 HTML

默認樣式更美觀。爲此,須要添加一些語義。

Bootstrap 提供了您使用的默認結構化 HTML 元素(好比 <div><h3>)的內置樣式。它還提供了一些不屬於默認 HTML 元素的附加的結構化類,好比 row
col

初學的 Web 開發人員經常會考慮網頁的結構,而不是顯示的信息。結果是,他們使用像 big-red-italicleft-column-header 這樣的名稱來編寫自定義 CSS 類。從語法上講該方法並無錯,但我發現使用像event-dateevent-location這樣的語義名稱時,網站的長期維護更容易。這樣,當客戶在一年後返回要求將全部分類彙總標爲綠色而不是紅色時,我可編寫一個 CSS 類來識別顯示了哪些內容
(subtotals),而不是 如何顯示(green-body-text)。我也不太可能不經意地更改頁面上其餘剛好也使用了 green-body-text CSS規則的元素。

返回到您剛編寫的 HTML,添加一些具備合適的語義的 CSS 類,好比 eventevent-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-*),哪些類是通用的(rowcenter-block)。

將這些類添加到 HTML 後,就能夠定義一些自定義 CSS 規則了。在文本編輯器中打開 public/modules/core/css/core.css。由於每一個模塊都擁有本身的CSS,因此可保持 「面向組件」 的理念。並且經過抓取整個子目錄樹,更容易在項目間共享模塊。

添加 CSS 樣式規則,如清單 3 所示。

清單 3. public/modules/core/css/core.css

.event {
    width: 75%;
}

.event-date {
    font-style: italic;
}

.event-title {
    margin-top: 0;
}

如今您的主頁看起來沒那麼粗糙,更加美觀了,如圖 4 所示。

圖 4. 帶 CSS 樣式的 UGLI 主頁

帶 CSS 樣式的 UGLI 主頁.png

最後,再次返回添加微數據元數據。可否跳過一步,在 CSS 規則中使用微數據元素,而不定義自定義元素?固然能夠。但我想將它們分開。畢竟,它們具備兩種不一樣的用途。一個用於 CSS樣式,另外一個用於搜索引擎優化 (SEO)。假設您 5年前遇到一個用戶案例,要求您將微數據遷移到另外一個更新的規範。若是在知足案例要求的同時,帶來了影響網站外觀的反作用,就太遺憾了。這兩個特性應是徹底不一樣的。

再一次在文本編輯器中打開 public/modules/core/views/home.client.view.html。爲該活動添加新的語義微數據標記,如清單 4所示。(有關使用微數據標記活動的範例,請參閱 富代碼段 - 活動。)

清單 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 識別爲同一個日期。但計算機更加註重字面內容,像這樣的細小的格式更改可能致使重大的故障。在這個示例中,您執行了多處更改來消除歧義:

  • 您將 HTML 元素從通用的 <div> 升級爲更具體的 <time>(一個新的 HTML5元素)。
  • CSS event-date 類識別數據的內容,而不是外觀。* itemprop="startDate" 微數據屬性將此日期識別爲 eventstartDate。* datetime 屬性(HTML5 <time> 元素的一部分)清晰地代表該時間爲 ISO 8601格式。這樣,既可提供一種機器可以使用的時間,又可提供一種用於顯示和人類使用的格式良好的時間。

對 HTML 的最大更改包括活動的地址,如清單 5 所示。您在 Event 模式內嵌套了多種新模式—Organization and Address

清單 5. 添加組織和地址的微數據

<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
所示:

圖 5. 使用 Semantic inspector 查看微數據

使用 Semantic inspector 查看微數據的屏幕截圖.png

如今您已有標註了微數據屬性的基本的 HTML 線框圖,是時候從這些連線中填入全新的 JSON 了。爲此,建立一個新的事件模塊,其中包含控制器、視圖、模型和服務。

建立一個 AngularJS 模塊

上一期 中,您使用 Yeoman 搭建了一個完整的 CRUD 模塊,包括 Express 路由和一個 Mongoose模型。對於此用戶案例,不須要服務器端基礎架構,由於原始 JSON 數據來自外部 Web 應用程序。幸運的是,MeanJS Yeoman生成器的建立者預見到了這一需求,單單爲應用程序的客戶端 AngularJS 部分提供了另外一個生成器。

鍵入 yo meanjs:angular-module events 來建立一個名爲 events 的新 AngularJS模塊。AngularJS 模塊是特定於您應用程序中一種特定數據類型的文件的邏輯分組。根據官方 AngularJS 文檔的描述,「可將模塊視爲您應用程序的不一樣部分(控制器、服務、過濾器、指令等)的容器。」

收到提示時,選擇列表中的全部元素,如清單 6 所示。

清單 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
模塊中使用每一個目錄,但知道有一個容易記住、容易理解的地方來在時機成熟時放入模塊的各部分,也很不錯。

下一步是向模塊添加一個控制器。

建立一個 AngularJS 控制器

搭建一個模塊後,搭建一個控制器也很是容易。鍵入 yo meanjs:angular-controller events 並選擇
events 模塊,如清單 7 所示。

清單 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 所示。

清單 8. 一個空的、無存根的 AngularJS控制器

'use strict';

angular.module('events').controller('EventsController', ['$scope',
  function($scope) {
    // Events controller logic
    // ...
  }
]);

稍後,咱們會將此控制器綁定到一個特定的 DOM 元素。$scope 變量將負責將模型傳遞給視圖的重要工做。

$scope 添加一個 title 變量(模型),如清單 9 所示。

清單 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 所示。

圖 6. 佔位符替換爲了實際文本

該屏幕截圖顯示佔位符替換爲了實際文本.png

如今您已有一個簡單、有效的示例,是時候詳細分析分析它了。(換句話說,您的應用程序已能正常工做,是時候再次破壞它了。)若是願意,可向 $scope添加許多變量,這些變量能夠是簡單的單個值,或者完整的 JSON 對象。

很快,您將會看到如何向 Meetup.com 發出一個 HTTP 請求,以檢索下一個即將舉辦的活動的 JSON。到那時,在 $scope中添加一些簡化的模擬數據,以模擬您將從實際的 Ajax 調用獲取的數據,如清單 10 所示。

清單 10. 模擬的 JSON 響應

'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 所示。

清單 11. 向 HTML 添加更多佔位符

<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 所示。

圖 7. 包含模擬的 JSON 數據的 UGLI 主頁

包含模擬的 JSON 數據的 UGLI 主頁.png

建立 AngularJS 服務來獲取實際、實時的數據以前,必須完成兩個與視圖相關的簡單任務:添加一些 AngularJS 過濾器來格式化日期,並在{{event.description}} 佔位符中顯示所呈現的 HTML — 而不是原始的、轉義的 HTML 代碼。 —

添加 AngularJS 過濾器

AngularJS 過濾器(與相機或 Instagram
濾鏡很像)可改變數據的外觀。向視圖添加過濾器,由於它們會影響模型數據的外觀,而不更改內容自己。

經過在數據元素後添加一個豎線 (|) 和一個過濾器名稱,如 {{product_code | uppercase}}
所示,可向模板佔位符應用一個過濾器。AngularJS 提供了許多內置的過濾器,包括
uppercaselowercasecurrency
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。

顯示未轉義的 HTML

目前爲止使用的全部 JSON 數據都是純數據,即沒有嵌套的 HTML 元素。event.description 字段是一個例外。

任什麼時候候您從外部來源收到 HTML(不管是否值得信任)時,都面臨着一種潛在的安全風險。包含的 HTML 可帶來不想要的 JavaScript庫,它們可能向其餘網站公開您的數據。

爲了防護此風險,AngularJS 會自動清理模板化數據,將它遇到的任何 HTML 元素進行轉義,將 「真實的」 尖括號替換爲轉義的等效表示&gt;&lt;。此行爲不是像前一節中看到的那樣的顯式過濾,可是一種相似的理念。

對於 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 模塊。

清單 12. 生成一個 AngularJS 服務

$ 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 服務的方式是,聲明它,而後將它以參數形式傳遞給函數。

清單 13. 注入 $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
所示。

清單 14. public/modules/events/services/events.client.service.js

'use strict';

angular.module('events').factory('Events', [
    function() {
        // Events service logic

        // ...

        // Public API
        return {
            someMethod: function() {
                return true;
            }
        };
    }
]);

注入 $http 服務,如清單 15 所示。

清單 15. 注入 $http 服務

angular.module('events').factory('Events', ['$http',
        function($http) {
            // Events service logic
            // ...

            // Public API
            return {
                someMethod: function() {
                    return true;
                }
            };
        }
    ]);

接下來,將 someMethod 更改成 getNextEvent 並刪除一些基本功能的存根,如清單 16 所示。

清單 16. 從 Ajax 調用返回 JSON

'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 所示。

清單 17. 來自 Meetup.com 的 API 的 JSON 響應

{
  "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 所示。

清單 18. 轉換 JSON 響應

// 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 信息都會丟棄。

爲了在開發過程當中提供幫助,可添加 successerror 處理函數,如清單 19所示。此代碼會將響應數據記錄到控制檯。您可自由地定義此代碼,或者徹底忽略它。

清單 19. 添加 successerror 處理函數

// 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 所示。

清單 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 所示。若是在演講描述中看到了比以前模擬的更多的細節,就會知道一切正常。

圖 8. 完整、有效的示例的實際應用

完整、有效的示例的實際應用.png

隱藏閃爍的無樣式內容

在主頁首次呈現到它向 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系列前3篇文章:

【全棧開發】精通 MEAN: MEAN 堆棧
【全棧開發】精通 MEAN: 瞭解一個 MEAN 應用程序
【全棧開發】精通 MEAN: 使用 MEAN 和 UGLI CRUD 實現響應式 Web 設計

原文出處:精通 MEAN: 當 MEAN 遇到 Meetup.com 和微數據

相關文章
相關標籤/搜索