Paso 5: Agrega imágenes de la Web

En este paso, aprenderás lo siguiente:

  • Cómo cargar recursos desde fuera de tu app y agregarlos al DOM mediante XHR y ObjectURLs

Tiempo estimado para completar este paso: 20 minutos
Para obtener una vista previa de lo que completarás en este paso, ve hacia la parte inferior de esta página ↓.

Cómo la CSP afecta el uso de recursos externos

La plataforma de Apps de Chrome obliga a tu app a cumplir plenamente con las Políticas de Seguridad del Contenido (CSP). No puedes cargar directamente recursos DOM, como imágenes, fuentes y CSS, desde fuera del paquete de apps de Chrome.

Si quieres mostrar una imagen externa en tu app, debes solicitarla a través de XMLHttpRequest, transformarlo en un Blob y crear una ObjectURL. Este ObjectURL se puede agregar al DOM porque hace referencia a un elemento en la memoria en el contexto de la app.

Mostrar miniaturas de imágenes de tareas pendientes

Cambiemos nuestra app para que busque las URLs de imágenes en un elemento de tareas pendientes. Si la URL parece una imagen (por ejemplo, termina en .png, .jpg, .svg o .gif), aplica el proceso mencionado anteriormente para mostrar una miniatura de imagen junto a la URL.

Actualizar permisos

En una app de Chrome, puedes realizar llamadas XMLHttpRequest a cualquier URL, siempre y cuando especifiques su dominio en el manifiesto. Dado que no sabrás de antemano qué URL de imagen escribirá el usuario, pide permiso para realizar solicitudes a "<all_urls>".

En manifest.json, solicita el permiso "":

"permissions": ["storage", "alarms", "notifications",
                "webview", "<all_urls>"],

Crea y borra ObjectURLs

En controller.js, agrega un método _createObjectURL() para crear ObjectURLs a partir de un BLOB:

Controller.prototype._createObjectURL = function(blob) {
  var objURL = URL.createObjectURL(blob);
  this.objectURLs = this.objectURLs || [];
  this.objectURLs.push(objURL);
  return objURL;
};

Las ObjectURLs contienen memoria, por lo que debes revocarlas cuando ya no necesites la ObjectURL. Agrega este método _clearObjectURL() a controller.js para controlar eso:

Controller.prototype._clearObjectURL = function() {
  if (this.objectURLs) {
    this.objectURLs.forEach(function(objURL) {
      URL.revokeObjectURL(objURL);
    });
    this.objectURLs = null;
  }
};

Realizar una solicitud XHR

Agrega un método _requestRemoteImageAndAppend() para ejecutar una XMLHttpRequest en una URL de imagen determinada:

Controller.prototype._requestRemoteImageAndAppend = function(imageUrl, element) {
  var xhr = new XMLHttpRequest();
  xhr.open('GET', imageUrl);
  xhr.responseType = 'blob';
  xhr.onload = function() {
    var img = document.createElement('img');
    img.setAttribute('data-src', imageUrl);
    img.className = 'icon';
    var objURL = this._createObjectURL(xhr.response);
    img.setAttribute('src', objURL);
    element.appendChild(img);
  }.bind(this);
  xhr.send();
};

Cuando se carga la XHR, este método crea una ObjectURL a partir de la respuesta de la XHR y agrega un elemento <img> con este ObjectURL al DOM.

Analizar URLs de imágenes en elementos de tareas pendientes

Ahora, agrega un método _parseForImageURLs() que busque todos los vínculos que aún no se procesaron y que busque imágenes en ellos. Para cada URL que parezca una imagen, ejecuta _requestRemoteImageAndAppend():

Controller.prototype._parseForImageURLs = function () {
  // remove old blobs to avoid memory leak:
  this._clearObjectURL();
  var links = this.$todoList.querySelectorAll('a[data-src]:not(.thumbnail)');
  var re = /\.(png|jpg|jpeg|svg|gif)$/;
  for (var i = 0; i<links.length; i++) {
    var url = links[i].getAttribute('data-src');
    if (re.test(url)) {
      links[i].classList.add('thumbnail');
      this._requestRemoteImageAndAppend(url, links[i]);
    }
  }
};

Renderiza miniaturas en la lista de tareas pendientes

Ahora llama a _parseForImageURLs() desde showAll(), showActive() y showCompleted():

/**
 * An event to fire on load. Will get all items and display them in the
 * todo-list
 */
Controller.prototype.showAll = function () {
  this.model.read(function (data) {
    this.$todoList.innerHTML = this._parseForURLs(this.view.show(data));
    this._parseForImageURLs();
  }.bind(this));
};

/**
 * Renders all active tasks
 */
Controller.prototype.showActive = function () {
  this.model.read({ completed: 0 }, function (data) {
    this.$todoList.innerHTML = this._parseForURLs(this.view.show(data));
    this._parseForImageURLs();
  }.bind(this));
};

/**
 * Renders all completed tasks
 */
Controller.prototype.showCompleted = function () {
  this.model.read({ completed: 1 }, function (data) {
    this.$todoList.innerHTML = this._parseForURLs(this.view.show(data));
    this._parseForImageURLs();
  }.bind(this));
};

Realiza lo mismo en editItem():

Controller.prototype.editItem = function (id, label) {
  ...
  var onSaveHandler = function () {
    ...
    if (value.length && !discarding) {
      ...
      label.innerHTML = this._parseForURLs(value);
      this._parseForImageURLs();
    } else if (value.length === 0) {
  ...
}

Restringe las dimensiones de la imagen que se muestra

Por último, en _bowercomponents/todomvc-common/base.css, agrega una regla de CSS para limitar el tamaño de la imagen:

.thumbnail img[data-src] {
  max-width: 100px;
  max-height: 28px;
}

Inicia tu app de tareas pendientes terminada

Has terminado el paso 5. Vuelve a cargar tu app y agrega una tarea pendiente con una URL a una imagen alojada en línea. Estas son algunas URL que puedes usar: http://goo.gl/nqHMF#.jpg o http://goo.gl/HPBGR#.png.

Más información

Para obtener información más detallada sobre algunas de las APIs presentadas en este paso, consulta lo siguiente:

¿Todo listo para continuar con el siguiente paso? Ir al Paso 6: Exporta todos al sistema de archivos »