前文 最近面試有一間公司要求使用Restcountries API 使用CRUD前端Html串接API,有看我文章的夥伴應該知道我大多是研究後端或CI/CD相關技術,對於前端技術較少研究,這次我打算使用vue.js來完成此次需求.
需求如下
分頁 
顯示國家相關資訊 
排序效果 
點選國家名稱進入Detail頁面 
 
因為以上幾點都是CRUD相關操作,關於CRUD相關操作使用三大框架就很適合(所以我選擇使用Vue)
話不多說先給大家看看成品 RestcountriesSample 
Source Code 
要使用的API介紹 雖然官網對於API介紹雖少,但我相信只要有常串API的人應該可以很快猜出每個API作用.
而且我發現大部分API都可以用GET來請求.
 
只要用這兩個就可以完成我們的需求
Code解說與問題分析 一開始我在分析問題是要找尋合適的API後面經過塞選挑出上面兩個API.
接下來我就考慮把畫面用Table + 分頁方式呈現,而Detail Page利用Query String方式傳Country Name來看明細資料.
我用Pure前端串接API,所以我建立兩個Html頁面
一個是Master Page 
一個是Detail Page 
 
Master page 
在Javascript code我主要介紹流程
主要在一開始頁面建立時去Load All  資料並把資料binding在rows陣列物件
orderBy方法,提供一個排序實現這邊可以讓Page呼叫時傳入要排的欄位名稱就可以不用HardCode(使用類似@click="orderBy('name'),ASC *= -1")傳入Name就可以對於Name來排序,提高程式碼可用性
因為API請求有時候會比較久,所以我這邊使用vue-loading-overlay 來當Loading Page(有興趣的可以在查閱此連結的API)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 <div  id ="app" > <template >     <div  class ="vld-parent" >      <loading  :active.sync ="isLoading"  :is-full-page ="true" > </loading >      </div >  </template > <div > <b > Search Country Name:</b >  <input  type ="text"  v-model ="countryName" > </div > <div  v-if ="filteredRows.length === 0" > No Data Display!!</div > <table  v-if ="filteredRows.length > 0"  class ="table table-condensed" >     <thead >      <tr >          <th > 國旗</th >          <th  @click ="orderBy('name'),ASC *= -1" > 國家名稱         <span  class ="icon"  :class ="{'Reverse':ASC==1}" >              <i  class ="fa fa-angle-up" > </i >          </span >          </th >          <th > 2位國家代碼</th >          <th > 3位國家代碼</th >          <th > 母語名稱</th >          <th > 替代國家名稱</th >          <th > 國際電話區號</th >      </tr >      </thead >      <tr  v-for ="item in filteredRows.slice(pageStart, pageStart + pageSize)" >      <td > <img  v-bind:src =item.flag  style ='height:150px' > </td >      <td >          <a  target ="_blank"  :href ="'./CountryModel.html?countryName=' + item.name" >          {{ item.name }}         </a >      </td >      <td > {{ item.alpha2Code }}</td >      <td > {{ item.alpha3Code }}</td >      <td > {{ item.nativeName }}</td >      <td > {{ item.altSpellings[0] }}</td >      <td > {{ item.callingCodes[0] }}</td >      </tr >  </table > <div  class ="pagination" >     <ul >      <li  v-bind:class ="{'disabled': (currPage === 1)}"  @click.prevent ="setPage(currPage-1)" > <a  href ="#" > Prev</a > </li >      <li  v-for ="n in totalPage"  v-bind:class ="{'active': (currPage === (n))}"  @click.prevent ="setPage(n)" > <a           href ="#" > {{n}}</a > </li >     <li  v-bind:class ="{'disabled': (currPage === totalPage || totalPage === 0)}"           @click.prevent ="setPage(currPage+1)" > <a  href ="#" > Next</a > </li >     </ul >  </div > </div > 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 var  app = new  Vue ({    el : '#app' ,     data : {       rows : [],       pageSize : 25 ,       currPage : 1 ,       countryName : '' ,       ASC : 1 ,       isLoading : true      },     computed : {       filteredRows : function  (         var  self = this ;         return  self.rows .filter (x =>countryName  || x.name .search (self.countryName ) != -1 );       },       pageStart : function  (         return  (this .currPage  - 1 ) * this .pageSize ;       },       totalPage : function  (         return  Math .ceil (this .filteredRows .length  / this .pageSize );       }     },     methods : {       setPage : function  (index ) {         if  (index <= 0  || index > this .totalPage ) {           return ;         }         this .currPage  = index;       },       orderBy : function  (item ) {         var  self = this ;         return  self.rows .sort (function  (obj1, obj2 ) {           var  obj1 = obj1[item]           var  obj2 = obj2[item]           if  (obj1 === obj2)             return  0 ;           else  if  (obj1 > obj2)             return  self.ASC ;           else              return  self.ASC  * -1 ;         });       }     },     created : function  (       var  self = this ;       $.get ('https://restcountries.eu/rest/v2/all' , function  (data ) {         self.rows  = data;         self.isLoading  = false ;       });     },     watch :{       countryName :function (newValue ){         this .currPage  = 1 ;       }     } }); 
Detail Page 
Detail我使用FULL NAME 來查找我要的國家明細
Detail Html畫面,我就不多說可以看原始碼
因為我在設計時想要使用QueryString來傳送CountryName,所以我利用URLSearchParams來取得QueryString countryName資料並使用Ajax查詢API
如果查不到資料或使用者傳送一個不存在的資訊,我就會顯示No Data Display!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 var  app = new  Vue ({    el : '#app' ,     data : {       vm : {},       isLoading : true      },     created : function  (       var  self = this ;       let  urlParams = new  URLSearchParams (window .location .search );       var  countryName = urlParams.has ('countryName' ) ? urlParams.get ('countryName' ) : '' ;       var  url = 'https://restcountries.eu/rest/v2/name/' +encodeURI (countryName)+'?fullText=true'        $.get (url, function  (data ) {         self.vm  = data[0 ];         self.isLoading  = false ;       }).fail (function (         document .write ('No Data Display!!' );       });     }   }); 
小結 這次題目我前後大約花半天就把東西從無到有完成,個人覺得還算蠻順利的,但我寫的Front Code可能不太標準(因為我很少寫Js XDD)
如果有寫得不好的地方在歡迎指教
不得不說我覺得Vuejs寫起來真的蠻直覺,而且很多資源可以查閱學習來相對蠻容易的
相是Loading Page就有很多不同的樣式可以挑選.
__此文作者__:Daniel Shih(石頭)https://isdaniel.github.io/vue-first-Restcountries/  CC BY-NC-SA 3.0 TW  許可協議。轉載請註明出處!