Microsoft Office

Explorando a API de JavaScript para Office: vinculações de dados e partes XML personalizadas

Stephen Oliver
Eric Schmidt

Baixar o código de exemplo

Este artigo é a Parte 3 de uma série de instruções passo a passo detalhadas da API JavaScript para Office. Este artigo continua o exame dos aspectos principais da API, concentrando-se na vinculação de dados e suporte ao trabalho com partes XML personalizadas. A parte 1, “Explorando a nova API de JavaScript para Office” (msdn.microsoft.com/magazine/jj891051), fornece uma ampla visão geral do modelo de objeto. A Parte 2, “Explorando a API de JavaScript para Office: acesso a dados e eventos” (msdn.microsoft.com/magazine/jj991976), examina mais de perto o importante conceito de como obter conteúdo de arquivo e conduz uma revisão completa do modelo de Evento. Em continuação a este artigo, a Parte 4 se concentrará unicamente no terceiro tipo de aplicativo para Office: aplicativos de email.

Em toda esta série, mencionamos com frequência a documentação de referência da API JavaScript para Office. Você pode encontrar a documentação oficial, exemplos de código e recursos da comunidade na página de Visualização para desenvolvedor de Aplicativos para Office e SharePoint do MSDN (dev.office.com).

Vinculação de dados em um aplicativo para o Office

A vinculação de dados fornece uma estreita integração entre uma região específica de dados no documento e o aplicativo. Os dados na região são vinculados a um objeto nomeado no aplicativo de modo que o aplicativo possa acessar os dados na região nomeada, mesmo que o usuário tenha selecionado algo mais.

Depois de criada, a vinculação persiste mesmo se a região foi movida na página (no Word) ou copiada para outra planilha (no Excel). Por exemplo, uma vinculação a uma tabela persiste mesmo que ela seja renomeada pelo usuário.

Quando os dados em uma região são alterados, a vinculação gera um evento ao qual o aplicativo pode se conectar. Desse evento, o aplicativo pode acessar os dados que foram alterados e reagir adequadamente.

Vinculações e a “Visualização” de um aplicativo Certamente, a vinculação de dados em um aplicativo para Office dá ao aplicativo o acesso direto a um conjunto de dados dentro do arquivo Office, tornando então mais fácil para o aplicativo analisar esses dados sem contar com ação direta do usuário. Ainda, a vinculação de dados faz mais do que apenas permitir o acesso a dados direcionados, ela permite ao desenvolvedor incluir o próprio arquivo Office como um componente personalizável e integral do aplicativo.

Muitos aplicativos para Office apresentam aos seus usuários uma interface contida unicamente nos limites da interface do usuário do aplicativo de painel de tarefas ou de conteúdo, e não há nada de errado nisso. Ainda, em um sentido muito simples, os dados e sua apresentação no arquivo Office são em si uma “visualização” do aplicativo. Os usuário interagem com seus dados no arquivo Office. Eles inserem novos dados, alteram dados existentes e excluem dados desnecessários dentro do conteúdo do documento. Os aplicativos do Office apresentam uma visualização dos dados que os usuários conhecem e entendem.

Os recursos da vinculação de dados na API JavaScript para Office permitem aproveitar a visualização dos dados que o aplicativo do Office fornece em um aplicativo. Você, o desenvolvedor, pode desenvolver uma “interface” para seu aplicativo usando o que já é oferecido para você no Office. Desta forma, você pode estilizar a visualização de seu aplicativo usando os recursos prontos para uso do aplicativo do Office. A vinculação de dados então fornece os tendões que conectam a visualização do aplicativo ao “modelo” de lógica de negócios contido nos arquivos JavaScript.

Certamente, o inverso é verdadeiro, também. Você pode usar o arquivo Office como sua fonte de dados, armazenando o conteúdo do modelo de dados. É possível então usar o aplicativo para fornecer uma visualização dos dados. Com a flexibilidade das vinculações, você pode aplicar o padrão Model-View-Controller (MVC) a um aplicativo e arquivo Office conforme suas necessidades.

Cenários para uso de vinculações Sem colocar um limite rígido na criatividade dos desenvolvedores, um aplicativo pode usar vinculações em qualquer combinação de três maneiras generalizadas:

  • O aplicativo reage quando o usuário altera os dados na região
  • O aplicativo coleta os dados na região, analisa-os e apresenta ao usuário as opções de modelagem ou envio dos dados.
  • O aplicativo envia os dados de uma fonte de dados externa para a região vinculada.

Considere, por exemplo, um simples aplicativo de cotação da bolsa inserido em uma pasta de trabalho do Excel, em que uma coluna na pasta de trabalho contém símbolos das ações e outra contém valores de ações atuais. Com a vinculação de dados, o aplicativo poderia vincular-se à coluna com os símbolos das ações, selecionando os símbolos das ações na coluna. O aplicativo poderia então assinar as alterações no preço dessas ações via um serviço Web e analisar os resultados enviados a partir do serviço. Finalmente, o aplicativo poderia vincular-se à coluna de preços na planilha e atualizar os valores em tempo real.

Faremos apenas isso, criar uma pasta de trabalho de cotação da bolsa, na próxima seção ao examinar o objeto Binding.

Usando o objeto Binding A mágica subjacente da vinculação de dados está contida na coleção Bindings e no objeto Binding.

  • A coleção Bindings representa todas as vinculações criadas entre o arquivo Office e o aplicativo para Office. Um aplicativo não tem acesso a nenhuma vinculação criada por outros aplicativos.
  • O objeto Binding representa uma vinculação nomeada entre uma região do arquivo Office e o aplicativo. Ele expõe vários membros para obter, ler e definir dados, e reagir a alterações na região vinculada.

Vamos dar uma olhada mais de perto nesse objetos ao criarmos o aplicativo de cotação da bolsa.

Antes de prosseguir, vamos dar uma olhada rápida nos dados. A Figura 1 mostra como a visualização desse aplicativo se parece. Como pode ser visto, estamos usando símbolos de ações fictícias para fins de demonstração.

A Table Named “Stocks” in an Excel Workbook with Formulas and Conditional Formatting Applied
Figura 1 Uma tabela chamada “Stocks” em uma pasta de trabalho Excel com fórmulas e formatação condicional aplicadas

Ainda, já adicionamos alguma “inteligência” a essa pasta de trabalho. A região de dados a qual queremos vincular foi formatada como uma tabela e chamada “Stocks.” Uma fórmula personalizada foi adicionada aos valores da coluna à direita para comparar os outros valores na tabela. Também aplicamos formatação condicional à tabela para fazer aparecer os conjuntos de ícones na coluna à direita.

Vale observar que adicionamos essa pasta de trabalho à nossa solução no Visual Studio 2012 para que não tenhamos de recriar nossa tabela a cada vez que depurarmos o aplicativo. Para adicionar a pasta de trabalho à solução, clique com o botão direito do mouse no projeto do aplicativo na solução (o primeiro projeto listado no Gerenciador de Soluções quando usamos o modelo padrão), clique em Adicionar Item Existente e selecione sua pasta de trabalho. Em seguida, nas propriedades do projeto do aplicativo, defina Iniciar Ação em seu arquivo da pasta de trabalho. Na depuração, precisaremos inserir o aplicativo manualmente em sua pasta de trabalho (guia Inserir | botão Aplicativos para Office)

Depois de inicializado, a lógica de negócios do aplicativo precisa definir a vinculação e adicionar um manipulador de eventos ao evento da vinculação, Office.EventType.BindingDataChanged. A Figura 2 mostra o código. Observe que encapsulamos o código em uma função anônima que é executada automaticamente armazenada na variável StockTicker. O nome da tabela na planilha, o nome da vinculação e a vinculação estão todos armazenados como campos de classe dentro da “classe” StockTicker. A “classe” StockTicker expõe somente um único membro: initializeBinding.

Figura 2 Criando a vinculação à pasta de trabalho Excel e adicionando um manipulador ao evento alterado por dados na vinculação

var StockTicker = (function () {
  var tableName = "Sheet1!Stocks",
      bindingName = "Stocks",
      binding;
  // Create the binding to the table on the spreadsheet.
  function initializeBinding() {
    Office.context.document.bindings.addFromNamedItemAsync(
      tableName,
      Office.BindingType.Table,
      { id: bindingName },
      function (results) {
        binding = results.value;
        addBindingsHandler(function () { refreshData(); });
    });
  }
  // Event handler to refresh the table when the
  // data in the table changes.
  var onBindingDataChanged = function (result) {
    refreshData();
  }
  // Add the handler to the BindingDataChanged event of the binding.
  function addBindingsHandler(callback) {
    Office.select("bindings#" + bindingName).addHandlerAsync(
      Office.EventType.BindingDataChanged,
      onBindingDataChanged,
      function () {
        if (callback) { callback(); }
    });
  }
  // Other member methods of this "class" ...
  return {
    initializeBinding: initializeBinding
  };
})();

Para estabelecer uma vinculação entre o aplicativo e a tabela na planilha, podemos usar um de diversos métodos diferentes da classe Document na API de JavaScript, incluindo addFromNamedItemAsync, addFromPromptAsync e addFromSelectionAsync. (Observe que addFromPromptAsync está disponível somente no Excel e no Excel Web App.)

Como sabemos o nome da região a qual desejamos vincular - é a tabela intitulada “Stocks” em Sheet1 - usamos o método addFromNamedItemAsync para estabelecer a vinculação. Passamos o nome da tabela usando a notação de intervalo do Excel (Sheet1!Stocks). Os resultados dessa chamada de método inclui uma referência à própria vinculação, permitindo que armazenemos uma referência à vinculação em nossa variável de vinculação (campo de classe).

Em nosso código, passamos o valor Office.BindingType.Table para o parâmetro bindingType do método. Isso especifica que desejamos criar um tipo de vinculação “Tabela” com nossos dados, embora também poderíamos ter especificado um tipo de vinculação de texto ou matriz. Vincular à região como uma tabela nos fornece diversos benefícios. Por exemplo, se o usuário adiciona uma nova coluna ou linha à tabela, o escopo da região vinculada também aumenta. Isso também funciona do modo inverso. O objeto TableBinding, que fundamenta a vinculação, expõe propriedades para adição de colunas, adição de linhas e até exclusão de todos os dados da tabela.

(Consulte a seção intitulada “Acessando o conteúdo do arquivo do Office em um aplicativo para o Office” no segundo artigo dessa série para saber detalhes sobre os tipos de dados de texto e matriz na API de JavaScript API para Office.)

Nosso código então adiciona um manipulador ao evento BindingDataChanged da vinculação. Quando os dados se alteram na região vinculada, isso é, quando o usuário altera os dados na região, desejamos chamar uma função refreshData definida localmente para iniciar o processo que atualiza a tabela. Ainda, como a tabela ainda não foi atualizada com dados da fonte de dados, queremos chamar refreshData depois que o manipulador de eventos foi adicionado.

Você observará que a função addBindingsHandler usa o método Office.select para obter a vinculação, embora, em vez disso, poderíamos ter usado o método Bindings.getByIdAsync. A principal diferença entre os dois métodos é o nível de acesso aos dados retornados nos resultados. O método Office.select retorna uma promessa de objeto Binding ao código de chamada. Se o método for bem-sucedido, o objeto Binding retornado terá somente um número limitado de membros disponíveis para uso. Selecionando a vinculação com Office.select, podemos chamar membros do objeto Binding imediatamente. Dessa forma, não temos de adicionar um retorno de chamada para uma função que obtém a vinculação para adicionar um manipulador à vinculação.

(Você pode estar pensando que poderíamos ter usado simplesmente a variável “binding” local que captura a referência à vinculação. Você está certo, poderíamos. Escrevemos esse código como está para fins de demonstração.)

A Figura 3 exibe as funções refreshData e getBindingData. A função refreshData apenas começa a cadeia de chamadas assíncronas que obtém os dados de tabela da planilha chamando getBindingData. A função getBindingData contém uma chamada para o método Binding.getDataAsync e retorna os dados como um objeto TableData.

Figura 3 Obtendo os dados da vinculação de tabela e chamando o serviço Web

var StockTicker = (function () {
  // Other members of this "class"...
  // Refresh the data displayed in the bound table of the workbook.
  // This function begins a chain of asynchronous calls that
  // updates the bound table.
  function refreshData() {
    getBindingData();
  }
  // Get the stock symbol data from the bound table and
  // then call the stock quote information service.
  function getBindingData() {
    binding.getDataAsync(
      {
        startRow: 0,
        startColumn: 0,
        columnCount: 1
      },
      function (results) {
        var bindingData = results.value,
            stockSymbols = [];
        for (var i = 0; i < bindingData.rows.length; i++) {
          stockSymbols.push(bindingData.rows[i][0]);
        }
        getStockQuotes(stockSymbols);
    });
  }
  return {
    // Exposed members of the "class."
  };
})();

Na chamada de getDataAsync mostrada na Figura 3, poderíamos ter especificado o tipo de dados para recuperar (ou alterado o tipo de dados) explicitamente passando um objeto anônimo, {coercionType: Office.CoercionType.Table}, para o parâmetro de opções. Como não especificamos uma tipo de dados para recuperar, a chamada getDataAsync retorna os dados de vinculação em seu tipo de dados original (um objeto TableData).

O objeto TableData, como discutimos no segundo artigo, fornece mais estrutura aos dados com que estamos trabalhando, a saber, um cabeçalho e uma propriedade de linhas que pode ser usada para selecionar dados da tabela. Neste exemplo, precisamos apenas obter os símbolos das ações na primeira coluna da tabela. Como você pode se lembrar, a propriedade de linhas armazena os dados na tabela como uma matriz de matrizes, em que cada item na primeira matriz corresponde a uma linha na tabela.

Ao trabalhar com uma vinculação a um objeto TableData, podemos especificar um subconjunto das linhas e colunas a serem obtidas da vinculação, usando os parâmetros startRow e startColumn. Os dois parâmetros especificam pontos iniciais de base zero para os dados a serem extraídos da tabela, em que o canto superior esquerdo da tabela é o ponto de origem. (Observe que você deve usar os parâmetros startRow e startColumn juntos ou, então, você gerará uma exceção.) Como precisamos apenas da primeira coluna de dados da tabela, também passamos o parâmetro columnCount, definido como 1.

Depois de termos essa coluna de dados, enviamos cada valor a uma matriz unidimensional. Na Figura 3, você vê que chamamos uma função getStockQuotes que aceita a matriz de símbolos de ações como um argumento. Na Figura 4, usamos a função getStockQuotes para recuperar dados de um serviço Web de cotação de ações. (Para fins de demonstração, deixamos o código do serviço Web de fora.) Depois de termos analisado os resultados no serviço Web, chamamos o método removeHandler definido localmente.

Figura 4 Chamando o serviço Web e removendo o manipulador de eventos BindingDataChanged

var StockTicker = (function () {
  // Other members of this "class"...
  // Call a Web service to get new stock quotes.
  function getStockQuotes(stockSymbols) {
    var stockValues = [];
    // Make a call to the Web service and parse the results.
    // The results are stored in the stockValues variable, which
    // contains an array of arrays that include the stock symbol
    // with the current value.
    removeHandler(function () {
      updateTable(stockValues);
    });
  }
  // Disables the BindingDataChanged event handler
  // while the table is being updated.
  function removeHandler(callback) {
    binding.removeHandlerAsync(
      Office.EventType.BindingDataChanged,
      { handler: onBindingDataChanged },
      function (results) {
        if (results.status == Office.AsyncResultStatus.Succeeded) {
           if (callback) { callback(); }
        }
    });
  }
  return {
    // Exposed members of the "class."
  };
})();

A função removeHandler chama o método binding.removeHandlerAsync, que remove o manipulador de eventos do evento BindingDataChanged. Agora, se tivéssemos deixado o manipulador conectado ao evento, então, o evento teria gerado quando atualizamos a tabela. O manipulador de eventos teria sido chamado novamente e atualizaria a tabela, causando então um loop infinito. Depois de termos atualizado a tabela com os novos dados, adicionaríamos novamente o manipulador de eventos ao evento.

(Certamente, poderíamos ter criado também diferentes vinculações para separar as colunas na tabela, usando o tipo de coerção de matriz. Poderíamos então ter conectado eventos somente às colunas que os usuários podem editar.)

O método removeHandlerAsync toma um parâmetro, manipulador, que especifica o nome do manipulador a ser removido. É uma prática recomendada usar o parâmetro do manipulador para remover manipuladores de eventos de vinculação.

Na Figura 5, vamos atualizar a tabela com os novos valores de ações chamando a função updateTable definida localmente.

Figura 5 Obtendo os dados da vinculação de tabela e chamando o serviço Web

var StockTicker = (function () {
  // Other members of this "class"...
  // Update the TableData object referenced by the binding
  // and then update the data in the table on the worksheet.
  function updateTable(stockValues) {
    var stockData = new Office.TableData(),
        newValues = [];
    for (var i = 0; i < stockValues.length; i++) {
      var stockSymbol = stockValues[i],
          newValue = [stockSymbol[1]];
      newValues.push(newValue);
    }
    stockData.rows = newValues;
    binding.setDataAsync(
      stockData,
      {
        coercionType: Office.CoercionType.Table,
        startColumn: 3,
        startRow: 0
      },
      function (results) {
        if (results.status == Office.AsyncResultStatus.Succeeded) {
          addBindingsHandler();
        }
    });  
  }
  return {
    // Exposed members of the "class."
  };
})();

A função updateTable toma os dados passados do serviço Web e grava-os de volta na tabela vinculada. Nesse exemplo, o parâmetro stockValues contém uma outra matriz de matrizes, em que cada item na primeira matriz é uma matriz que contém um símbolo de ação e seu preço atual. Para definir esses dados de volta na tabela vinculada, criamos um novo objeto TableData e inserimos os dados do valor da ação nele.

Precisamos ser cuidadosos para que os dados definidos na propriedade TableData.rows corresponda à forma dos dados que estamos inserindo na vinculação. Se definirmos cegamente um objeto TableData totalmente novo na tabela vinculada, corremos o risco de perder alguns dados em nossa tabela, por exemplo, as fórmulas. Na Figura 5, adicionamos os dados ao objeto TableData como uma única coluna de dados (uma matriz de matrizes, em que cada submatriz contém um único item). Ao inserirmos esses dados de volta na tabela vinculada, precisamos inserir essa coluna atualizada de dados na coluna apropriada.

Novamente aqui usamos as propriedades startRow e startColumn. A função updateTable contém uma chamada para binding.setDataAsync que envia o TableData de volta para a tabela na planilha, especificando os parâmetros startColumn e startRow. O parâmetro startColumn é definido como 3, significando que o objeto TableData inserido irá inserir seus dados começando na quarta coluna da tabela. No retorno de chamada do método setDataAsync, chamamos a função addBindings­Handler novamente para reaplicar o manipulador de eventos ao evento.

Quando o método binding.setDataAsync é concluído com sucesso, os novos dados da tabela são enviados para a região vinculada e imediatamente exibidos. Da perspectiva do usuário a experiência é contínua. O usuário digita dados em uma célula da tabela, pressiona Enter e, então, a coluna Valor da tabela é atualizada automaticamente.

Partes XML personalizadas

Um recurso particularmente importante suportado pela API de JavaScript para Office é a capacidade de criar e manipular partes XML personalizadas no Word. Para apreciar o grande potencial da API de JavaScript para Office para partes XML personalizadas, algum histórico é útil. Especialmente, você precisa entender como o formato de arquivo Office Open XML (OOXML ou OpenXML), as partes XML personalizadas, os controles de conteúdo e o mapeamento XML podem ser combinados para criar soluções realmente poderosas, ou seja, soluções que envolvem a criação de documentos dinâmicos do Word.

Formatos OOXML O Office 2007 introduziu o novo formato de arquivo OOXML para documentos do Office, agora o formato de arquivo padrão do Office 2010 e Office 2013. (É possível dizer quais documentos do Office estão no formato de arquivo OOXML porque as extensões desses documentos são agora extensões com quatro letras, muitas delas terminando com “x”, por exemplo, “.docx” para um documento do Word, “.xlsx” para uma planilha do Excel ou “.pptx” para um documento do PowerPoint.)

Os documento do Office no formato OOXML são essencialmente arquivos .zip. Cada arquivo .zip contém uma coleção de arquivos XML, chamados “partes”, que juntos constituem o documento do Office. Se você renomear um documento do Office, como um documento .docx do Word, para .zip e, em seguida, examinar os arquivos que estão dentro, poderá ver que o documento é realmente apenas uma coleção de arquivos XML separados, organizados em pastas dentro de um pacote .zip, como mostrado na Figura 6.

File Structure of an Office Open XML Format Document
Figura 6 Estrutura de arquivo de um documento no formato Office Open XML

Noções básicas das partes XML personalizadas Embora haja partes XML padrão que os aplicativos do Office sempre criam para cada novo documento do Office no formato OOXML (por exemplo, há uma parte XML integrada que descreve as principais propriedades do documento), a coisa interessante é que você também pode adicionar suas próprias partes “XML personalizadas” a um documento do Word, pasta de trabalho do Excel ou apresentação do PowerPoint. As partes XML personalizadas são adicionadas à coleção de arquivos XML dentro do pacote .zip que forma o documento do Office. Uma parte XML personalizada é armazenada na estrutura de arquivo do documento, mas não é exibida para o usuário final. Isso lhe permite inserir dados corporativos que acompanham uma instância específica de um documento do Office oculto dentro da estrutura de arquivo. Você pode então trabalhar com esse XML personalizado em seu aplicativo. Isso é exatamente o que a API de JavaScript para Office dá suporte.

Controles de conteúdo Junto com o formato OOXML e sua estrutura de arquivo que permite a inclusão de XML personalizado em um documento, o Word 2007 adicionou controles de conteúdo, um recurso que complementa de forma avançada as partes XML personalizadas.

Os controles de conteúdo são uma forma de definir regiões fixas em um documento do Word que mantém certos tipos de dados, como texto sem formatação, rich text, imagens, datas e até mesmo dados repetidos. O principal aspecto dos controles de conteúdo que complementa as partes XML personalizadas é a vinculação de dados com o uso de mapeamento XML.

Mapeamento XML Um controle de conteúdo pode ser vinculado ou “mapeado” a um elemento no XML em uma parte XML que está contida no documento. Por exemplo, uma empresa poderia injetar dados corporativos de um sistema back-end como uma parte XML personalizada em um documento do Word que tem controles de conteúdo mapeados para a parte XML personalizada. Os controles de conteúdo são vinculados a nós específicos na parte XML personalizada, assim, quando o usuário final abre o documento, os controles de conteúdo mapeados para XML são automaticamente preenchidos com dados da parte XML personalizada. Ou, invertendo o cenário, uma empresa poderia usar o mesmo documento do Word com controles de conteúdo mapeados, mas fazer com que o usuário final insira dados nos controles de conteúdo. Quando o documento é salvo, os dados nos controles de conteúdo mapeados são salvos de volta no arquivo XML. Um aplicativo poderia então extrair os dados da parte XML personalizada no documento salvo e enviá-los a um sistema back-end. A API de JavaScript para Office fornece suporte avançado para desenvolvimento de aplicativos exatamente como os que foram descritos. 

Usando a API de JavaScript para Office para trabalhar com partes XML personalizadas A melhor maneira de percorrer algumas das partes mais significativas da API das partes XML personalizadas no modelo de objeto JavaScript de aplicativos para Office é por meio de um exemplo. Nesta seção, usamos o exemplo do “gerenciador de faturas” (bit.ly/YRdlwt) da área de Exemplos do portal do desenvolvedor de aplicativos para Office e SharePoint para que você possa acompanhar. O exemplo do gerenciador de faturas é um exemplo de um cenário de documento dinâmico em que uma empresa deseja gerar documentos que retiram dados de um sistema back-end para produzir faturas. Nesse caso, os dados são o nome de um cliente e o endereço para entrega, e uma lista associada das compras do cliente.  

O exemplo inclui um documento modelo usado para criar novas faturas. O documento modelo tem um layout com o nome do cliente, endereço e uma tabela de compras do cliente. As seções do nome do cliente, do endereço e das compras do documento são todas controles de conteúdo. Cada controle de conteúdo está mapeado para um nó no esquema que foi criado para conter os dados da fatura do cliente, como mostrado na Figura 7.

Content Controls on the Document Surface Mapped to a Custom XML Part
Figura 7 Controles de conteúdo na superfície do documento mapeados para uma parte XML personalizada

A interface do usuário do aplicativo de exemplo do gerenciador de faturas é direta, como mostrado na Figura 8.

The UI for the Invoice Manager Sample App
Figura 8 A interface do usuário do aplicativo de exemplo do gerenciador de faturas

O usuário final escolhe um número de fatura na caixa suspensa na interface do usuário do aplicativo e os dados do cliente associados ao número de fatura são mostrados no corpo do aplicativo, como mostrado na Figura 9.

The Invoice Manager UI Populated with Data from a Custom XML Part
Figura 9 A interface do usuário do gerenciador de faturas populada com dados de uma parte XML personalizada

Quando o usuário escolhe o botão Popular, o aplicativo envia os dados exibidos como uma parte XML personalizada para o documento. Como os controles de conteúdo são mapeados para nós na parte XML personalizada, assim que a parte XML personalizada é enviada para o documento, os controles de conteúdo imediatamente mostram os dados de cada nó XML para o qual foram mapeados. É possível substituir a parte XML personalizada instantaneamente (como fizemos aqui), mas desde que a parte esteja em conformidade com o esquema para o qual os controles de conteúdo estão mapeados, os controles de conteúdo mostrarão os dados mapeados. A Figura 10 mostra o formulário da Guia de remessa do gerenciador de faturas quando os controles de conteúdo estão mapeados para uma parte XML personalizada no documento.

Content Controls Mapped to Nodes in a Custom XML Part Showing Bound Data
Figura 10 Controles de conteúdo mapeados para nós em uma parte XML personalizada mostrando dados vinculados

O objeto CustomXmlParts

CustomXmlParts.addAsync A primeira etapa ao trabalhar com partes XML personalizadas é aprender como adicioná-las a um documento usando a API de JavaScript para Office. Isso pode ser feito apenas com o método customXml­Parts.addAsync. Como seu nome sugere, o método customXmlParts.addAsync adiciona uma parte XML personalizada de forma assíncrona e tem a seguinte assinatura:

Office.context.document.customXmlParts.addAsync(xml [, options], callback);

Observe que o primeiro parâmetro exigido para a função é uma cadeia de caracteres de XML. Esse é o XML da parte XML personalizada. Como mencionamos anteriormente, o gerenciador de faturas usa partes XML personalizadas que são mapeadas para os controles de conteúdo na superfície do documento, mas primeiro ele tem de obter dados do cliente para inserir como XML personalizado. No arquivo InvoiceManager.js, que contém a lógica do aplicativo inteiro, o aplicativo simula a obtenção de dados do cliente de um sistema back-end usando a função definida pelo usuário setupMyOrders. Essa função cria uma matriz de três objetos que representa três pedidos do cliente. Você pode, certamente, imaginar inúmeras maneiras que uma empresa pode armazenar e obter um histórico de compras do cliente, por exemplo, um banco de dados SQL, mas para simplificar, o aplicativo cria três pedidos de cliente “programados” diretamente dentro do aplicativo.

Depois que os objetos de pedidos são criados, os dados que eles representam devem ser renderizados em XML para que possam ser usados na chamada para custom­XmlParts.addAsync. É o que acontece na função initializeOrders, que também configura a interface do usuário do aplicativo e conecta manipuladores de eventos aos controles na interface do usuário. A parte importante a ser notada está no código jQuery que conecta um manipulador de eventos para o evento de clique do botão Popular, como mostrado na Figura 11.

Figura 11 Conectando um manipulador de eventos para o evento de clique do botão Popular

$("#populate").click(function () {
  var selectedOrderID = parseInt($("#orders option:selected").val());
  _document.customXmlParts.getByNamespaceAsync("", function (result) {
    if (result.value.length > 0) {
      for (var i = 0; i < result.value.length; i++) {
        result.value[i].deleteAsync(function () {
        });
      }
    }
  });
  var xml = $.json2xml(findOrder(myOrders, selectedOrderID));
  _document.customXmlParts.addAsync(xml, function (result) { });
});

Essencialmente, a função anônima que atua como o manipulador de eventos para o evento de clique do botão Popular converte um objeto de pedido (que foi criado com a função setupMyOrders) em uma cadeia de caracteres de XML e, em seguida, chama o método customXmlParts.addAsync e passa a cadeia de caracteres de XML que contém as informações do pedido como o primeiro parâmetro exigido.

O outro parâmetro para customXml­Parts.addAsync é um retorno de chamada. Certamente, isso pode ser uma referência para um método definido em outro lugar do código, ou pode ser uma função anônima. O exemplo de gerenciador de faturas usa uma função anônima embutida:

_document.customXmlParts.addAsync(xml,
  function (result) { });

Como é o caso de todos os retornos de chamadas na API de Java­Script para Office, um objeto AsyncResult é passado como o único argumento do retorno de chamada. Para customXmlParts.addAsync e todas as funções customXmlParts, você pode usar o objeto AsyncResult para:

  • obter uma referência para a parte XML personalizada recém-criada usando a propriedade Async­Result.value
  • obter o resultado da solicitação usando a propriedade AsyncResult.status
  • obter informações a respeito de um erro (se ocorreu algum) usando a propriedade Async­Result.error
  • obter seus próprios dados de estado (se incluiu algum na chamada para custom­XmlParts.addAsync) usando a propriedade AsyncResult.asyncContext

Para esse último item, lembre-se de que o outro parâmetro no método customXmlParts.addAsync foi um objeto de opções opcional:

Office.context.document.customXmlParts.addAsync(
  xml [, options], callback);

O objeto de opções é fornecido como uma forma de você passar seu próprio objeto definido pelo usuário na chamada para seu retorno de chamada.

Como pode ser visto no exemplo do gerenciador de faturas, a função anônima na chamada para customXmlParts.addAsync não faz nada, mas em um ambiente de produção você provavelmente desejaria fazer verificação de erro para manipular uma instância com habilidade, se por algum motivo a parte XML personalizada não for adicionada com sucesso.

CustomXmlParts.getByNamespaceAsync Outra parte importante da API de JavaScript para Office para se trabalhar com partes XML personalizadas que é demonstrada no exemplo do gerenciador de faturas é o uso do método customXmlParts.getByNamespaceAsync, que pode ser visto no código do manipulador de eventos de clique do botão Popular. A assinatura para customXmlParts.getByNamespaceAsync é:

Office.context.document.customXmlParts.getByNamespaceAsync(
  ns [, options], callback);

O primeiro parâmetro exigido, ns, é uma cadeia de caracteres que especifica o namespace das partes XML personalizadas que você deseja obter. Assim, customXmlParts.getByNamespaceAsync retorna uma matriz de partes XML personalizadas no documento que têm o mesmo namespace especificado. Como as partes XML personalizadas criadas no exemplo do gerenciador de faturas não usam namespaces, a chamada para customXmlParts.getByNamespaceAsync passa uma cadeia de caracteres vazia como o argumento do parâmetro namespace, como mostrado na Figura 12.

Figura 12 Usando o método CustomXmlParts.getByNamespaceAsync

$("#populate").click(function () {
  var selectedOrderID = parseInt($("#orders option:selected").val());
  _document.customXmlParts.getByNamespaceAsync("", function (result) {
    if (result.value.length > 0) {
      for (var i = 0; i < result.value.length; i++) {
        result.value[i].deleteAsync(function () {
        });
      }
                    }
     });
     var xml = $.json2xml(findOrder(myOrders, selectedOrderID));
     _document.customXmlParts.addAsync(xml, function (result) { });
   });
   var selOrder = $("#orders option:selected");
   popOrder(selOrder.val());

Como todas as funções assíncronas na API, customXmlParts.getByNamespaceAsync tem opções e parâmetros de retorno de chamada opcionais.

CustomXmlParts.getByIdAsync A última maneira programática de obter uma parte XML personalizada em um documento é a customXmlParts.getByIdAsync. A assinatura é:

Office.context.document.customXmlParts.getByIdAsync(id [, options], callback);

Essa função obtém uma parte XML personalizada única usando o GUID da parte. O GUID da parte XML personalizada é encontrado no arquivo itemPropsn.xml dentro do pacote do documento. É possível também obter o GUID usando a propriedade id de customXmlPart. Uma coisa importante a ser observada aqui é que a cadeia de caracteres do GUID deve conter as chaves (“{}”) em torno do GUID.

O exemplo do gerenciador de faturas não usa a função customXml­Parts.getByIdAsync, mas o seguinte código demonstra isso de forma suficientemente clara:

function showXMLPartBuiltId() {
  Office.context.document.customXmlParts.getByIdAsync(
    "{3BC85265-09D6-4205-B665-8EB239A8B9A1}", function (result) {
    var xmlPart = result.value;
    write(xmlPart.id);
  });
}
// Function that writes to a div with id='message' on the page.
function write(message){
  document.getElementById('message').innerText += message;
}

Além do parâmetro id, como customXmlParts.addAsync e customXmlParts.getByNamespaceAsync, o método customXml­Parts.getByIdAsync também tem o parâmetro opcional, opções e o retorno de chamada do parâmetro exigido, e eles são usados exatamente como nas outras funções.

The CustomXmlPart Object O objeto customXmlPart representa uma parte XML personalizada única. Depois de obter uma referência a uma customXmlPart usando os métodos do objeto customXmlParts, você tem várias propriedades disponíveis, como mostrado na Figura 13.

Figura 13 Propriedades do CustomXmlPart

Nome Descrição
builtIn Obtém um valor que indica se o customXmlPart é interno.
id Obtém o GUID do customXmlPart.
namespaceManager Obtém o conjunto de mapeamentos de prefixo do namespace (customXmlPrefixMappings) usado em relação ao customXmlPart atual.

CustomXmlPart também tem eventos associados a ele, que são mostrados na Figura 14.a

Figura 14 Eventos do CustomXmlPart

Nome Descrição
nodeDeleted Ocorre quando um nó é excluído.
nodeInserted Ocorre quando um nó é inserido.
nodeReplaced Ocorre quando um nó é substituído.

Mas para a finalidade deste artigo, queremos nos concentrar em alguns métodos importantes do objeto customXmlPart que, com frequência, serão usados por desenvolvedores. Eles estão mostrados na Figura 15.

Figura 15 Métodos do CustomXmlPart

Nome Descrição
addHandlerAsync Adiciona de forma assíncrona um manipulador de eventos para um evento do objeto customXmlPart.
deleteAsync Exclui de forma assíncrona essa parte XML personalizada da coleção.
getNodesAsync Obtém de forma assíncrona quaisquer customXmlNodes na parte XML personalizada que correspondam ao XPath especificado.
getXmlAsync Obtém de forma assíncrona o XML dentro dessa parte XML personalizada.

CustomXMLPart.addHandlerAsync O método customXmlPart.add­HandlerAsync é importante para conectar manipuladores de eventos que respondem a alterações na parte XML personalizada. A assinatura do método customXmlPart.addHanderAsync é a seguinte:

customXmlPart.addHandlerAsync(eventType, handler [, options], callback);

Observe que o primeiro parâmetro exigido é uma enumeração Office.EventType, que especifica qual tipo de evento nos aplicativos para o modelo de objeto do Office você deseja manipular. O próximo parâmetro exigido é o manipulador do evento. A coisa importante aqui é que quando o manipulador for chamado, a API de JavaScript para Office passará um parâmetro de argumentos do evento específico ao tipo de evento sendo manipulado (NodeDeletedEventArgs, NodeInsertedEventArgs ou NodeReplacedEventArgs). Em seguida, como em todas as funções assíncronas na API, você tem, opcionalmente, opções e parâmetros de retorno de chamada.

Considere o cenário em que um documento está sendo usado como um formulário de entrada de dados. O usuário insere dados no formulário e, em seguida, o formulário é extraído para os dados. O formulário contém um controle de conteúdo Seção repetida de modo que cada vez que o usuário insere um item repetido, um novo nó é adicionado à parte XML personalizada subjacente. Toda vez que um nó é adicionado, ou inserido, o evento NodeInserted é disparado e você pode reagir ao evento (e a todos eventos customXmlPart) usando customXmlPart.addHandlerAsync.

A Figura 16 mostra como você poderia responder ao evento NodeInserted.

Figura 16 Conectando um manipulador de eventos para o evento CustomXmlPart.NodeInserted

function addNodeInsertedEvent() {
  Office.context.document.customXmlParts.getByIdAsync(
    "{3BC85265-09D6-4205-B665-8EB239A8B9A1}", function (result) {
    var xmlPart = result.value;
    xmlPart.addHandlerAsync(Office.EventType.NodeInserted,
      function (eventArgs) {
        write("A node has been inserted.");
    });
  });
}
// Function that writes to a div with id='message' on the page.
function write(message){
  document.getElementById('message').innerText += message;
}

CustomXMLPart.deleteAsync Com certeza, além de saber como adicionar uma parte XML personalizada, é importante saber como excluí-la. O método customXmlPart.deleteAsync fornece essa funcionalidade. CustomXmlPart.deleteAsync é uma função assíncrona com a seguintes assinatura:

customXmlPart.deleteAsync([options ,] callback);

Voltando ao exemplo do gerenciador de faturas, você pode ver uma demonstração de customXMLPart.deleteAsync: 

$("#populate").click(function () {
  var selectedOrderID = parseInt($("#orders option:selected").val());
  _document.customXmlParts.getByNamespaceAsync("", function (result) {
    if (result.value.length > 0) {
      for (var i = 0; i < result.value.length; i++) {
        result.value[i].deleteAsync(function () {
        });
      }
    }
});

No manipulador do evento de clique do botão Popular, a lógica do programa verifica se há qualquer parte XML personalizada com namespaces “em branco”. Se houver, ela exclui cada uma usando o método custom­XmlPart.deleteAsync.

Há muito mais para o trabalho com partes XML personalizadas, mas o que percorremos neste artigo deve ser suficiente para lhe dar uma ideia do suporte avançado que a API de JavaScript para Office fornece para as partes XML personalizadas.

A seguir: aplicativos de email

Neste terceiro artigo da série, examinamos algumas técnicas avançadas para o trabalho com dados em aplicativos do Office. Mostramos como adicionar inteligência adicional a uma tabela no Excel usando vinculações de dados. Também exploramos como aproveitar as partes XML personalizadas em um aplicativo do Word para facilitar a criação de documentos automatizada.

No próximo e final artigo desta série, examinaremos como a API de JavaScript para Office se aplica aos aplicativos de email. Os aplicativos de email representam um conjunto exclusivo de recursos dentro da API de JavaScript para Office, permitindo que os desenvolvedores de aplicativos e administradores do Exchange criem ferramentas poderosas para o trabalho com itens de email.

Stephen Oliver é um escritor de programação na Divisão do Office e um Microsoft Certified Professional Developer (SharePoint 2010). Escreve a documentação do desenvolvedor para Serviços do Excel e Word Automation Services, bem como para PowerPoint Automation Services. Ajudou a organizar e desenvolver o site Excel Mashup, em ExcelMashup.com.

Eric Schmidt é um escritor de programação na Divisão do Office. Criou vários exemplos de código para aplicativos para o Office, incluindo o popular “Manter as configurações personalizadas”. Além disso, escreveu artigos e criou vídeos sobre outros produtos e tecnologias na programação do Office.

Agradecemos aos seguintes especialistas técnicos pela revisão deste artigo: Mark Brewster Microsoft), Shilpa Kothari (Microsoft) e Juan Balmori Labra (Microsoft)
Mark Brewster graduado como bacharel em matemática e ciência da computação pela Universidade do Arizona em 2008 e desenvolve software na Microsoft há quatro anos. Ele anda de bicicleta por diversão e para economizar, e gosta de beber cerveja e ouvir discos.

Shilpa Kothari (Bhavsar) é engenheira de software em teste da Microsoft. Ela trabalhou em vários produtos da Microsoft, como Bing Mobile, Visual Studio e Office. Ela é apaixonada por controle de qualidade de software e experiência do usuário e pode ser contatada através do email shilpak@microsoft.com.

Juan Balmori Labra é gerente de programa, que trabalhou nos últimos três anos na API de JavaScript do Microsoft Office. Ele trabalhou anteriormente na versão Office 2010, entregando Serviços Corporativos de Conectividade e Duet. Antes de ir em busca de seu sonho de se mudar para Redmond, Juan trabalhou na Microsoft México como o arquiteto principal da prática de consultoria para o setor público.