使用AngularJS制作一個(gè)簡(jiǎn)單的RSS閱讀器的教程
簡(jiǎn)介
幾年前,我用C#寫了一個(gè)RSS閱讀器,但是我想如果把它做成一個(gè)SPA(單頁應(yīng)用)效果會(huì)更好。 Angular使一些事情變得簡(jiǎn)單,RSS閱讀器就是其中之一。 我也用Twitter Bootstrap(做UI)實(shí)現(xiàn)了RSS閱讀器,調(diào)試頁面樣式是最難的地方之一...可能是因?yàn)槲也簧瞄L(zhǎng)css的原因。
背景
我有一些自己喜歡的網(wǎng)站( CodeProject, Dr.Dobb's Journal, ComputerWorld, Inc. Magazine)。 然而,我發(fā)現(xiàn)其中很多網(wǎng)站都有煩人的廣告、風(fēng)格不好的布局,我實(shí)在不愿意看到這些東西。當(dāng)我說這話的時(shí)候,并不包括 CodeProject網(wǎng)站。
在這些網(wǎng)站之間來回切換浪費(fèi)了很多時(shí)間。 因此我更喜歡瀏覽文章標(biāo)題和簡(jiǎn)介,這樣我可以決定是否進(jìn)入文章內(nèi)容頁面。 這就是我決定寫FreedReadR 單頁應(yīng)用的原因。
FreedReadR 響應(yīng)是比較快的,因?yàn)樗x取的數(shù)據(jù)量(RSS源)比較小。
下面是點(diǎn)擊CodeProject選項(xiàng)的效果圖:
下面是FreedReadR 加載某一個(gè)站點(diǎn)數(shù)據(jù)的效果圖:
你現(xiàn)在可以試下效果:
http://newtonsaber.com/FreedReadR
差點(diǎn)忘了,我在創(chuàng)建自己的RSS 閱讀器之前在Google上搜索了這個(gè)想法,發(fā)現(xiàn)jsfiddle中一段比較好的代碼: angularJS Feed Reader alt.
我的代碼和它的代碼有相似的地方,但仍有不同,因?yàn)槲蚁胍獙?shí)現(xiàn)更多的功能。 FreedReadR 允許你本地存儲(chǔ)自己的RSS源數(shù)據(jù),這樣你就可以一直使用應(yīng)用來創(chuàng)建自定義的RSS源。 另外,它的代碼基于Twitter Bootstrap 2,F(xiàn)reedReadR 基于新版本Twitter Bootstrap 3。
使用代碼
如果你熟悉Angular,開發(fā)時(shí)代碼并不多。 大部分的難點(diǎn)是在Angular中使用Bootstrap。
其它問題可以在”Angular編程思想”中找到解決方法。$scope 的用法和控制器工作的方式有點(diǎn)不同。 首先你必須在html中設(shè)置應(yīng)用程序的作用域。 類似下面的使用ng-app="FreedReadR"的代碼,設(shè)置了html中$scope的作用域:所有div標(biāo)簽內(nèi)的對(duì)象 –- 在下面的示例中作用域是整個(gè)頁面。 我只需要一個(gè)控制器來處理整個(gè)應(yīng)用程序邏輯,我對(duì)這一點(diǎn)比較滿意。
<body ng-app="FreedReadR"> <div data-ng-controller="FeedCtrl"> <h4>RSS Feed Reader using AngularJS</h4> <form class="form-horizontal col-md-12" role="form"> <div class = "row"> <div class="col-md-6">
在上面的html代碼中,你可以看到Angular 應(yīng)用模板的名稱是FreedReadR。 當(dāng)我設(shè)置應(yīng)用程序模板、添加控制器(FeedCtrl) 代碼時(shí),我在main.js文件中使用了相同的名字。讓我們看一下main.js中設(shè)置參數(shù)的代碼。
var app = angular.module('FreedReadR', []); app.factory('FeedService',function($http){ return { parseFeed : function(url){ return $http.jsonp('//ajax.googleapis.com/ajax/services/feed/load?v=1.0&num=50&callback=JSON_CALLBACK&q=' + encodeURIComponent(url)); } } }); app.controller("FeedCtrl", ['$scope','FeedService', FeedCtrl]);
在上面的js代碼中,第一行是創(chuàng)建AngularJS 應(yīng)用模板。 注意,它的名稱是FreedReadR,我們?cè)趆tml代碼中使用相同的名稱以引用這個(gè)模板。
接下來,我們創(chuàng)建一個(gè)Angular工廠類,后面會(huì)用它訪問RSS源URL來獲取真實(shí)的源數(shù)據(jù)。 認(rèn)真地看下代碼中使用的 $http.jsonp請(qǐng)求。URL格式如下:
//ajax.googleapis.com/ajax/services/feed/load?v=1.0&num=50&callback=JSON_CALLBACK&q=
URL里調(diào)用了Google API,以前我并不知道Google API。這就是我在文章開頭提到的jsfiddle示例的主要代碼。 如果你想更多地了解Google API,你可以在這里下載:https://developers.google.com/feed/v1/jsondevguide。
在上面的js代碼中,你會(huì)發(fā)現(xiàn)我們調(diào)用了encodeURIComponent 函數(shù),它是用來轉(zhuǎn)換URL的。
配置Angular 控制器
控制器用來處理應(yīng)用程序的邏輯,因此大部分js函數(shù)應(yīng)該在控制器內(nèi)部。這就是Angular 幫助你組織(混亂的)js代碼的方式。 我剛有大聲說出來嗎?
好吧,JavaScript鼓勵(lì)雜亂無章,不是嗎?這是它不好的地方.提到JavaScript的全局變量的時(shí)候,你不用帶著恐懼尖聲叫喊著說嗎?如果不用,那么我們可能要撤銷你的開發(fā)者許可證.你有編寫軟件的許可,不是嗎?(譯者注:因?yàn)槟阌虚_發(fā)者許可證,所以你自然也是帶著恐懼尖聲叫喊著說JavaScript的"全局變量"的--這里有些夸張的說法,意在說明開發(fā)者對(duì)于JavaScript的"全局變量"的使用應(yīng)該有所顧忌.)
現(xiàn)在,在我們深入了解應(yīng)用程序具體做了些什么的時(shí)候,看看控制器提供的功能.看看在控制器中的每個(gè)函數(shù),以便了解FreedReadR可以做些什么.
檢查localStorage, 存在則加載
我想做的一件事是,希望你在瀏覽器的slocalStorage中保存你的源.我不希望因?yàn)樵O(shè)置數(shù)據(jù)庫而弄得一團(tuán)糟,但我希望你可以增加自定義的源,并且讓他們?cè)谀愕臑g覽器中一直可用.
localStorage的限制
當(dāng)然,localStorage的限制是,它存在于一個(gè)特定瀏覽器的給定域名.
這意味著,如果你從NewtonSaber.com/FreedReadR運(yùn)行應(yīng)用程序并保存一些自定義的源,當(dāng)你從瀏覽器打開最初打開的連接的時(shí)候,你將會(huì)再次只看到列表中的這些內(nèi)容.每個(gè)瀏覽器的localStorage都是私有的.所以,如果你某天使用IE增加了一些源,在接下來的一天將無法使用Chrome來查看已經(jīng)添加的源.
對(duì)我來說,這個(gè)版本的應(yīng)用程序已經(jīng)OK了.因?yàn)樗俏宜M?用非??焖俸土私馄湎拗频募夹g(shù)開始.修正限制可能是以后另一篇文章的事情了.
你會(huì)發(fā)現(xiàn)在控制器代碼中,我首先調(diào)用了一個(gè)函數(shù):retrieveFromLocalStorage()。
函數(shù)如下:
function retrieveFromLocalStorage() { $scope.allFeeds = []; console.log("retrieving localStorage..."); try { $scope.allFeeds = JSON.parse(localStorage["feeds"]); console.log($scope.allFeeds.length); // console.log(JSON.stringify($scope.allFeeds)); if ($scope.allFeeds === null) { console.log("couldn't retrieve feeds" ); loadDefaultFeeds(); } } catch (ex) { console.log("ex: " + ex); loadDefaultFeeds(); saveToLocalStorage($scope.allFeeds); } }
這是一個(gè)非常簡(jiǎn)單的函數(shù)。 它在 $scope的作用域內(nèi)定義了一個(gè)名為 allFeeds的數(shù)組變量。 然后,通過 JSON.parse方法從localStorage數(shù)組中讀取一個(gè) feeds對(duì)象,這個(gè)對(duì)象保存的是已存在的 RSS源。如果這個(gè) feeds對(duì)象為 undefined(這是首次運(yùn)行應(yīng)用程序),程序會(huì)拋出一個(gè)異常。 當(dāng)異常被拋出時(shí),應(yīng)用程序會(huì)加載一些默認(rèn)的 RSS源(loadDefaultFeeds()),然后將這些源保存到localStorage中供下次使用。
先看看loadDefaultFeeds()函數(shù),然后我們?cè)倏磗aveToLocalStorage()函數(shù).
function loadDefaultFeeds() { $scope.allFeeds = [{titleText:"Load (from textbox)",url:""}, {titleText:"CodeProject C#",url:"http://www.codeproject.com/webservices/articlerss.aspx?cat=3"}, {titleText:"ComputerWorld - News",url:"http://www.computerworld.com/index.rss"}, {titleText:"Dr. Dobb's",url:"http://www.drdobbs.com/rss/all"}, {titleText:"InfoWorld Today's News",url:"http://www.infoworld.com/news/feed"}, {titleText:"Inc. Magazine",url:"http://www.inc.com/rss/homepage.xml"}, {titleText:"TechCrunch",url:"http://feeds.feedburner.com/TechCrunch"}, {titleText:"CNN",url:"http://rss.cnn.com/rss/cnn_topstories.rss"} ]; }
就像你所看到的,我增加了一些我最喜歡的源,這樣,你就可以很容易的試用這個(gè)應(yīng)用程序.它僅僅只是一個(gè)對(duì)象數(shù)組,通過titleText和url來定義.
只要它們都加載到$scope類型的變量allFeeds之中,你就可以使用ng-repeat從HTML獲取它們了,它看起來像下面這樣:
<li ng-repeat="feed in allFeeds"> <a href="#" ng-click="loadFeed($event,feed.url);">{{feed.titleText}}</a> </li>
這創(chuàng)建了選項(xiàng)的列表,當(dāng)你點(diǎn)擊下拉框按鈕的時(shí)候,它們將會(huì)顯示在上面.如你所看到的,我們?cè)趎g-repeat語句中引用了allFeeds $scope變量,然后我們引用了feed.titleText來生成入口.
現(xiàn)在你的按鈕已經(jīng)加載了很好的標(biāo)題了。
我說過會(huì)給你介紹saveToLocalStorage()方法,現(xiàn)在讓我們看一看。
function saveToLocalStorage(feeds) { // Put the object into storage localStorage.setItem('feeds', angular.toJson(feeds)); console.log(angular.toJson(feeds)); console.log("wrote feeds to localStorage"); }
這是一個(gè)非常簡(jiǎn)單的方法。方法允許你傳入 feeds 對(duì)象(這應(yīng)該是一個(gè)feed對(duì)象數(shù)組)。然后我們簡(jiǎn)單地調(diào)用 localStorage.setItem( ) 方法。正如方法的名字所說,我們可以用該方法來保存對(duì)象。要注意的是,當(dāng)我們保存對(duì)象的時(shí)候,我們會(huì)調(diào)用對(duì)象的 angular.toJson() 方法。這是個(gè)方法會(huì)幫助我們?nèi)コ恍゛ngular特有的屬性,而這些屬性是我們不想保存的。所以調(diào)用這個(gè)方法非常重要,因?yàn)閍ngular會(huì)在對(duì)象中保存一些特有屬性,這些屬性會(huì)讓你感到迷惑。
現(xiàn)在,應(yīng)用程序已經(jīng)加載了一些默認(rèn)的RSS源,如果你想獲取某一個(gè)RSS源的數(shù)據(jù),點(diǎn)擊下拉框按鈕,選擇其中一個(gè)值,然后應(yīng)用程序會(huì)運(yùn)行下面的代碼來獲取相關(guān)的數(shù)據(jù)。 我們?cè)?scope作用域內(nèi)添加一個(gè)loadFeed函數(shù),函數(shù)如下。 下面的函數(shù)會(huì)被調(diào)用,因?yàn)槲覀冊(cè)趆tml中給按鈕綁定了事件ng-click="loadFeed($event,feed.url);"。
loadFeed=function(e,url){ $scope.currentButtonText = angular.element(e.target).text(); // 清空過濾文本框中的上次展示的信息, // 當(dāng)我們選擇一個(gè)新的RSS源時(shí),如果不清空會(huì)讓人疑惑 $scope.filterText = ""; console.log("loadFeed / click event fired"); if ($scope.currentButtonText == $scope.allFeeds[0].titleText) { //console.log($scope.feedSrc); url = $scope.feedSrc; } $scope.feedSrc = url; if (url === undefined || url === "") { $scope.phMessage = "Please enter a valid Feed URL & try again."; return; } console.log("button text: " + angular.element(e.target).text()); console.log("value of url: " ); console.log(url); FeedService.parseFeed(url).then(function(res){ $scope.loadButonText=angular.element(e.target).text(); $scope.feeds=res.data.responseData.feed.entries; }); }
通過點(diǎn)擊事件調(diào)用上面的函數(shù)時(shí),我們先判斷用戶選擇的信息與下拉框第一個(gè)選項(xiàng) "Load (from textbox)"是否相同。 這么做是為了判斷用戶是否想加載文本框中提供的 RSS源。 如果是這樣,我們調(diào)用FeedService.parseFeed 方法時(shí),直接傳入文本框中 URL值。如果不是這樣,我們從相關(guān)的源對(duì)象中獲取 URL。
結(jié)果列表
當(dāng)源信息返回的時(shí)候,HTML代碼使用另一個(gè)ng-repeat來迭代遍歷每個(gè)項(xiàng),并用友好的格式顯示它們.HTML代碼看起來像下面這樣:
<ul class="unstyled"> <span class="badge badge-warning top-buffer" ng-show="feeds.length > 0">{{(feeds | filter:filterText).length}} Items</span> <li ng-repeat="feed in feeds | filter:filterText"> <h5><a href="{{feed.link}}" target="_blank"><span class="titleText" >{{feed.title}}</span></a><span class="small"> {{feed.publishedDate}}</span></h5> <p class="text-left">{{feed.contentSnippet}}</p> </li> </ul>
HTML也創(chuàng)建了一個(gè)好友的標(biāo)記,用來顯示獲取到的文本鏈接數(shù).
搜索結(jié)果里的內(nèi)容
它也顯示了一個(gè)搜索文本框,可以用來輸入文字,根據(jù)文本鏈接的內(nèi)容來過濾下拉列表.
點(diǎn)擊鏈接: 在新的頁面加載
最后,如果你點(diǎn)擊一個(gè)鏈接,它會(huì)在瀏覽器的新頁面或窗口加載.這讓這個(gè)工具很容易使用.
保存新的源
這個(gè)部分我做的很簡(jiǎn)單,因?yàn)槲彝瓿蛇@個(gè)應(yīng)用程序只是為了我自己使用.
如果你在文本框中輸入一個(gè)URL,然后點(diǎn)擊保存按鈕(向下的箭頭圖標(biāo)),你將看到一個(gè)JavaScript提示框,它看起來像下面展示的圖片這樣:
你可以簡(jiǎn)單的輸入標(biāo)題(它將顯示在下拉列表中)用來標(biāo)識(shí)新的源,然后點(diǎn)擊OK按鈕.
只要你這么做了,這個(gè)項(xiàng)就會(huì)增加到下拉列表.這個(gè)過程通過增加這個(gè)項(xiàng)到allFeeds對(duì)象來實(shí)現(xiàn),同時(shí),這個(gè)項(xiàng)也將立即保存到localStorage之中.
這就是所有的內(nèi)容.
希望你和我一樣喜歡這個(gè)工具,它節(jié)省了很多時(shí)間.
發(fā)布提示
請(qǐng)注意許多外部庫的鏈接是CDN的,除了下載的Bootstrap文件,其他下載的文件在一個(gè)3rdPartyLibs(第三方庫)的目錄里。你可以下載代碼,解壓和運(yùn)行它們。
為本地存儲(chǔ)準(zhǔn)備Web服務(wù)器是必需的
你必須運(yùn)行一個(gè)web服務(wù)器使它正常工作。注意,在下載的文件中已經(jīng)包含一個(gè)精致小巧的web服務(wù)器(mongoose web server)和配置文件,因此,如果你希望使用它,你可以雙擊mongoose.exe,它就開始運(yùn)行了,之后,你可以簡(jiǎn)單地加載: http://localhost:999/FreedReadR/ 它就從你的電腦上運(yùn)行了。
最后一個(gè)要注意的是:那個(gè)【X】按鈕是什么?
保存按鈕后面的那個(gè)[X]按鈕是什么?那個(gè)按鈕允許你釋放所有來自你的localStorage的反饋。如果你點(diǎn)擊它,它們會(huì)被移除。被命名為反饋的localstorage條目就這樣簡(jiǎn)單地被銷毀了,所以要小心處理。如果你在添加任何反饋之前使用這個(gè)按鈕,沒有任何問題,否則你將會(huì)失去你已經(jīng)添加的反饋。
我很懶,我使用它僅僅是為了我的測(cè)試,你還是應(yīng)該把它去掉的。懶惰還真是一個(gè)偉大開發(fā)者的標(biāo)志啊。
- Angularjs根據(jù)json文件動(dòng)態(tài)生成路由狀態(tài)的實(shí)現(xiàn)方法
- 使用AngularJS 跨站請(qǐng)求如何解決jsonp請(qǐng)求問題
- AngularJS中的JSONP實(shí)例解析
- AngularJS入門教程之 XMLHttpRequest實(shí)例講解
- AngularJS入門教程之Cookies讀寫操作示例
- angularJS 如何讀寫緩沖的方法(推薦)
- 三種AngularJS中獲取數(shù)據(jù)源的方式
- 基于AngularJS實(shí)現(xiàn)頁面滾動(dòng)到底自動(dòng)加載數(shù)據(jù)的功能
- Angularjs實(shí)現(xiàn)多個(gè)頁面共享數(shù)據(jù)的方式
- AngularJS實(shí)現(xiàn)數(shù)據(jù)列表的增加、刪除和上移下移等功能實(shí)例
- angularjs學(xué)習(xí)筆記之雙向數(shù)據(jù)綁定
- AngularJS讀取JSON及XML文件的方法示例
相關(guān)文章
AngularJS使用ng-inlude指令加載頁面失敗的原因與解決方法
這篇文章主要介紹了AngularJS使用ng-inlude指令加載頁面失敗的原因與解決方法,對(duì)比不同瀏覽器錯(cuò)誤提示分析了加載失敗的原因及通過http訪問的解決方法,需要的朋友可以參考下2017-01-01angularjs點(diǎn)擊圖片放大實(shí)現(xiàn)上傳圖片預(yù)覽
這篇文章主要為大家詳細(xì)介紹了angularjs點(diǎn)擊圖片放大實(shí)現(xiàn)上傳圖片預(yù)覽的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02Angular2使用Guard和Resolve進(jìn)行驗(yàn)證和權(quán)限控制
本篇文章主要介紹了Angular2使用Guard和Resolve進(jìn)行驗(yàn)證和權(quán)限控制,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-04-04Angular.js中$resource高大上的數(shù)據(jù)交互詳解
這篇文章主要給大家介紹了關(guān)于Angular.js中$resource高大上的數(shù)據(jù)交互的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用angular.js具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編來一起看看吧。2017-07-07后端接收不到AngularJs中$http.post發(fā)送的數(shù)據(jù)原因分析及解決辦法
這篇文章主要介紹了后端接收不到AngularJs中$http.post發(fā)送的數(shù)據(jù)原因分析及解決辦法的相關(guān)資料,需要的朋友可以參考下2016-07-07