Codificação com Office Render
This content is not available in your language yet.
Introdução
O office-render
é um motor de template inspirado no Mustache, mas focado em arquivos Microsoft Office. Ele funciona expandindo {{tags}}
presentes no template com valores, expressões, funções e subtemplates fornecidos em formato json. Pode ser importado como módulo de serviço em runtime node.js ou função remota serverless utilizando API.
O diferencial desse motor é o uso de Angular Expressions + Angular Filters. Isso quer dizer que, além dos convencionais valores json
e lambdas
do Mustache, também pode-se utilizar expressões parecidas com Javascript ganhando maior flexibilidade, sem deixar de lado a segurança.
TL;DR;
O office-render
segue a arquitetura de requisição e respostas. Seu uso se dá através do envio de um payload
de configuração e recebe uma promessa de entrega de um documento tipo Microsoft Office no caso de sucesso ou erro se não for possível gerar o documento com as configurações enviadas.
Para usar como serviço:
Para usar como API:
Definir header Content-Type: application/json; charset=utf-8
e enviar:
Dado o seguinte config:
Irá produzir:
MUSTACHE TAGS
Tags são indicadas através da marcação com chave dupla {{tag}}
ou chave tripla {{{tag}}}
. Vejamos seus diferentes tipos:
Undefined
O desacoplamento entre a view criada pelo designer e o model fornecido pela engenharia tem como conseqüencia um caso especial que é quando a view está esperando um valor, mas ele está ausente no model. Nesse caso, a tag não será interpolada e permanecerá no documento final.
Variáveis
O tipo mais simples de tag é a {{variable}}
. Ela vai procurar pelo valor guardado na chave variable
do contexto atual. Se não houver variable
no contexto atual, os contextos superiores serão avaliados recursivamente.
::: important
Variáveis marcadas com chave dupla {{html_escaped}}
passam, por padrão de segurança por HTML escape. Se o autor precisar do conteúdo raw, deve-se utilizar o mustache triplo: {{{unescaped_html}}}
:::
Seções e Seções Invertidas
Seções são úteis para alterar o contexto por uma parte. O comportamento é diferente dependendo do valor associado à chave. Uma seção começa com uma cerquilha #
e termina com uma barra /
. Isto é, {{section}}
começa uma seção e {{/section}}
termina uma seção (também pode ser apenas {{/}}
).
Enquanto as seções são úteis para alterar o contexto por uma parte, as seções invertidas são úteis para capturar a ausência de conteúdo. Uma seção invertida começa com um acento circunflexo ^
e termina com uma barra /
. Isto é, {{^inverted_section}}
começa uma seção invertida e {{/inverted_section}}
termina a seção invertida (também pode ser apenas {{/}}
).
Booleanos
Se a seção {{#boolean}}
for verdadeira, o conteúdo definido na seção será renderizado; se for falsa, o conteúdo será ignorado. Por exemplo:
Template
Config
Resultado
Listas
Se a seção {{#array}}
existir e possuir conteúdo, a parte será reavaliada para cada valor da lista; se estiver vazia, o conteúdo será ignorado. Por exemplo:
Template
Config
Resultado
Objetos
Se a seção {{#object}}
existir, a parte será reavaliada com o contexto atualizado para object. Por exemplo:
Template:
Config:
Resultado:
Lambdas
Quando o valor associado à chave é uma função, ela será executada com o atual contexto scope
como primeiro argumento e o gerenciador de contextos scopeManager
como segundo argumento. Por exemplo:
Template:
Config:
Resultado:
Outro exemplo:
Template:
Config:
Resultado:
::: tip
Este foi um exemplo ilustrativo de if-else. No contexto anterior, o ideal seria utilizar um operador ternário. e.g. const {isOrganization, federalTaxNumber} = scope.user; return ${isOrganization ? 'DUNS' : 'SSN'} ${federalTaxNumber}
.
:::
ANGULARJS EXPRESSIONS
Em matemática, quando combinamos números e variáveis de uma forma válida, utilizando operadores como adição, subtração, multiplicação, divisão, exponenciação et cetera e funções, damos o nome de expressão matemática à agregação combinada desses símbolos.
Em linguagens de programação, uma expressão é uma unidade de código que pode ser reduzida à um valor. No Javascript, existem dois tipos de expressões: as que produzem efeitos colaterais (como atribuir valor) e aquelas que não (como resultado de um cálculo).
A expressão x = 7
é um exemplo do primeiro tipo. Essa expressão usa o operador =
para atribuir o valor sete para a variável x
. O resultado da expressão resulta em 7
.
A expressão 3 + 4
é um exemplo do segundo tipo. Essa expressão usa o operador +
para adicionar 3
e 4
produzindo o valor 7
conhecido como soma.
Como dito inicialmente, o grande diferencial desse motor é o uso das AngularJS Expressions. Extraídas do código fonte do projeto angular.js, elas permitem a criação simplificada de templates complexos. São exemplos de AngularJS Expressions:
Operadores de atribuição
Um operador de atribuição associa o valor ou resultado do operando direito ao operando esquerdo. O mais simples dos operadores de atribuição é o igual (=
) e um exemplo clássico do uso desse operador é y = f(x)
.
Operador | Descrição | Exemplo |
---|---|---|
Assignment = | Atribui o valor do operando à direita ao operando da esquerda | x = 1 |
Operadores de comparação
Um operador de comparação avalia seus operandos e retorna um valor lógico baseado no fato da comparação se mostrar verdadeira ou não. Os operandos podem ser numéricos, textuais, booleanos ou propriedades de objetos. Valores textuais são comparados utilizando a ordem lexicográfica padrão usando valores unicode.
Operador | Descrição | Exemplo |
---|---|---|
Equal == | Retorna true se os operandos são iguais | 1 == '1' |
Not Equal != | Retorna true se os operandos não são iguais | 1 != 2 |
Strict Equal === | Retorna true se os operandos são iguais e possuem o mesmo tipo | 1 === 1 |
Strict not equal !== | Retorna true se os operandos não são iguais | 1 !== '1' |
Greater than > | Retorna true se o operando da esquerda é maior que o operando da direita | 2 > 1 |
Greater than or equal >= | Retorna true se o operando da esquerda é maior ou igual ao operador da direita | 1 >= 1 |
Less than < | Retorna true se o operando da esquerda é menor que o operando da direita | 1 < 2 |
Less than or equal | Retorna true se o operando da esquerda é menor ou igual ao operando da direita | 1 <= 1 |
Operadores aritméticos
Operadores aritméticos utilizam valores numéricos, tanto literais como variáveis, como seus operandos e entregam como resultado outro valor numérico. Os operadores aritméticos padrão são adição +
, subtração -
, multiplicação *
e divisão /
. Esses operadores funcionam de forma análoga ao esperado em outras linguagens de programação quando consideramos o operador $\circ$:
$$\circ : IEEE754^n \rightarrow IEEE754$$
::: important Uma particularidade da divisão nesse motor é que a divisão por zero produz Infinity devido à natureza aproximada da especificação de ponto flutuante. :::
Além dos já consagrados +
, -
, *
, /
segue abaixo a relação de outros operadores aritméticos disponíveis:
Operador | Descrição | Exemplo |
---|---|---|
Remainder % | Operador binário. Retorna o valor inteiro resto da divisão entre dois operandos | 12 % 5 resulta em 2 |
Unary negation - | Operador unário. Retorna o inverso aditivo do operando | Se x = 1 , o valor de -x é -1 |
Unary plus + | Operador unário. Retorna a versão numérica de um operando | +'1' retorna 1 |
Operadores lógicos
Geralmente, operadores lógicos são utilizados com valores booleanos, mas nesse motor funcionam com valores falsy e truthy. Operandos como &&
e ||
retornam o valor de um dos seus operandos, então se os valores forem não booleanos, as expressões retornam esse valor. Abaixo, segue a definição e tabela de quem são os valores falsy e truthy:
Falsy: um valor que é considerado false
quando aplicado no contexto booleano
Truthy: valores complementares aos Falsy
Operador | Descrição |
---|---|
false | Palavra reservada false |
0 | O número ZERO (também 0.0 , 0x0 , i.e. qualquer representação de ZERO) |
-0 | A negação do número ZERO |
"" , '' | Conteúdo textual vazio |
null | Palavra reservada null representa a ausência intencional de objeto |
undefined | Palavra reservada undefined representa que o tipo e valor da variável ainda são desconhecidos |
NaN | Palavra reservada NaN representa um resultado algébrico que não pode ser representado como número. Exemplos: 'render'**2 , 'nagao'/2 |
document.all | Objetos são considerados falsy se, e somente se, possuem um slot interno IsHTMLDDA definidos pelo motor de execução. |
Definidos falsy e truthy, podemos descrever os operadores lógicos a seguir:
Operador | Descrição | Exemplo |
---|---|---|
Logical AND && | Retorna expr1 , se for falsy e expr2 caso contrário. i.e. && resulta em true quando ambos operandos são truthy | expr1 && expr2 |
Logical OR ` | ` | |
Logical NOT ! | Operador unário, retorna false se o operando for truthy | !expr |
Operadores de texto
Além dos operadores de comparação, que também servem para texto, o operador de concatenação +
concatena dois operandos do tipo texto, retornando a união seqüencial da esquerda para direita dos operandos.
Operador condicional ternário
Existe apenas um operador padrão que recebe três operandos: o operador condicional ternário. Nele, o resultado varia entre dois valores dependendo da condição. Sua sintaxe é:
Por exemplo:
Essa expressão retorna ‘adulto’ se a variável idade
assumir valor maior ou igual a dezoito anos e ‘menor’ caso contrário.
Diferenças entre AngularJS Expressions e Javascript Expressions
- Contexto: AngularJS Expressions utilizam como contexto o objeto
scope
; - Indulgente: No Javascript, a avaliação de propriedades com
undefined
disparaReferenceError
ouTypeError
. No AngularJS Expressions, a avaliação é permissiva paraundefined
enull
; - Filters: É possível aplicar transformações adicionais ao valor da expressão antes da impressão;
- Ausência de controladores de fluxo: AngularJS Expressions não oferece suporte a declarações condicionais, loops ou disparo de erros;
- Impossibilidade de declarar funções: Em AngularJS Expressions, não é possível declarar novas funções;
- Impossibilidade de declarar expressões regulares: Em AngularJS Expressions, não é possível declarar expressões regulares;
- Impossibilidade de criação de novos objetos utilizando o operador
new
: AngularJS Expressions não interpreta o operador new; - Ausência da notação de atalho para operações de atribuição: Em AngularJS Expressions, não é possível utilizar
+=
,-=
,*=
,/=
,%=
,**=
,<<=
,>>=
,>>>=
,&=
,^=
,|=
,&&=
,||=
,??=
; - Ausência de operadores aritméticos increment, decrement e exponentiation: Não é possível utilizar operados
++
,--
e**
, - Ausência de operadores bitwise, comma e void: Não é possível utilizar operadores bitwise,
,
, ouvoid
; - Ausência de operadores relacionais: Não é possível utilizar operadores relacionais
in
einstanceof
.
::: tip Para regras complexas, deve-se utilizar expressões lambda que são chamadas pelo nome no template. :::
ANGULARJS FILTERS
Filtros permitem a transformação do resultado da expressão antes de aplicar a interpolação. Para chamar um filtro, deve-se utilizar a seguinte notação:
É possível passar argumentos aos filtros. Por exemplo:
Imprime
Filtros permitem chaining. Por exemplo:
Outra forma ainda é utilizar uma imagem como placeholder para outra imagem. Para isso, clicar com botão direito na imagem placeholder, selecionar Edit Alt Text … e colocar {{%image}}
como conteúdo.
Botão Direito | Alt Text |
---|---|
Right Click Menu width=290px | [Edit Alt Text Screen] |
WORD
HTML e Markdown
Criadores de conteúdo web podem ficar felizes, pois o motor aceita aplicar valores Markdown e HTML. Em linhas gerais, elementos HTML permitidos no <body>
são de block-level ou text-level (também conhecido por inline). A distinção é fundamentada em diversos conceitos:
- Conteúdo: Geralmente, elementos block-level podem conter outros elementos block-level e também text-level; enquanto os text-level possuem apenas dados e outros elementos text-level. A idéia chave dessa separação estrutural é que elementos block-level criam estruturas maiores que elementos text-level.
- Formatação: Por padrão, a formatação de elementos block-level e text-level é diferente. Elementos block-level começam novas linhas e text-level não.
- Direcionalidade: Por razões técnicas envolvendo o funcionamento do algorítmo de texto bidirecional unicode, os elementos block-level e text-level diferem na forma como trabalham a direcionalidade do conteúdo.
Portanto, é importante utilizar a semântica correta para informar o office-render
na hora de enviar conteúdo HTML. Para indicar que a tag irá receber um valor text-level (imprimir dentro do parágrafo), deve-se utilizar a notação {{~inline}}
e para indicar que a tag irá receber um valor block-level (irá criar um novo parágrafo), deve-se utilizar a notação {{~~block}}
. Analogamente, pode-se utilizar Markdown utilizando {{~inline|markdown}}
para conteúdos text-level e {{~~block|markdown}}
para conteúdos block-level.
O que é Markdown?
Markdown é a ferramenta de conversão text-to-HTML
para escritores que venceu as adversárias no começo dos anos 2000 e se tornou padrão de mercado. Ela define a escrita de conteúdo semântico textual utilizando uma notação fácil-de-ler, fácil-de-escrever que é convertida mecanicamente em HTML. Por exemplo, esse manual foi todo escrito utilizando Markdown. No Wordpress e no GitHub, utiliza-se Markdown para escrever artigos. Para maiores informações sobre sua syntax, visite o playground.
Relação de tags text-level suportadas: <br />
, <span>
, <small>
, <ins>
, <del>
, <strong>
, <em>
, <a>
, <sub>
, <sup>
.
Relação de tags block-level suportadas: <p>
, <h1>
, <h2>
, <h3>
, <h4>
, <h5>
, <h6>
, <ul>
, <ol>
, <li>
, <pre>
, <code>
, <table>
, <thead>
, <tfoot>
, <tr>
, <th>
, <td>
, <img>
.
Relação de propriedade de estilo suportadas: font-family
, font-size
, color
, background-color
, text-decoration
, text-align
, vertical-align
, border
, break-before
, break-after
, width
, height
, padding-left
.
::: important Não é possível utilizar HTML e Markdown em arquivos PowerPoint e Excel. O fato do Word compartilhar da mesma natureza de fluxo linear, facilita a conversão. Já documentos PowerPoint possuem múltiplos slides, cada um armazenado em um arquivo diferente com cada elemento posicionado absolutamente em relação ao slide. :::
Notas de rodapé
Para inserir notas de rodapé em uma página, deve-se utilizar a tag {{:footnote id}}
, onde id
é a chave no datasource que contém a nota de rodapé.
Template:
Config:
Resultado:
Amazing Grace: Rear Adm. Grace Hopper, USN, was a pioneer in computer science.
Military Officer. Vol. 12, no. 3. Military Officers Association of America. pp. 52–55, 106. Retrieved March 1, 2014.
Subtemplates E Segments
A operação de transclusão no office-render
é realizada utilizando subtemplates. Ou seja, incluindo outros documentos Word. Para isso, o motor oferece dois tipos diferentes de tags: {{:include id}}
, {{:segment}}
e {{:includesegment}}
.
Subtemplates
A tag {{:include id}}
permite a inserção de um documento ou template “ancestral” no corpo do template em construção. Por exemplo:
Templates:
Config:
Resultado:
Config:
Dash Syntax
Geralmente, quando usamos seções, o motor vai supor qual elemento do office que utilizará na iteração. Por exemplo, se entre as {{#tags}} ... {{/}}
:
- Existir uma célula de tabela (
<w:tc>
ou<a:tc>
), o motor irá iterar sobre a linha da tabela (<w:tr>
ou<a:tr>
), - Em outros casos, o motor não expandirá o loop e ficará restrito à parte definida na seção.
Com a notação Dash Syntax o autor pode definir o elemento ooxml
que ele deseja iterar. Por exemplo, se o desejo for criar um novo parágrafo para cada elemento de uma lista, pode-se fazer o seguinte:
Javascript
O motor office-render
, suas extensões e lambdas utilizadas em runtime utilizam a linguagem Javascript. O runtime de execução é continuamente atualizado de acordo com a disponibilidade de novas versões do NodeJS e V8 nos ambientes de hospedagem serverless. Isso faz com que novas funcionalidades que apareçam na camada de motor e linguagem fiquem também disponíveis na confecção dos templates.