Etapa 5: adicionar imagens da Web

Nesta etapa, você vai aprender:

  • Como carregar recursos de fora do app e adicioná-los ao DOM por meio de XHR e URLs de objeto.

Tempo estimado para concluir esta etapa: 20 minutos.
Para conferir o que você vai concluir nesta etapa, vá até o final desta página ↓.

Como a CSP afeta o uso de recursos externos

A plataforma de apps do Chrome força o app a obedecer às Políticas de Segurança de Conteúdo (CSP). Não é possível carregar recursos DOM diretamente, como imagens, fontes e CSS, de fora do pacote de apps do Chrome.

Para mostrar uma imagem externa no app, solicite-a via XMLHttpRequest, transforme-a em um Blob e crie um ObjectURL. Esse ObjectURL pode ser adicionado ao DOM porque se refere a um item na memória no contexto do app.

Mostrar imagens em miniatura para itens de tarefas

Vamos mudar o app para procurar URLs de imagem em um item de lista de tarefas. Se o URL se parecer com uma imagem (por exemplo, termina em .png, .jpg, .svg ou .gif), aplique o processo mencionado acima para mostrar uma miniatura da imagem ao lado do URL.

Atualizar permissões

Em um app do Chrome, é possível fazer chamadas XMLHttpRequest para qualquer URL, desde que você especifique o domínio dele no manifesto. Como você não sabe de antemão qual URL de imagem o usuário digitará, peça permissão para fazer solicitações a "<all_urls>".

No manifest.json, solicite a permissão "":

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

Criar e limpar ObjectURLs

No controller.js, adicione um método _createObjectURL() para criar ObjectURLs com base em um blob:

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

ObjectURLs mantêm a memória, portanto, quando não precisar mais do ObjectURL, revogue-os. Adicione este método _clearObjectURL() ao controller.js para processar isso:

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

Fazer uma solicitação XHR

Adicione um método _requestRemoteImageAndAppend() para executar um XMLHttpRequest em um determinado URL de imagem:

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

No carregamento do XHR, esse método cria um ObjectURL a partir da resposta do XHR e adiciona um elemento <img> com esse ObjectURL ao DOM.

Analisar URLs de imagem em itens de tarefas

Agora, adicione um método _parseForImageURLs() que encontra todos os links ainda não processados e verifica se há imagens. Para cada URL semelhante a uma imagem, execute _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]);
    }
  }
};

Renderizar miniaturas na lista de tarefas

Agora, chame _parseForImageURLs() de showAll(), showActive() e 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));
};

Faça o mesmo para 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 as dimensões da imagem exibida

Por fim, em _bowercomponents/todomvc-common/base.css, adicione uma regra CSS para limitar o tamanho da imagem:

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

Iniciar o app Todo concluído

Você concluiu a Etapa 5! Atualize o app e adicione um item de tarefa com um URL para uma imagem hospedada on-line. Alguns URLs que você pode usar: http://goo.gl/nqHMF#.jpg ou http://goo.gl/HPBGR#.png.

Mais informações

Para informações mais detalhadas sobre algumas das APIs introduzidas nesta etapa, consulte:

Tudo pronto para passar à próxima etapa? Vá para a Etapa 6: exportar tarefas para o sistema de arquivos »