PhoneGap crea un lettore di feed - Logica dell'applicazione

Questa è la seconda parte della serie Audero Feed Reader. In questo articolo approfondiremo la logica di business della nostra applicazione e forniremo ulteriori informazioni sui plug-in e le API utilizzate per il nostro progetto.


1. Panoramica plugin e API

Il plugin di notifica

In diversi punti all'interno del Audero Feed Reader app useremo il mettere in guardia() metodo del plugin di notifica. Il modo in cui verrà mostrato l'avviso dipende in realtà dalla piattaforma su cui verrà eseguita l'app. Infatti, la maggior parte dei sistemi operativi supportati utilizza una finestra di dialogo nativa, ma altri, come Bada 2.x, usano i browser classici mettere in guardia() funzione, che è meno personalizzabile. Questo metodo accetta fino a quattro parametri:

  1. Messaggio: Una stringa contenente il messaggio da visualizzare.
  2. alertCallback: Una richiamata da richiamare quando viene chiusa la finestra di avviso.
  3. titolo: Il titolo della finestra di dialogo (il valore predefinito è "avviso").
  4. buttonname: Il testo del pulsante incluso nella finestra di dialogo (il valore predefinito è "OK")

Tieni presente che Windows Phone 7 e 8 non dispongono di un avviso browser incorporato. Quindi, se vuoi usarlo ( 'Messaggio') segnalazione;, devi assegnare window.alert = navigator.notification.alert.

Il plugin InAppBrowser

Nella prima parte di questa serie, ho detto che un punto interessante della pagina dei crediti è l'attributo target = "_ blank" applicato ai collegamenti. Questa sezione spiegherà come openLinksInApp () metodo del Applicazione lavori di classe.

InAppBrowser è un browser Web che viene visualizzato nella tua app quando si utilizza il window.open chiamata. Come ho detto nella prima parte, a partire dalla versione 2.3.0, ha due nuovi metodi: executeScript () e insertCSS (). Attualmente, questo plugin fornisce i seguenti cinque metodi in totale:

  • addEventListener (): Consente all'utente di ascoltare tre eventi (loadstart, loadstop, e Uscita), e per allegare una funzione che viene eseguita non appena tali eventi vengono attivati.
  • removeEventListener (): Utilizzato per rimuovere un listener allegato in precedenza.
  • vicino(): Utilizzato per chiudere la finestra di InAppBrowser.
  • executeScript (): Abilita l'inserimento del codice JavaScript nel file InAppBrowser finestra.
  • executeScript (): Abilita l'inserimento del codice CSS nel file InAppBrowser finestra.

Se non hai usato Cordova per diversi mesi, o se ti attieni alla versione 2.0.0, ti ricorderai che per impostazione predefinita ha aperto i link esterni nella stessa Cordova WebView su cui era in esecuzione l'applicazione. Pertanto, una volta visitata una pagina esterna, l'ultima pagina visualizzata è stata visualizzata esattamente com'era prima che l'utente la lasciasse. Da quella versione in poi, questo non è più il comportamento standard. In effetti, i collegamenti esterni vengono ora aperti utilizzando Cordova WebView se l'URL si trova nella lista bianca della tua app. Gli URL che non sono nella tua lista bianca vengono aperti usando il plug-in InBrowser (di più su questo nella documentazione). Ma cosa significa questo praticamente? Significa che se non gestisci correttamente i collegamenti e se gli utenti della tua app fanno clic su un link e poi tornano all'applicazione, tutti i jQuery Mobile o altri miglioramenti di questo tipo vengono persi. Ciò accade perché tutti i file CSS e JavaScript vengono caricati solo nella pagina principale e gli URL successivi vengono caricati utilizzando AJAX (il sistema predefinito adottato da jQuery Mobile).

La correzione per questo problema è implementata nel openLinksInApp () metodo. In realtà, la soluzione è catturare i clic su tutti i link esterni impostando il target = "_ blank" attributo, impedendo il comportamento predefinito indesiderato e aprendo i collegamenti utilizzando il window.open () metodo. Per funzionare, questa soluzione richiede l'impostazione di una lista bianca nel file di configurazione.

L'API di Google Feed

Prima di parlare delle classi del Audero Feed Reader, dobbiamo approfondire il magico mondo della API di Google Feed e dell'interfaccia JSON di Google Feed perché li utilizzeremo all'interno della funzionalità principale della nostra applicazione. Come ho sottolineato nella prima parte di questa serie, l'interfaccia analizza un feed RSS o ATOM e restituisce un oggetto JSON unificato e facile da analizzare. Naturalmente, possiamo gestire felicemente questo oggetto JSON usando JavaScript.

Questa interfaccia supporta due tipi di query: Trova feed e Carica feed. Le prime ricerche di feed in base alle parole chiave fornite passate come argomento, mentre la seconda ricerca i feed in base a un URL di feed fornito. Nella nostra applicazione, utilizzeremo solo la funzione di caricamento del carico.

Ogni richiesta di questa API di Google deve inviare almeno due parametri: v e q. Sì, hanno nomi molto criptici! Il primo parametro, v, specifica il numero di versione del protocollo. Al momento della stesura di questo documento, l'unico valore valido è "1.0". Nel secondo parametro, q, passiamo l'URL per analizzare. Oltre a questi, la nostra applicazione utilizzerà anche il num parametro. La documentazione specifica il numero di voci da caricare dal feed specificato da q. Un valore di -1 indica il numero massimo di voci supportate, attualmente 100. Per impostazione predefinita, il caricamento del feed restituisce quattro risultati. Pertanto, è essenziale implementare la nostra funzionalità di caricamento di 10 voci per impostazione predefinita e quindi incrementare di altri 10 ogni volta che all'utente viene richiesto di mostrare altro.

Ora che sei a conoscenza di come richiederemo il servizio Google, è importante chiarire il risultato che verrà restituito. Se l'URL che abbiamo fornito era corretto, troveremo le voci del Feed all'interno di responseData.feed.entries proprietà. Ogni voce ha molte informazioni, ma ne useremo solo alcune. In particolare, stamperemo le seguenti proprietà:

  • titolo: Il titolo della voce.
  • collegamento: L'URL per la versione HTML della voce.
  • autore: L'autore della voce.
  • contentSnippet: Uno snippet con meno di 120 caratteri dell'attributo content. Lo snippet non contiene tag HTML.

I dettagli che ho fornito sopra sono sufficienti per lo scopo della nostra applicazione, ma se vuoi saperne di più, dai un'occhiata alla documentazione di Google Feed.


2. Costruire la classe di alimentazione

Questa sezione descriverà il Alimentazione classe e i suoi metodi, tutti inclusi nel feed.js file. Come ho sottolineato nella parte precedente, salveremo solo due campi per ogni feed: il titolo e l'URL. Quindi, questa classe accetta questi due punti dati come parametri. Al suo interno, creiamo due proprietà private: _db e _tableName. Ricorda che JavaScript non ha effettivamente modificatori di visibilità di proprietà e metodi, pertanto stiamo emulando dati privati.

Il primo è una scorciatoia per il memoria locale proprietà del finestra oggetto. È utilizzato per accedere ai metodi esposti dal plug-in di archiviazione, su cui è basata la nostra app, e che useremo per memorizzare i feed. Il secondo è una stringa contenente il nome della chiave in cui salveremo i dati. Infatti, richiamando le specifiche di archiviazione, memorizza i dati utilizzando un formato chiave-valore. Pertanto, per memorizzare la nostra gamma di feed, abbiamo bisogno di JSON-ify. Questo è esattamente ciò che è nostro salvare() metodo farà. Allo stesso modo, per recuperare i dati dobbiamo analizzare una stringa JSON per trasformarla in un oggetto. Questo compito è raggiunto dal caricare() metodo. Questi metodi sono gli unici due che devono essere all'interno della definizione di classe perché utilizzano proprietà private.

La sezione relativa del feed.js il file è elencato di seguito:

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

Intorno a questi due metodi semplici creeremo un mucchio di altri comuni. In particolare, costruiremo alcuni metodi di istanza come Inserisci(), per aggiungere un nuovo feed, Elimina(), per eliminare un feed, e confrontare con(), per confrontare un'istanza Feed con un altro Feed. Oltre a questi, svilupperemo anche alcuni metodi statici come getFeeds () per recuperare tutti i feed dallo storage, getFeed () per recuperarne solo uno, e confrontare() per confrontare due oggetti.

I metodi di confronto valgono una piccola discussione per capire Come li confronteremo. Salterò la descrizione di confrontare con() perché non fa altro che chiamare la sua controparte statica, confrontare(), questo fa davvero il lavoro. In esso, prima verificheremo se uno dei valori indicati è nullo. Nel caso in cui nessuno di essi sia nullo, confronteremo lessicograficamente il loro nome e, nel caso siano uguali, confronteremo il loro URL. Tuttavia, come scoprirai più tardi, imporremo all'utente di non avere mai due feed con lo stesso nome o URL.
Il confrontare() il metodo è importante perché definisce il modo in cui confrontiamo due feed, e questo è fondamentale per stabilire in che modo i Feed verranno ordinati nel list-feeds.html pagina. In effetti, useremo il nativo ordinare() metodo array che accetta un parametro opzionale, una funzione, che definisce l'ordinamento dell'array in base ai suoi valori di ritorno.

Il codice che implementa ciò che ho descritto è il seguente:

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

Oltre ai metodi visti finora, creeremo due metodi di ricerca che utilizzeremo per trovare ed eliminare un dato feed: ricerca per nome() e searchByUrl (). L'ultimo metodo che voglio sottolineare è getIndex (), ed è quello usato per recuperare l'indice di un determinato file.

Ora che abbiamo scoperto tutti i dettagli di questa classe, posso elencare l'intero codice sorgente del file:

 funzione Feed (nome, url) var _db = window.localStorage; var _tableName = 'feed'; this.name = nome; this.url = url; this.save = function (feeds) _db.setItem (_tableName, JSON.stringify (feed)); ; 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 [indice] = questo;  this.save (feed); ; Feed.prototype.delete = function () var index = Feed.getIndex (this); var feeds = Feed.getFeeds (); if (index! == false) feeds.splice (index, 1); this.save (feed);  feed di ritorno; ; Feed.prototype.compareTo = function (other) return Feed.compare (this, other); ; Feed.compare = function (feed, other) if (other == null) return 1;  if (feed == null) return -1;  var test = feed.name.localeCompare (other.name); return (test === 0)? feed.url.localeCompare (other.url): test; ; Feed.getFeeds = function () var feeds = new Feed (). Load (); return (feed === null)? []: feed; ; Feed.getFeed = function (feed) var index = Feed.getIndex (feed); if (index === false) return null;  var feed = Feed.getFeeds () [index]; restituire nuovo feed (feed.name, feed.url); ; Feed.getIndex = function (feed) var feeds = Feed.getFeeds (); per (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. Costruire la classe dell'applicazione

Questa sezione discute la seconda e ultima classe del progetto, Applicazione, contenuto all'interno del application.js file. Il suo scopo è di inizializzare il layout delle pagine, allegare eventi agli elementi della pagina dell'app e utilizzare il Alimentazione classe per salvare, caricare e recuperare i feed.

Questa classe è organizzata per avere il punto di ingresso nel file InitApplication () metodo. Si chiama non appena inizializzato Cordova e le sue API sono pronte per agire. All'interno di questo metodo, stiamo allegando un gestore specifico all'inizializzazione di ogni pagina in modo che possiamo gestire gli eventi innescati dai loro widget. In esso, chiameremo anche Application.openLinksInApp () per motivi discussi in precedenza. Inoltre, per migliorare l'esperienza dell'utente, cattureremo ogni pressione del pulsante fisico indietro (dove esiste) per reindirizzare l'utente alla pagina iniziale della nostra app.

La funzione principale della nostra applicazione è initShowFeedPage () perché utilizza l'interfaccia JSON di Google Feed. Prima di eseguire la richiesta al servizio, contiamo il numero di voci già caricate (currentEntries variabile) e calcolare il numero di voci che il servizio deve recuperare (entriesToShow variabile). Quindi eseguiremo la richiesta AJAX, usando jQuery ajax () metodo, e allo stesso tempo mostriamo il widget di caricamento della pagina per l'utente. Quando viene eseguita la richiamata di successo, prima verifichiamo se il numero di voci restituite è uguale al numero già mostrato, nel qual caso mostriamo il messaggio "Niente più voci da caricare". Altrimenti, li aggiungiamo alla lista e aggiorniamo il widget della fisarmonica ($ List.collapsibleset ( 'Aggiorna')). Insieme a ogni voce, allega anche un gestore al pulsante che viene creato, quindi se la connessione è disattivata viene richiesto un messaggio invece di accedere alla pagina.

Finalmente, il updateIcons () il metodo sarà discusso nella prossima e ultima parte della serie.

Il codice che implementa la classe discussa è elencato di seguito:

 var Application = 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-form '). submit (function (event) event.preventDefault (); var feedName = $ (' # nome-feed '). val (). trim (); var feedUrl = $ (' # feed -url '). val (). trim (); if (feedName === ") navigator.notification.alert (' Il campo Nome è obbligatorio e non può essere vuoto ', function () ,' Errore '); return false; if (feedUrl === ") navigator.notification.alert ('Il campo URL è obbligatorio e non può essere vuoto', function () , "Errore"); restituisce falso;  if (Feed.searchByName (feedName) === false && Feed.searchByUrl (feedUrl) === false) var feed = new Feed (feedName, feedUrl); feed.add (); navigator.notification.alert ('Feed salvato correttamente', function () $ .mobile.changePage ('index.html');, 'Success');  else navigator.notification.alert ('Feed non salvato! O il nome o l'Url specificato è già in uso', function () , 'Error');  return false; ); , initListFeedPage: function () var $ feedsList = $ ('# feed-list'); var items = Feed.getFeeds (); var htmlItems = "; $ feedsList.empty (); items = items.sort (Feed.compare); for (var i = 0; i < items.length; i++)  htmlItems += '
  • '+ elementi [i] .name +'
  • '; $ feedsList.append (htmlItems) .listview ('refresh'); , initShowFeedPage: function (url) var step = 10; var loadFeed = function () var currentEntries = $ ('# feed-entries'). find ('div [data-role = collapsible]'). 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: 'Attendere il recupero dei dati ...', textVisible: true);, success: function (data) var $ list = $ ( "# feed-entries"); if (data.responseData === null) navigator.notification.alert ('Impossibile recuperare il feed. URL non valido', function () , 'Error'); return; var items = data.responseData.feed.entries; var $ post; if (currentEntries === items.length) navigator.notification.alert ('Non ci sono più voci da caricare', function () , 'Info') ; return; for (var i = currentEntries; i < items.length; i++) $post = $('
    '); $ post .append ($ ('

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

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

    ') .html (elementi [i] .contentSnippet)) // Aggiungi descrizione .append ($ ('

    ') .text (' Autore: '+ items [i] .author)) .append ($ (' ') .text (' Vai all'articolo ') .button () .click (function (event) if ( Application.checkRequirements () === false) event.preventDefault (); navigator.notification.alert ('La connessione è spenta, accendila per favore', function () , 'Errore'); return false; $ (this) .removeClass ('ui-btn-active');)); $ List.append ($ post); $ list.collapsibleset ('refresh'); , error: function () navigator.notification.alert ('Impossibile recuperare il feed. Prova più tardi', function () , 'Error'); , complete: function () $ .mobile.loading ('nascondi'); ); ; $ ('# show-more-entries'). click (function () loadFeed (); $ (this) .removeClass ('ui-btn-active');); $ ('# delete-feed'). click (function () Feed.searchByUrl (url) .delete (); navigator.notification.alert ('Feed cancellato', function () $ .mobile.changePage ('lista -feeds.html ');,' Success ');); if (Application.checkRequirements () === true) loadFeed (); else navigator.notification.alert ('Per utilizzare questa app è necessario abilitare la connessione Internet', function () , 'Warning'); , initAurelioPage: function () $ ('a [target = _blank]'). click (function () $ (this) .closest ('li'). removeClass ('ui-btn-active'); ); , checkRequirements: function () if (navigator.connection.type === Connection.NONE) return false; return true; , updateIcons: function () var $ buttons = $ ('a [data-icon], button [data-icon]'); var isMobileWidth = ($ (finestra) .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'); ); ;


    Conclusione

    Nella terza e ultima puntata di questa serie, vedremo come creare e testare gli installer utilizzando la CLI e Adobe PhoneGap Build.