PhoneGap bouw een feedlezer - Application Logic

Dit is het tweede deel van de serie over Audero Feed Reader. In dit artikel zullen we ingaan op de bedrijfslogica van onze applicatie en aanvullende achtergrondinformatie geven over de plug-ins en de API die voor ons project worden gebruikt.


1. Plugin & API Overzicht

De meldingsplugin

Op verschillende punten binnen de Audero Feed Reader app gebruiken we de alert () methode van de meldingsplugin. Hoe de waarschuwing wordt getoond, hangt echt af van het platform waarop de app zal worden uitgevoerd. In feite gebruiken de meeste ondersteunde besturingssystemen een native dialoogvenster, maar anderen, zoals Bada 2.x, gebruiken de klassieke browser alert () functie, die minder aanpasbaar is. Deze methode accepteert maximaal vier parameters:

  1. bericht: Een tekenreeks die het bericht bevat dat moet worden weergegeven.
  2. alertCallback: Een terugroepactie om aan te roepen wanneer het waarschuwingsvenster wordt gesloten.
  3. titel: De titel van het dialoogvenster (de standaardwaarde is "alert").
  4. buttonname: De tekst van de knop in het dialoogvenster (de standaardwaarde is "OK")

Houd er rekening mee dat Windows Phone 7 en 8 geen ingebouwde browser-melding hebben. Dus, als je het wilt gebruiken alert ( 'e-mail');, je moet toewijzen window.alert = navigator.notification.alert.

De invoegtoepassing InAppBrowser

In het eerste deel van deze serie heb ik gezegd dat een interessant punt op de aftitelingpagina het attribuut is target = "_ blank" toegepast op de links. In dit gedeelte wordt uitgelegd hoe het openLinksInApp () methode van de Toepassing klasse werkt.

De InAppBrowser is een webbrowser die in uw app wordt weergegeven wanneer u de window.open telefoontje. Zoals ik in het eerste deel al zei, vanaf versie 2.3.0, heeft het twee nieuwe methoden: executeScript () en insertCSS (). Momenteel biedt deze plug-in de volgende vijf methoden in totaal:

  • addEventListener (): Hiermee kan de gebruiker luisteren naar drie gebeurtenissen (loadstart, loadstop, en Uitgang) en om een ​​functie toe te voegen die wordt uitgevoerd zodra deze gebeurtenissen worden geactiveerd.
  • removeEventListener (): Gebruikt om een ​​eerder gekoppelde luisteraar te verwijderen.
  • dichtbij(): Wordt gebruikt om het venster InAppBrowser te sluiten.
  • executeScript (): Maakt injectie van JavaScript-code mogelijk in de InAppBrowser venster.
  • executeScript (): Maakt injectie van CSS-code mogelijk in de InAppBrowser venster.

Als u Cordova enkele maanden niet hebt gebruikt of als u zich aan versie 2.0.0 houdt, weet u dat standaard externe links werden geopend in dezelfde Cordova WebView waarop de toepassing werd uitgevoerd. Daarom werd, nadat een externe pagina was bezocht, de laatst weergegeven pagina precies zo weergegeven als voordat de gebruiker de pagina verliet. Vanaf die versie is dit niet langer het standaardgedrag. In feite worden externe links nu geopend met behulp van de Cordova WebView als de URL in de witte lijst van uw app staat. URL's die niet op uw witte lijst staan, worden geopend met de InAppBrowser-plug-in (meer hierover in de documentatie). Maar wat betekent dit praktisch? Dit betekent dat als u de links niet correct beheert en als de gebruikers van uw app op een koppeling klikken en vervolgens terugkeren naar de toepassing, alle jQuery Mobile of andere dergelijke verbeteringen verloren gaan. Dit gebeurt omdat alle CSS- en JavaScript-bestanden alleen op de hoofdpagina worden geladen en de daaropvolgende URL's worden geladen met AJAX (het standaardsysteem dat door jQuery Mobile wordt gebruikt).

De oplossing voor dit probleem is geïmplementeerd in de openLinksInApp () methode. In feite is de oplossing om de klikken op alle externe links te vangen door de target = "_ blank" attribuut, het ongewenste standaardgedrag voorkomen en de koppelingen openen met behulp van de window.open () methode. Om te kunnen werken, moet u voor deze oplossing een witte lijst instellen in het configuratiebestand.

De Google Feed API

Alvorens te praten over de klassen van de Audero Feed Reader, We moeten ons verdiepen in de magische wereld van de Google Feed API en de Google Feed JSON-interface, omdat we ze zullen gebruiken binnen de kernfunctie van onze applicatie. Zoals ik in het eerste deel van deze reeks heb aangegeven, parseert de interface een RSS- of ATOM-feed en retourneert een uniform en eenvoudig te analyseren JSON-object. Natuurlijk kunnen we dit JSON-object graag beheren met behulp van JavaScript.

Deze interface ondersteunt twee typen query's: Feed zoeken en Feed laden. De eerste zoekopdrachten naar feeds op basis van de opgegeven zoekwoorden zijn doorgegeven als een argument, terwijl de tweede feeds zoekt op basis van een opgegeven feed-URL. In onze applicatie gebruiken we alleen de functie Load Feed.

Elke aanvraag voor deze Google API moet ten minste twee parameters verzenden: v en q. Ja, ze hebben erg cryptische namen! De eerste parameter, v, specificeert het versienummer van het protocol. Op het moment van schrijven is de enige geldige waarde "1.0". In de tweede parameter, q, we geven de URL door om te parseren. Naast deze, zal onze applicatie ook de num parameter. De documentatie specificeert het aantal invoeren om te laden vanuit de feed gespecificeerd door q. Een waarde van -1 geeft het maximumaantal ondersteunde vermeldingen aan, momenteel 100. Standaard levert invoer laden vier resultaten op. Het is dus essentieel om onze functie van het laden van 10 vermeldingen standaard in te voeren en vervolgens met telkens 10 te verhogen telkens als de gebruiker meer moet weergeven.

Nu u weet hoe we de Google-service zullen ondervragen, is het belangrijk om het resultaat te verduidelijken dat wordt geretourneerd. Als de URL die we hebben opgegeven correct was, vinden we de vermeldingen van de feed in de responseData.feed.entries eigendom. Elke inzending heeft veel informatie, maar we gebruiken er slechts een paar. We zullen met name de volgende eigenschappen afdrukken:

  • titel: De titel van het item.
  • link: De URL voor de HTML-versie van het item.
  • schrijver: De auteur van het item.
  • contentSnippet: Een fragment van minder dan 120 tekens van het kenmerk content. Het fragment bevat geen HTML-tags.

De details die ik hierboven heb verstrekt, zijn voldoende voor het doel van onze applicatie, maar als u meer wilt weten, bekijk dan de documentatie van Google Feed.


2. De feedklasse bouwen

Dit gedeelte beschrijft de Voeden klasse en zijn methoden, allemaal inbegrepen in de feed.js het dossier. Zoals ik in het vorige deel al heb aangegeven, besparen we slechts twee velden voor elke feed: de titel en de URL. Dus deze klasse accepteert deze twee gegevenspunten als parameters. Binnenin creëren we twee privé-eigenschappen: _db en _tafel naam. Houd er rekening mee dat JavaScript geen property- en methodezichtbaarheidsmodifiers heeft, dus we zijn feitelijk in het emuleren van privégegevens.

De eerste is een snelkoppeling voor de lokale opslag eigendom van de venster voorwerp. Het wordt gebruikt om toegang te krijgen tot de methoden die worden weergegeven door de Opslag-plug-in, waarop onze app is gebaseerd, en die we zullen gebruiken om de feeds op te slaan. De tweede is een string met de naam van de sleutel waar we de gegevens zullen opslaan. Door de opslagspecificaties te onthouden, worden de gegevens opgeslagen met behulp van een sleutelwaarde-indeling. Daarom, om onze reeks feeds op te slaan, moeten we JSON-ify it. Dat is precies wat onze opslaan() methode zal doen. Op dezelfde manier moeten we voor het ophalen van de gegevens een JSON-tekenreeks analyseren om er een object van te maken. Deze taak wordt bereikt door de laden() methode. Die methoden zijn de enige twee die in de klassendefinitie moeten voorkomen omdat ze privé-eigenschappen gebruiken.

Het relatieve gedeelte van de feed.js bestand staat hieronder:

 functie Feed (naam, url) var _db = window.localStorage; var _tableName = 'feed'; this.name = naam; this.url = url; this.save = function (feeds) _db.setItem (_tableName, JSON.stringify (feeds)); ; this.load = function () return JSON.parse (_db.getItem (_tableName)); ; 

Rond deze twee eenvoudige methoden maken we een aantal andere veelvoorkomende methoden. In het bijzonder bouwen we enkele instantiemethoden zoals toevoegen(), om een ​​nieuwe feed toe te voegen, wissen (), om een ​​feed te verwijderen, en vergelijk met(), om een ​​Feed-instantie te vergelijken met een andere feed. Naast deze zullen we ook enkele statische methoden ontwikkelen, zoals getFeeds () om alle feeds uit de opslag op te halen, getFeed () om er slechts één te halen, en vergelijken() om twee objecten te vergelijken.

De vergelijkingsmethoden zijn een kleine discussie waard om te begrijpen hoe we zullen ze vergelijken. Ik sla de beschrijving van vergelijk met() omdat het niets anders doet dan zijn statische tegenhanger noemen, vergelijken(), dat doet echt werk. Daarin zullen we eerst testen of een van de gegeven waarden null-achtig is. In het geval dat geen ervan nul is, zullen we hun naam lexicografisch vergelijken en, in het geval ze gelijk zijn, hun URL vergelijken. Zoals u later zult ontdekken, dwingen we de gebruiker echter nooit om twee feeds met dezelfde naam of URL te gebruiken.
De vergelijken() methode is belangrijk omdat het de manier bepaalt waarop we twee feeds vergelijken, en dit is cruciaal om vast te stellen hoe de feeds in de feeds worden gesorteerd. list-feeds.html pagina. In feite zullen we de native gebruiken soort() matrixmethode die een optionele parameter accepteert, een functie die de sorteervolgorde van de array definieert op basis van de retourwaarden.

De code die implementeert wat ik heb beschreven, is de volgende:

 Feed.prototype.compareTo = function (other) return Feed.compare (deze, andere); ; Feed.compare = function (feed, other) if (other == null) return 1;  if (feed == null) return -1;  var test = feed.name.localeCompare (other.name); terugkeer (test === 0)? feed.url.localeCompare (other.url): test; ;

Naast de tot nu toe bekende methoden, maken we twee zoekmethoden die we gebruiken om een ​​bepaalde feed te vinden en te verwijderen: zoek op naam() en searchByUrl (). De laatste methode die ik wil benadrukken is getIndex (), en het is degene die wordt gebruikt om de index van een bepaald bestand op te halen.

Nu we alle details van deze klasse hebben gevonden, kan ik de volledige broncode van het bestand weergeven:

 functie Feed (naam, url) var _db = window.localStorage; var _tableName = 'feed'; this.name = naam; this.url = url; this.save = function (feeds) _db.setItem (_tableName, JSON.stringify (feeds)); ; this.load = function () return JSON.parse (_db.getItem (_tableName)); ;  Feed.prototype.add = function () var index = Feed.getIndex (this); var feeds = Feed.getFeeds (); if (index === false) feeds.push (this);  else feeds [index] = dit;  this.save (feeds); ; Feed.prototype.delete = function () var index = Feed.getIndex (this); var feeds = Feed.getFeeds (); if (index! == false) feeds.splice (index, 1); this.save (feeds);  voer feeds in; ; Feed.prototype.compareTo = function (other) return Feed.compare (deze, andere); ; Feed.compare = function (feed, other) if (other == null) return 1;  if (feed == null) return -1;  var test = feed.name.localeCompare (other.name); terugkeer (test === 0)? feed.url.localeCompare (other.url): test; ; Feed.getFeeds = function () var feeds = nieuwe feed (). Load (); return (feeds === null)? []: feeds; ; Feed.getFeed = function (feed) var index = Feed.getIndex (feed); if (index === false) return null;  var feed = Feed.getFeeds () [index]; retourneer nieuwe feed (feed.name, feed.url); ; Feed.getIndex = functie (feed) var feeds = Feed.getFeeds (); for (var i = 0; i < feeds.length; i++)  if (feed.compareTo(feeds[i]) === 0)  return i;   return false; ; Feed.deleteFeeds = function ()  new Feed().save([]); ; Feed.searchByName = function (name)  var feeds = Feed.getFeeds(); for (var i = 0; i < feeds.length; i++)  if (feeds[i].name === name)  return new Feed(feeds[i].name, feeds[i].url);   return false; ; Feed.searchByUrl = function (url)  var feeds = Feed.getFeeds(); for (var i = 0; i < feeds.length; i++)  if (feeds[i].url === url)  return new Feed(feeds[i].name, feeds[i].url);   return false; ;

3. De toepassingsklasse bouwen

Dit gedeelte bespreekt de tweede en laatste klasse van het project, Toepassing, vervat in de application.js het dossier. Het doel is om de lay-out van de pagina's te initialiseren, gebeurtenissen aan de elementen van de app-pagina bij te voegen en de Voeden klasse om feeds op te slaan, te laden en op te halen.

Deze klasse is georganiseerd om het beginpunt te hebben in de initApplication () methode. Het wordt gebeld zodra Cordova is geïnitialiseerd en de API's gereed zijn voor actie. Binnen deze methode voegen we een specifieke handler toe aan elke pagina-initialisatie, zodat we de gebeurtenissen kunnen beheren die worden geactiveerd door hun widgets. Daarin bellen we ook Application.openLinksInApp () om redenen die eerder zijn besproken. Bovendien, om de gebruikerservaring te verbeteren, zullen we elke druk op de fysieke terugknop opvangen (waar deze bestaat) om de gebruiker door te sturen naar de startpagina van onze app.

De kernfunctie van onze applicatie is initShowFeedPage () omdat het de Google Feed JSON-interface gebruikt. Voordat we het verzoek naar de service uitvoeren, tellen we het aantal reeds geladen items (currentEntries variabele) en bereken hoeveel items de service moet ophalen (entriesToShow variabel). Vervolgens voeren we het AJAX-verzoek uit met behulp van jQuery Ajax() methode, en tegelijkertijd tonen we de widget voor het laden van pagina's voor de gebruiker. Wanneer de callback voor succes wordt uitgevoerd, testen we eerst of het aantal geretourneerde items gelijk is aan het nummer dat al is weergegeven. In dat geval wordt het bericht "No more entries to load" weergegeven. Anders voegen we ze toe aan de lijst en verversen we de accordeon-widget ($ List.collapsibleset ( 'refresh')). Samen met elk item voegen we ook een handler toe aan de knop die is gemaakt, dus als de verbinding is uitgeschakeld, wordt een bericht gevraagd in plaats van de pagina te openen.

eindelijk, de updateIcons () methode zal worden besproken in het volgende en laatste deel van de serie.

De code die de besproken klasse implementeert, wordt hieronder vermeld:

 var Toepassing = initApplication: function () $ (document) .on ('pageinit', '# add-feed-page', function () Application.initAddFeedPage ();) .on ('pageinit', ' # list-feeds-page ', function () Application.initListFeedPage ();) .on (' pageinit ',' # show-feed-page ', function () var url = this.getAttribute (' data- url '). replace (/(.*?) url = / g, "); Application.initShowFeedPage (url);) .on (' pageinit ',' # aurelio-page ', function () Application.initAurelioPage ();) .on ('backbutton', function () $ .mobile.changePage ('index.html');); Application.openLinksInApp ();, initAddFeedPage: function () $ ('# add-feed-formulier '). submit (function (event) event.preventDefault (); var feedName = $ (' # feed-naam '). val (). trim (); var feedUrl = $ (' # feed -url '). val (). trim (); if (feedName === ") navigator.notification.alert (' Naamveld is verplicht en kan niet leeg zijn ', function () ,' Error '); return false; if (feedUrl === ") navigator.notification.alert ('URL-veld is verplicht en kan niet leeg zijn', function ()  , 'Fout'); return false;  if (Feed.searchByName (feedName) === false && Feed.searchByUrl (feedUrl) === false) var feed = nieuwe feed (feedName, feedUrl); feed.add (); navigator.notification.alert ('Feed correct opgeslagen', functie () $ .mobile.changePage ('index.html');, 'Succes');  else navigator.notification.alert ('Feed niet opgeslagen! De opgegeven naam of URL is al in gebruik', function () , 'Error');  return false; ); , initListFeedPage: function () var $ feedsList = $ ('# feeds-list'); var items = Feed.getFeeds (); var htmlItems = "; $ feedsList.empty (); items = items.sort (Feed.compare); for (var i = 0; i < items.length; i++)  htmlItems += '
  • '+ items [i] .name +'
  • '; $ feedsList.append (htmlItems) .listview ('refresh'); , initShowFeedPage: function (url) var step = 10; var loadFeed = function () var currentEntries = $ ('# feed-entries'). find ('div [data-role = inklapbaar]'). length; var entriesToShow = currentEntries + step; $ .ajax (url: 'https://ajax.googleapis.com/ajax/services/feed/load?v=1.0&num=' + entriesToShow + '& q =' + encodeURI (url), dataType: 'json' , beforeSend: function () $ .mobile.loading ('show', text: 'Even geduld met het ophalen van gegevens ...', textVisible: true);, success: function (data) var $ list = $ ( '# feed-entries'); if (data.responseData === null) navigator.notification.alert ('Kan de feed niet ophalen. Ongeldige URL', functie () , 'Fout'); return; var items = data.responseData.feed.entries; var $ post; if (currentEntries === items.length) navigator.notification.alert ('Geen invoer meer om te laden', function () , 'Info') ; return; for (var i = currentEntries; i < items.length; i++) $post = $('
    '); $ post .append ($ ('

    '). tekst (items [i] .title)) .append ($ ('

    ') .html (' '+ items [i] .title +' ')) // Titel toevoegen .append ($ ('

    ') .html (items [i] .contentSnippet)) // Beschrijving toevoegen .append ($ ('

    '). tekst (' Auteur: '+ items [i] .author)) .append ($ (' ') .text (' Ga naar de Article ') .button (). klik (functie (event) if ( Application.checkRequirements () === false) event.preventDefault (); navigator.notification.alert ('De verbinding is uitgeschakeld, schakel deze in', function () , 'Error'); return false; $ (this) .removeClass ('ui-btn-active');)); $ List.append ($ post); $ list.collapsibleset ('refresh'); , error: function () navigator.notification.alert ('Kan de feed niet ophalen. Probeer het later opnieuw', function () , 'Error'); , voltooid: function () $ .mobile.loading ('hide'); ); ; $ ('# show-more-entries'). click (function () loadFeed (); $ (this) .removeClass ('ui-btn-active');); $ ('# delete-feed'). klik (function () Feed.searchByUrl (url) .delete (); navigator.notification.alert ('Feed deleted', function () $ .mobile.changePage ('list -feeds.html ');,' Succes ');); if (Application.checkRequirements () === true) loadFeed (); else navigator.notification.alert ('Om deze app te gebruiken moet je je internetverbinding inschakelen', function () , 'Warning'); , initAurelioPage: function () $ ('a [target = _blank]'). klik (functie () $ (this) .closest ('li'). removeClass ('ui-btn-active'); ); , checkRequirements: function () if (navigator.connection.type === Connection.NONE) return false; return true; , updateIcons: function () var $ buttons = $ ('een [data-icoon], knop [data-icoon]'); var isMobileWidth = ($ (window) .width () <= 480); isMobileWidth ? $buttons.attr('data-iconpos', 'notext') : $buttons.removeAttr('data-iconpos'); , openLinksInApp: function () $(document).on('click', 'a[target=_blank]', function (event) event.preventDefault(); window.open($(this).attr('href'), '_blank'); ); ;


    Conclusie

    In het derde en laatste deel van deze serie zullen we zien hoe we de installatieprogramma's kunnen bouwen en testen met behulp van de CLI en Adobe PhoneGap Build.