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 por medio de 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 a la parte inferior de esta página ↓.

Cómo afecta la CSP al uso de recursos externos

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

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

Mostrar imágenes en miniatura de las tareas pendientes

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

Actualizar permisos

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

En manifest.json, solicita el elemento "" permiso:

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

Crea y borra ObjectURLs

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

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

Las ObjectURLs conservan la memoria, por lo que, cuando ya no necesites la ObjectURL, debes revocarlas. Agregar _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;
  }
};

Hacer 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();
};

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

Cómo analizar URLs de imágenes en elementos de tareas pendientes

Ahora, agrega un método _parseForImageURLs() que encuentre todos los vínculos que aún no se procesaron y los verifique imágenes de contenedores. Para cada URL que se vea como 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]);
    }
  }
};

Cómo renderizar 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) {
  ...
}

Restringir las dimensiones de la imagen que se muestran

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 completado el paso 5. Vuelve a cargar tu app y agrega un elemento de tareas pendientes con una URL a una imagen alojada en línea. Algunos 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:

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