Etapa 5: adicionar imagens da Web

Nesta etapa, você vai aprender:

  • Como carregar recursos de fora do app e adicioná-los ao DOM usando XHR e ObjectURLs.

Tempo estimado para concluir esta etapa: 20 minutos.
Para visualizar o que você concluirá nesta etapa, vá para a parte inferior desta página ↓.

Como o CSP afeta o uso de recursos externos

A plataforma de Aplicativos do Google Chrome força seu aplicativo a estar em total conformidade com as 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 app do Chrome .

Se você quiser mostrar uma imagem externa no app, é necessário solicitá-la com XMLHttpRequest, transformá-lo em um Blob e criar 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 nosso app para procurar URLs de imagem em um item de tarefas. Se o URL se parecer com uma imagem (por exemplo, termine com .png, .jpg, .svg ou .gif), aplique o processo mencionado acima para exibir uma miniatura de imagem ao lado do URL.

Atualizar permissões

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

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

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

Criar e limpar ObjectURLs

No controller.js (link em inglês), adicione um método _createObjectURL() para criar ObjectURLs de um blob:

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

Os ObjectURLs mantêm memória, portanto, quando você não precisar mais do ObjectURL, revogue-os. Adicionar Método _clearObjectURL() para controller.js a fim de 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 XHR, esse método cria um ObjectURL a partir da resposta do XHR e adiciona um elemento <img> com este ObjectURL ao DOM.

Analisar URLs de imagens em itens de tarefas

Agora, adicione um método _parseForImageURLs() que encontre todos os links ainda não processados e verifique se eles foram encontrados de imagens de contêiner. Execute _requestRemoteImageAndAppend() para cada URL semelhante a uma imagem:

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 do 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 tarefas com um URL para uma imagem hospedada on-line. Algumas URLs que podem ser usados: 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 para a próxima etapa? Vá para a Etapa 6: exportar tarefas para o sistema de arquivos »