AngularJS Phonecat (步驟10-步驟12)

導(dǎo)言


最近在學(xué)AngularJS的實例教程PhoneCat Tutorial App,發(fā)現(xiàn)網(wǎng)上的中文教程都比較久遠(yuǎn)麦射,與英文版對應(yīng)不上蛾娶,而且缺少組件和文件重構(gòu)兩節(jié)。所以決定自己整理一個中文簡明教程潜秋。

此篇為10-12節(jié)蛔琅。

上一篇:AngularJS Phonecat (步驟8-步驟9)

10 更多模板


在這一步中,我們將實現(xiàn)手機(jī)詳情視圖半等,當(dāng)用戶手機(jī)列表上的某一項時顯示揍愁。我們將使用的$ HTTP來獲取我們的數(shù)據(jù)呐萨,并修改phoneDetail組件的模板。

數(shù)據(jù)

除了phoens.json莽囤,app/phones/文件也包含每款手機(jī)的JSON文件:

app/phones/nexus-s.json: (一個例子)

{
  "additionalFeatures": "Contour Display, Near Field Communications (NFC), ...",
  "android": {
    "os": "Android 2.3",
    "ui": "Android"
  },
  ...
  "images": [
    "img/phones/nexus-s.0.jpg",
    "img/phones/nexus-s.1.jpg",
    "img/phones/nexus-s.2.jpg",
    "img/phones/nexus-s.3.jpg"
  ],
  "storage": {
    "flash": "16384MB",
    "ram": "512MB"
  }
}

這些文件使用相同的數(shù)據(jù)結(jié)構(gòu)描述手機(jī)的各種特性谬擦,我們要將這些信息顯示到手機(jī)詳情視圖中鳍置。

組件控制器

我們利用$http服務(wù)請求JSON文件床未,來增強(qiáng)手機(jī)詳情組件的控制器任柜。這與手機(jī)列表控件控制器的工作原理相同乳愉。

app/phone-detail/phone-detail.component.js:

angular.
  module('phoneDetail').
  component('phoneDetail', {
    templateUrl: 'phone-detail/phone-detail.template.html',
    controller: ['$http', '$routeParams',
      function PhoneDetailController($http, $routeParams) {
        var self = this;

        $http.get('phones/' + $routeParams.phoneId + '.json').then(function(response) {
          self.phone = response.data;
        });
      }
    ]
  });

為了構(gòu)建HTTP的URL請求,我們使用了$routeParams.phoneId剥纷,它是通過$route服務(wù)從當(dāng)前路由中提取出來的锄蹂。

組件模板

前面用占位符粗略定義的模板已經(jīng)被替換成一個成熟的外部模板了袁,該模板包含手機(jī)列表和手機(jī)詳情的數(shù)據(jù)綁定最筒。
注意贺氓,我們使用{{表達(dá)式}}和ngRepeat將數(shù)據(jù)模型中的手機(jī)信息傳送到視圖。

app/phone-detail/phone-detail.template.html:

<img ng-src="{{$ctrl.phone.images[0]}}" class="phone" />

<h1>{{$ctrl.phone.name}}</h1>

<p>{{$ctrl.phone.description}}</p>

<ul class="phone-thumbs">
  <li ng-repeat="img in $ctrl.phone.images">
    <img ng-src="{{img}}" />
  </li>
</ul>

<ul class="specs">
  <li>
    <span>Availability and Networks</span>
    <dl>
      <dt>Availability</dt>
      <dd ng-repeat="availability in $ctrl.phone.availability">{{availability}}</dd>
    </dl>
  </li>
  ...
  <li>
    <span>Additional Features</span>
    <dd>{{$ctrl.phone.additionalFeatures}}</dd>
  </li>
</ul>
tutorial_10.png

測試

我們編寫了一個新的單元測試床蜘,與第7節(jié)中phoneList組件控制器的測試類似辙培。

app/phone-detail/phone-detail.component.spec.js:

describe('phoneDetail', function() {

  // 在每次測試前,加載包含`phoneDetail`組件的功能
  beforeEach(module('phoneDetail'));

  // 測試控制器
  describe('PhoneDetailController', function() {
    var $httpBackend, ctrl;

    beforeEach(inject(function($componentController, _$httpBackend_, $routeParams) {
      $httpBackend = _$httpBackend_;
      $httpBackend.expectGET('phones/xyz.json').respond({name: 'phone xyz'});

      $routeParams.phoneId = 'xyz';

      ctrl = $componentController('phoneDetail');
    }));

    it('should fetch the phone details', function() {
      expect(ctrl.phone).toBeUndefined();

      $httpBackend.flush();
      expect(ctrl.phone).toEqual({name: 'phone xyz'});
    });

  });

});

我們也增加了一個端到端測試:導(dǎo)航到'Nexus S' 詳情頁邢锯,驗證頁面頭部是否為"Nexus S"扬蕊。

e2e-tests/scenarios.js

...

describe('View: Phone detail', function() {

  beforeEach(function() {
    browser.get('index.html#!/phones/nexus-s');
  });

  it('should display the `nexus-s` page', function() {
    expect(element(by.binding('$ctrl.phone.name')).getText()).toBe('Nexus S');
  });

});

...

命令行中輸入<code>npm run protractor</code>既可運(yùn)行。

11 自定義轉(zhuǎn)換器


這一節(jié)丹擎,我們要創(chuàng)建一個自定義顯示轉(zhuǎn)換器尾抑。
上一節(jié),詳情頁面直接用"true"和"false"來顯示某個手機(jī)特性是否被支持蒂培,在這里再愈,我們將定制一個轉(zhuǎn)換器將字符轉(zhuǎn)成圖形,符號:? 對應(yīng) "true", ? 對應(yīng) "false"毁渗。

檢查標(biāo)識轉(zhuǎn)換器

由于該轉(zhuǎn)換器是通用的(不是只用于單個視圖或組件)践磅,所以我們將它注冊到覆蓋應(yīng)用程序范圍的核心模塊。

app/core/core.module.js:

angular.module('core', []);

app/core/checkmark/checkmark.filter.js:

angular.
  module('core').
  filter('checkmark', function() {
    return function(input) {
      return input ? '\u2713' : '\u2718';
    };
  });

我們的轉(zhuǎn)換器叫做"checkmark"灸异,輸入的值為trur或false。返回結(jié)果是unicode字符:true (\u2713 -> ?) 或者false (\u2718 -> ?)羔飞。

現(xiàn)在轉(zhuǎn)換器已經(jīng)OK肺樟,接著需要注冊其核心模塊,作為主模塊phonecatApp的依賴逻淌。

app/app.module.js:

angular.module('phonecatApp', [
  ...
  'core',
  ...
]);

模板

我們已經(jīng)創(chuàng)建了兩個新文件(core.module.js, checkmark.filter.js)么伯,還需要將它們引入我們的布局模板中。
app/index.html:

...
<script src="core/core.module.js"></script>
<script src="core/checkmark/checkmark.filter.js"></script>
...

轉(zhuǎn)換器的語法:

{{expression | filter}}

將轉(zhuǎn)換器引入手機(jī)詳情模板:

app/phone-detail/phone-detail.template.html:

...
<dl>
  <dt>Infrared</dt>
  <dd>{{$ctrl.phone.connectivity.infrared | checkmark}}</dd>
  <dt>GPS</dt>
  <dd>{{$ctrl.phone.connectivity.gps | checkmark}}</dd>
</dl>
...

測試

app/core/checkmark/checkmark.filter.spec.js:

describe('checkmark', function() {

  beforeEach(module('core'));

  it('should convert boolean values to unicode checkmark or cross',
    //注入轉(zhuǎn)換器
    inject(function(checkmarkFilter) {
    //檢查轉(zhuǎn)換器字符串與unicode編碼是否對應(yīng)
      expect(checkmarkFilter(true)).toBe('\u2713');
      expect(checkmarkFilter(false)).toBe('\u2718');
    })
  );

});

在每次測試前卡儒,beforeEach(module('core')) 加載了核心模板(包含checkmark轉(zhuǎn)換器)田柔。
我們還調(diào)用了輔助功能函數(shù)inject(function(checkmarkFilter) { ... })來訪問待測試的轉(zhuǎn)換器俐巴。具體功能函數(shù)請參閱angular.mock.inject

注入時硬爆,轉(zhuǎn)換器名稱需要加后綴"Filter"欣舵。比如,checkmark轉(zhuǎn)化器以checkmarkFilter注入缀磕。更多內(nèi)容缘圈,請參閱Filters

12 事件處理


在這一步中袜蚕,我們會增加可點(diǎn)擊的手機(jī)圖片糟把,點(diǎn)擊后進(jìn)入手機(jī)詳情頁。手機(jī)詳情視圖展示一個當(dāng)前手機(jī)的大圖和其他手機(jī)的縮略圖牲剃。點(diǎn)擊縮略圖會切換大圖遣疯。

組件控制器

app/phone-detail/phone-detail.component.js:

...
controller: ['$http', '$routeParams',
  function PhoneDetailController($http, $routeParams) {
    var self = this;

    self.setImage = function setImage(imageUrl) {
      self.mainImageUrl = imageUrl;
    };

    $http.get('phones/' + $routeParams.phoneId + '.json').then(function(response) {
      self.phone = response.data;
      self.setImage(self.phone.images[0]);
    });
  }
]
...

在phoneDetail控制器中,我們創(chuàng)建了一個mainImageUrl模型屬性凿傅,并且設(shè)置默認(rèn)值為第一個手機(jī)圖片的URL另锋。而setImage()是事件處理程序,用于改變mainImageUrl狭归。

組件模板

app/phone-detail/phone-detail.template.html:

<img ng-src="{{$ctrl.mainImageUrl}}" class="phone" />
...
<ul class="phone-thumbs">
  <li ng-repeat="img in $ctrl.phone.images">
    <img ng-src="{{img}}" ng-click="$ctrl.setImage(img)" />
  </li>
</ul>
...
  • 大圖片的ngSrc指令與$ctrl.mainImageUrl屬性綁定夭坪。
  • 縮略圖注冊ngClick事件處理程序。當(dāng)用戶點(diǎn)擊縮略圖時过椎,事件處理程序會調(diào)用$ctrl.setImage() 函數(shù)室梅,將$ctrl.mainImageUrl屬性改為縮略圖的url。從而改變大圖內(nèi)容疚宇。
tutorial_12.png

測試

為了驗證新特性亡鼠,增加了兩個端到端測試。一個驗證mainImageUrl默認(rèn)值是第一張手機(jī)圖片的url敷待。另一個驗證點(diǎn)擊縮略圖時间涵,大圖的url會跟著改變(即大圖可正常切換)。

e2e-tests/scenarios.js:

...

describe('View: Phone detail', function() {

  ...
//驗證大圖是第一張手機(jī)圖片
  it('should display the first phone image as the main phone image', function() {
    var mainImage = element(by.css('img.phone'));

    expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.0.jpg/);
  });
//驗證圖片切換
  it('should swap the main image when clicking on a thumbnail image', function() {
    var mainImage = element(by.css('img.phone'));
    var thumbnails = element.all(by.css('.phone-thumbs img'));

    thumbnails.get(2).click();
    expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.2.jpg/);

    thumbnails.get(0).click();
    expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.0.jpg/);
  });

});

...

命令行輸入<code>npm run protractor</code>榜揖,運(yùn)行測試勾哩。

我們還要重構(gòu)單元測試,因為這一步phoneDetial添加了mainImageUrl模型屬性举哟。與之前一樣思劳,我們會在測試中使用模擬響應(yīng)。

app/phone-detail/phone-detail.component.spec.js:

...

describe('controller', function() {
  var $httpBackend, ctrl
  var xyzPhoneData = {
    name: 'phone xyz',
    images: ['image/url1.png', 'image/url2.png']
  };

  beforeEach(inject(function($componentController, _$httpBackend_, _$routeParams_) {
    $httpBackend = _$httpBackend_;
    $httpBackend.expectGET('phones/xyz.json').respond(xyzPhoneData);

    ...
  }));

  it('should fetch phone details', function() {
    expect(ctrl.phone).toBeUndefined();

    $httpBackend.flush();
    expect(ctrl.phone).toEqual(xyzPhoneData);
  });

});

...

就這樣妨猩,我們的單元測試也完成了潜叛。

下一篇:AngularJS Phonecat (步驟13-步驟14)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子威兜,更是在濱河造成了極大的恐慌销斟,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件椒舵,死亡現(xiàn)場離奇詭異蚂踊,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)逮栅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門悴势,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人措伐,你說我怎么就攤上這事特纤。” “怎么了侥加?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵捧存,是天一觀的道長。 經(jīng)常有香客問我担败,道長昔穴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任提前,我火速辦了婚禮吗货,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘狈网。我一直安慰自己宙搬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布拓哺。 她就那樣靜靜地躺著勇垛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪士鸥。 梳的紋絲不亂的頭發(fā)上闲孤,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天,我揣著相機(jī)與錄音烤礁,去河邊找鬼讼积。 笑死,一個胖子當(dāng)著我的面吹牛鸽凶,可吹牛的內(nèi)容都是我干的币砂。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼玻侥,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了亿蒸?” 一聲冷哼從身側(cè)響起凑兰,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤掌桩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后姑食,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體波岛,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年音半,在試婚紗的時候發(fā)現(xiàn)自己被綠了则拷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡曹鸠,死狀恐怖煌茬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情彻桃,我是刑警寧澤坛善,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站邻眷,受9級特大地震影響眠屎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜肆饶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一改衩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧驯镊,春花似錦葫督、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至洒放,卻和暖如春蛉鹿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背往湿。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工妖异, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人领追。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓他膳,卻偏偏與公主長得像,于是被迫代替她去往敵國和親绒窑。 傳聞我的和親對象是個殘疾皇子棕孙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內(nèi)容