PhoneGap construir un lector de feeds - lógica de aplicaciones

Esta es la segunda parte de la serie sobre Audero Feed Reader. En este artículo, profundizaremos en la lógica de negocios de nuestra aplicación y proporcionaremos antecedentes adicionales sobre los complementos y API utilizados para nuestro proyecto..


1. Complemento y descripción general de la API

El complemento de notificación

En varios puntos dentro del Audero Feed Reader aplicación utilizaremos el alerta() Método del complemento de notificación. La forma en que se mostrará la alerta depende realmente de la plataforma en la que se ejecutará la aplicación. De hecho, la mayoría de los sistemas operativos compatibles utilizan un cuadro de diálogo nativo, pero otros, como Bada 2.x, usan el navegador clásico alerta() Función, que es menos personalizable. Este método acepta hasta cuatro parámetros:

  1. mensaje: Una cadena que contiene el mensaje que se mostrará.
  2. alertCallback: Una devolución de llamada para invocar cuando se descarta el cuadro de diálogo de alerta.
  3. título: El título del diálogo (el valor predeterminado es "alerta").
  4. buttonName: El texto del botón incluido en el cuadro de diálogo (el valor predeterminado es "OK")

Tenga en cuenta que Windows Phone 7 y 8 no tienen una alerta de navegador integrada. Así que, si quieres usar mensaje de alerta');, tienes que asignar window.alert = navigator.notification.alert.

El complemento InAppBrowser

En la primera parte de esta serie, mencioné que un punto interesante de la página de créditos es el atributo target = "_ en blanco" Aplicado a los enlaces. Esta sección explicará cómo el openLinksInApp () método de la Solicitud trabajos de clase.

InAppBrowser es un navegador web que se muestra en su aplicación cuando usa el ventana abierta llamada. Como dije en la primera parte, a partir de la versión 2.3.0, tiene dos nuevos métodos: executeScript () y insertCSS (). Actualmente, este complemento proporciona los siguientes cinco métodos en total:

  • addEventListener (): Permite al usuario escuchar tres eventos (arranque de carga, parada de carga, y salida), y para adjuntar una función que se ejecuta tan pronto como se activan esos eventos.
  • removeEventListener (): Se utiliza para eliminar un oyente adjunto previamente.
  • cerrar(): Se utiliza para cerrar la ventana InAppBrowser.
  • executeScript (): Permite la inyección de código JavaScript en el InAppBrowser ventana.
  • executeScript (): Permite la inyección de código CSS en el InAppBrowser ventana.

Si no usó Cordova durante varios meses, o si se adhiere a la versión 2.0.0, recordará que, de forma predeterminada, abrió enlaces externos en el mismo Cordova WebView que ejecutaba la aplicación. Por lo tanto, una vez que se visitó una página externa, la última página mostrada se mostró exactamente como estaba antes de que el usuario la abandonara. A partir de esa versión, este ya no es el comportamiento estándar. De hecho, los enlaces externos ahora se abren con Cordova WebView si la URL está en la lista blanca de su aplicación. Las URL que no están en su lista blanca se abren con el complemento InAppBrowser (más sobre esto en la documentación). ¿Pero qué significa esto prácticamente? Esto significa que si no administra los enlaces correctamente y si los usuarios de su aplicación hacen clic en un enlace y luego regresan a la aplicación, se perderán todas las mejoras de jQuery Mobile u otras similares. Esto sucede porque todos los archivos CSS y JavaScript se cargan solo en la página principal, y las URL posteriores se cargan utilizando AJAX (el sistema predeterminado adoptado por jQuery Mobile).

La solución para este problema se implementa en el openLinksInApp () método. De hecho, la solución es capturar los clics en todos los enlaces externos estableciendo el target = "_ en blanco" atributo, evitando el comportamiento predeterminado no deseado y abriendo los enlaces usando el window.open () método. Para que funcione, esta solución requerirá que establezca una lista blanca en el archivo de configuración.

La API de Google Feed

Antes de hablar de las clases del Audero Feed Reader, Necesitamos profundizar en el mundo mágico de la API de Google Feed y la interfaz JSON de Google Feed porque los usaremos dentro de la función principal de nuestra aplicación. Como señalé en la primera parte de esta serie, la interfaz analiza una fuente RSS o ATOM y devuelve un objeto JSON unificado y fácil de analizar. Por supuesto, podemos gestionar este objeto JSON con JavaScript..

Esta interfaz admite dos tipos de consulta: Buscar feed y Cargar feed. La primera busca las fuentes basadas en las palabras clave dadas pasadas como un argumento, mientras que la segunda busca las fuentes basadas en la URL de la fuente proporcionada. En nuestra aplicación, solo usaremos la función Load Feed.

Cada solicitud a esta API de Google debe enviar al menos dos parámetros: v y q. Sí, tienen nombres muy crípticos! El primer parametro, v, Especifica el número de versión del protocolo. En el momento de escribir esto, el único valor válido es "1.0". En el segundo parametro, q, Pasamos la URL para analizar. Además de estos, nuestra aplicación utilizará también el num parámetro. La documentación especifica el número de entradas para cargar desde el feed especificado por q. Un valor de -1 indica el número máximo de entradas admitidas, actualmente 100. De forma predeterminada, la alimentación de carga devuelve cuatro resultados. Por lo tanto, es esencial implementar nuestra función de cargar 10 entradas de forma predeterminada y luego aumentar en otras 10 cada vez que el usuario deba mostrar más.

Ahora que sabe cómo consultaremos el servicio de Google, es importante aclarar el resultado que devolverá. Si la URL que proporcionamos era correcta, encontraremos las entradas del Feed dentro de la responseData.feed.entries propiedad. Cada entrada tiene mucha información, pero usaremos solo algunas de ellas. En particular, imprimiremos las siguientes propiedades:

  • título: El título de la entrada.
  • enlazar: La URL de la versión HTML de la entrada..
  • autor: El autor de la entrada..
  • contentSnippet: Un fragmento de menos de 120 caracteres del atributo de contenido. El fragmento de código no contiene ninguna etiqueta HTML.

Los detalles que he proporcionado anteriormente son suficientes para el propósito de nuestra aplicación, pero si desea obtener más información, consulte la documentación de Google Feed..


2. Construyendo la clase de alimentación

Esta sección describirá la Alimentar clase y sus métodos, todo incluido en el feed.js expediente. Como señalé en la parte anterior, solo guardaremos dos campos para cada fuente: el título y la URL. Entonces, esta clase acepta estos dos puntos de datos como parámetros. Dentro de ella, creamos dos propiedades privadas: _db y _nombre de la tabla. Recuerde que JavaScript en realidad no tiene modificadores de visibilidad de propiedades y métodos, por lo que estamos emulando datos privados.

El primero es un atajo para el almacenamiento local propiedad de la ventana objeto. Se utiliza para acceder a los métodos expuestos por el complemento de almacenamiento, en los que se basa nuestra aplicación, y que utilizaremos para almacenar los feeds. El segundo es una cadena que contiene el nombre de la clave donde guardaremos los datos. De hecho, al recordar las especificaciones de almacenamiento, almacena los datos utilizando un formato de clave-valor. Por lo tanto, para almacenar nuestra variedad de fuentes, necesitamos JSON-ify it. Eso es exactamente lo que nuestro salvar() método va a hacer. De la misma manera, para recuperar los datos tenemos que analizar una cadena JSON para convertirla en un objeto. Esta tarea se logra por la carga() método. Esos métodos son los únicos dos que deben estar dentro de la definición de clase porque usan propiedades privadas.

La sección relativa de la feed.js archivo se lista a continuación:

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

En torno a estos dos métodos simples, crearemos un montón de otros métodos comunes. Específicamente, construiremos algunos métodos de instancia como añadir(), para agregar un nuevo feed, borrar(), para eliminar un feed, y comparar con(), para comparar una instancia de Feed con otro Feed. Además de estos, también desarrollaremos algunos métodos estáticos como getFeeds () para recuperar todos los feeds del almacenamiento, getFeed () para recuperar solo uno, y comparar() para comparar dos objetos.

Los métodos de comparación valen una pequeña discusión para entender cómo los compararemos Me saltaré la descripción de comparar con() Porque no hace nada más que llamar a su contraparte estática., comparar(), que realmente hace el trabajo En él, primero probaremos si alguno de los valores dados es nulo. En caso de que ninguno de ellos sea nulo, compararemos lexicográficamente su nombre y, en caso de que sean iguales, compararemos su URL. Sin embargo, como descubrirá más adelante, forzaremos al usuario a nunca tener dos fuentes con el mismo nombre o URL..
los comparar() El método es importante porque define la forma en que estamos comparando dos fuentes, y esto es crucial para establecer cómo se ordenarán las fuentes en el list-feeds.html página. De hecho, utilizaremos el nativo. ordenar() método de matriz que acepta un parámetro opcional, una función, que define el orden de clasificación de la matriz en función de sus valores de retorno.

El código que implementa lo que describí es el siguiente:

 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); retorno (prueba === 0)? feed.url.localeCompare (other.url): prueba; ;

Además de los métodos vistos hasta ahora, crearemos dos métodos de búsqueda que usaremos para encontrar y eliminar un Feed dado: buscar por nombre() y searchByUrl (). El último método que quiero destacar es getIndex (), y es el que se usa para recuperar el índice de un determinado archivo.

Ahora que hemos descubierto todos los detalles de esta clase, puedo enumerar el código fuente completo del archivo:

 función Feed (nombre, url) var _db = window.localStorage; var _tableName = 'feed'; this.name = nombre; 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 (esto);  else feeds [index] = esto;  this.save (feeds); ; Feed.prototype.delete = function () var index = Feed.getIndex (this); var feeds = Feed.getFeeds (); if (index! == false) feeds.splice (index, 1); esta.save (alimenta);  devolver feeds; ; 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); retorno (prueba === 0)? feed.url.localeCompare (other.url): prueba; ; Feed.getFeeds = function () var feeds = new Feed (). Load (); volver (feeds === null)? []: alimenta; ; Feed.getFeed = function (feed) var index = Feed.getIndex (feed); if (index === false) return null;  var feed = Feed.getFeeds () [index]; devolver nuevo Feed (feed.name, feed.url); ; Feed.getIndex = function (feed) var feeds = Feed.getFeeds (); para (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. Construyendo la clase de aplicación

Esta sección discute la segunda y última clase del proyecto., Solicitud, contenido dentro de aplicacion.js expediente. Su objetivo es inicializar el diseño de las páginas, adjuntar eventos a los elementos de la página de la aplicación y utilizar el Alimentar Clase para guardar, cargar y recuperar feeds..

Esta clase está organizada para tener el punto de entrada en el initApplication () método. Se llama tan pronto como Cordova se haya inicializado y sus API estén listas para actuar. Dentro de este método, adjuntamos un controlador específico a cada inicialización de página para que podamos administrar los eventos desencadenados por sus widgets. En ella, también llamaremos. Application.openLinksInApp () Por razones discutidas anteriormente. Además, para mejorar la experiencia del usuario, capturaremos cada vez que presione el botón de retroceso físico (donde exista) para redirigir al usuario a la página de inicio de nuestra aplicación..

La función principal de nuestra aplicación es initShowFeedPage () porque utiliza la interfaz de Google Feed JSON. Antes de ejecutar la solicitud al servicio, contamos el número de entradas ya cargadas (currentEntries variable) y calcule cuántas entradas debe obtener el servicio (entriesToShow variable). Luego ejecutaremos la solicitud AJAX, usando jQuery ajax () Método, y al mismo tiempo mostramos el widget de carga de página al usuario. Cuando se ejecuta la devolución de llamada correcta, primero probamos si el número de entradas devueltas es el mismo que el que ya se mostró, en cuyo caso mostramos el mensaje "No hay más entradas para cargar". De lo contrario, los agregaremos a la lista y actualizaremos el widget de acordeón ($ list.collapsibleset ('actualizar')). Junto con cada entrada, también adjuntamos un controlador al botón que se crea, de modo que si la conexión está desactivada, se le solicitará un mensaje en lugar de acceder a la página.

Finalmente, el updateIcons () El método será discutido en la siguiente y última parte de la serie..

El código que implementa la clase discutida se enumera a continuación:

 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 (evento) event.preventDefault (); var feedName = $ (' # feed-name '). val (). trim (); var feedUrl = $ (' # feed -url '). val (). trim (); if (feedName === ") navigator.notification.alert (' El campo Nombre es obligatorio y no puede estar vacío ', function () ,' Error '); devolver falso; if (feedUrl === ") navigator.notification.alert ('el campo URL es obligatorio y no puede estar vacío', function () , 'Error'); falso retorno;  if (Feed.searchByName (feedName) === false && Feed.searchByUrl (feedUrl) === false) var feed = new Feed (feedName, feedUrl); feed.add (); navigator.notification.alert ('Feed guardado correctamente', función () $ .mobile.changePage ('index.html');, 'Success');  else navigator.notification.alert ('Feed no guardado! El nombre o la URL especificada ya están en uso', function () , 'Error');  falso retorno; ); , initListFeedPage: function () var $ feedsList = $ ('# feeds-list'); var items = Feed.getFeeds (); var htmlItems = "; $ feedsList.empty (); items = items.sort (Feed.compare); para (var i = 0; i < items.length; i++)  htmlItems += '
  • '+ elementos [i] .name +'
  • '; $ feedsList.append (htmlItems) .listview ('actualizar'); , initShowFeedPage: function (url) var step = 10; var loadFeed = function () var currentEntries = $ ('# feed-entries'). find ('div [data-role = colapsable]'). 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: 'Espere mientras se recuperan datos ...', textVisible: true);, success: function (data) var $ list = $ ( '# feed-entries'); if (data.responseData === null) navigator.notification.alert ('No se puede recuperar el Feed. URL no válida', function () , 'Error'); return; var items = data.responseData.feed.entries; var $ post; if (currentEntries === items.length) navigator.notification.alert ('No hay más entradas para cargar', función () , 'Información') ; return; for (var i = currentEntries; i < items.length; i++) $post = $('
    '); $ post .append ($ ('

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

    ') .html (' '+ elementos [i] .title +' ')) // Agregar título .append ($ ('

    ') .html (items [i] .contentSnippet)) // Agregar descripción .append ($ ('

    ') .text (' Autor: '+ elementos [i] .author)) .append ($ (' ') .text (' Ir al Artículo ') .button () .click (function (event) if ( Application.checkRequirements () === false) event.preventDefault (); navigator.notification.alert ('La conexión está desactivada, actívela', function () , 'Error'); devuelva false; $ (this) .removeClass ('ui-btn-active');)); $ list.append ($ post); $ list.collapsibleset ('actualizar'); , error: function () navigator.notification.alert ('No se puede recuperar la fuente. Intente más tarde', function () , 'Error'); , complete: function () $ .mobile.loading ('hide'); ); ; $ ('# show-more-entries'). click (function () loadFeed (); $ (this) .removeClass ('ui-btn-active');); $ ('# delete-feed'). haga clic en (function () Feed.searchByUrl (url) .delete (); navigator.notification.alert ('Feed deleted', function () $ .mobile.changePage ('list -feeds.html ');,' Success ');); if (Application.checkRequirements () === true) loadFeed (); else navigator.notification.alert ('Para usar esta aplicación debes habilitar tu conexión a 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; devuelve true; , updateIcons: function () var $ buttons = $ ('a [icono de datos], botón [icono de datos]'); 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'); ); ;


    Conclusión

    En la tercera y última entrega de esta serie, veremos cómo construir y probar los instaladores utilizando CLI y Adobe PhoneGap Build.