Skip to content

Objeto app ​

Tipo
Referência
Para
Desenvolvedor

Consultar dados, pôr um filtro na barra do Dashboard, formatar um valor, saber o tema e quem está vendo: tudo o que o código de um Widget Svelte faz com a Aplicação passa pelo objeto app. O componente o recebe como propriedade (export let app;); um componente paramétrico recebe também config.

O app é um store do Svelte. app.fetchMatrix(...) chama uma função; $app.theme lê o estado e reage quando ele muda.

Membros ​

MembroDevolveO que faz
fetchMatrix(params)Promise<{ Result, Columns, columnKeys, Warnings, SQL }>Consulta os dados da Aplicação; cada linha de Result é uma lista, na ordem de columns. Veja fetchMatrix
fetchData(params)Promise<{ Result, Columns, columnKeys, Warnings, SQL }>A mesma consulta, com cada linha como objeto. Veja fetchData
columnFromString(texto)objeto de coluna ou nullConverte '[Vendas]."VALOR":SUM' no objeto que addFilter, getFilter e getReportUrl pedem
formatValue(valor, coluna)textoFormata o valor com a máscara da coluna. coluna pode ser o objeto ou o texto
isColumnNumeric(coluna)booleantrue para sufixo numérico (SUM, AVG, MIN, MAX, COUNT, DISTINCT) ou expressão numérica
addFilter(coluna, operador, valor, comparativos?, encode?)índice do filtro ou nadaPõe ou troca um filtro na barra do Dashboard. Veja Filtros
getFilter(coluna)filtro ou undefinedO filtro da barra nessa coluna, com o mesmo sufixo
removeFilter(filtro)nadaTira um filtro da barra. Recebe o objeto que getFilter devolveu
fetchFilterValues(coluna)Promise<{ data: { Result, Columns } }>Os valores distintos da coluna, com os outros filtros do Dashboard aplicados
getDateFilterReference(column_id, padrão?){ start, end, relativeToken? }O período do filtro de data. Veja Período do filtro de data
getReportUrl(params)textoMonta o link para o Explorer. Não navega. Veja Link para o Explorer
evalExpression(corpoJs, params?, nome?)Promise<valor ou undefined>Avalia uma fórmula em JavaScript, como as dos componentes paramétricos. Em erro, loga no console e devolve undefined; nunca lança
generateJWTToken()Promise<{ token, payload } ou null>Gera um token assinado para chamar uma API sua. Veja JWT
user{ id, nome } ou nullQuem está vendo. Veja Usuário
$app.theme'light' ou 'dark'O tema da página. Veja Tema
$app.moldura{ ativa, cores? }As cores da marca no aplicativo móvel. Veja Tema
writebackobjeto com listTables, metadata, find, findOne, save e deleteLê e grava registros de um Cadastro da Aplicação. Veja Gravar em Cadastro
refreshDashboard()Promise<void>Recarrega os outros Widgets do Dashboard, sem remontar o que chamou. Veja Depois de gravar
get()o estado da AplicaçãoLeitura pontual do estado (filtros, dados da Aplicação, tema). Sem reatividade

Referência de coluna ​

Toda coluna, em columns, filters, orderBy e columnFromString, é um texto com o nome e um sufixo. A Aplicação resolve o nome para a coluna do Tenant onde o Widget está rodando. Maiúscula e minúscula não fazem diferença.

FormaExemploResolve para
Coluna pelo nome no banco'[Vendas]."VALOR":SUM'A coluna VALOR da tabela Vendas da Aplicação
Coluna pelo rótulo'[Vendas].[Valor Líquido]:SUM'A coluna cujo rótulo é "Valor Líquido"
Expressão da Aplicação'["Receita YTD"]'A expressão da aba Expressões. Sem prefixo de tabela

Vendas é o nome da tabela na Aplicação. A expressão da Aplicação usa o próprio cálculo: o sufixo, se houver, é trocado por EXPR.

Nunca ID numérico no código

'7905:SUM' e 'X141197:EXPR' funcionam no Tenant onde o código foi escrito e não leem nada em outro. A instalação de um template troca os IDs da configuração do Widget, como Filtros Pré Aplicados e Campos para Relatório, mas não mexe no texto do código; clonar e publicar geram IDs novos para as expressões. Com o nome, o mesmo código roda em qualquer Tenant que tenha a tabela.

Coluna que não existe na Aplicação faz columnFromString devolver null (e logar "Tentativa de buscar coluna inexistente" no console) e aparece como null em Columns da resposta.

Sufixos ​

GrupoSufixos
AgregaçãoSUM, AVG, MIN, MAX, COUNT, DISTINCT (aceita COUNTDISTINCT, DCOUNT, COUNT_DISTINCT), PCT (PERCENTAGE), PCTA
AgrupamentoAGP
DataDATE, DATETIME, HOUR, HOURDAY, YEARMONTH, MONTHDAY, DAY, MONTH, YEAR, WEEKDAY, YEARWEEK

columnFromString sem sufixo assume AGP. Em consulta, escreva sempre o sufixo da coluna física: '[Vendas]."VENDEDOR"' sem :AGP é um erro comum.

fetchMatrix ​

javascript
const res = await app.fetchMatrix({
  columns: ['[Vendas]."VENDEDOR":AGP', '[Vendas]."VALOR":SUM'],
  filters: [{ column: '[Vendas]."STATUS":AGP', operator: '=', value: 'FATURADO' }],
  orderBy: ['[Vendas]."VALOR":SUM:DESC'],
  limit: 10,
});
// res.Result  → [["Ana", 1540.5], ["Bruno", 980]]
// res.Columns → [{ column_id, expression, label, sqlCode, column: { data_type, ... } }, ...]
ParâmetroFormatoPadrão
columnsLista de textos de coluna ou de objetos de columnFromStringobrigatório
filtersLista de { column, operator, value }. column em texto, com sufixo, ou objeto[]
orderByLista de '<coluna>:ASC' / '<coluna>:DESC', ou { column, direction }sem ordem
limitNúmero de linhas500
options{ limit, bringNulls, isTotalizer }. bringNulls: true traz as linhas com dimensão vazia. isTotalizer: true devolve uma linha só, com as medidas recalculadas sobre o agrupamento (o rodapé de uma tabela){ limit: 500 }
ignoreCrossfiltersLista de column_id: os filtros do Dashboard nessas colunas ficam fora desta consulta. Veja Ignorar um filtro numa consulta[]

Operadores de filtro: =, !=, >, <, >=, <=, between (valor 'início,fim'), like, not like e relative (períodos relativos do filtro de data).

Campo da respostaO que é
ResultAs linhas. Em fetchMatrix, Result[linha][coluna], na ordem de columns
ColumnsUm objeto de coluna por item de columns, na mesma ordem, para usar em formatValue e addFilter. null para coluna que não foi encontrada
columnKeysO código de cada coluna na resposta do servidor (sqlCode)
WarningsAvisos do servidor. Lista vazia quando não há
SQLA consulta que rodou, para depurar

Ignorar um filtro numa consulta ​

ignoreCrossfilters compara com === o column_id de cada item da lista com o do filtro do Dashboard. O column_id que columnFromString devolve vem do cadastro como número para coluna de tabela (7905) e é texto para expressão (X141197); um filtro que voltou de um link ou de um favorito traz o número como texto ('7905'), e o === não os iguala. Passe as duas formas e a consulta ignora o filtro de qualquer origem:

javascript
const id = app.columnFromString('[Vendas]."VENDEDOR":AGP').column_id;
const res = await app.fetchMatrix({
  columns: ['[Vendas]."VALOR":SUM'],
  ignoreCrossfilters: [id, String(id)],
});

Tire o column_id de columnFromString na hora, nunca escrito à mão. Para ignorar só o filtro de período, há também o filtro com value: false, em Exemplos.

Quais filtros a consulta leva ​

A consulta junta três fontes, nesta ordem de força:

  1. Os Filtros Pré Aplicados do Widget (no paramétrico, os do configurador). Valem sempre, e o código não os desliga.
  2. Os filters que o código passa.
  3. Os filtros do Dashboard: barra, aba ativa e filtros cruzados.

Na mesma coluna (mesmo column_id, qualquer sufixo), a fonte mais forte substitui a mais fraca. Um filtro em filters na coluna de data troca o período do Dashboard só nesta consulta. Filtro com value: false sai da consulta.

Erros ​

fetchMatrix e fetchData lançam exceção quando o servidor recusa a consulta (coluna sem acesso, expressão com erro, tempo esgotado). O objeto lançado é o erro do servidor ou { message, sql }. Consulta sem Aplicação carregada devolve Result vazio, sem lançar.

javascript
try {
  const res = await app.fetchMatrix({ columns: [...] });
  if (!res.Result.length) { vazio = true; return; }
  linhas = res.Result;
} catch (e) {
  erro = e?.message ?? String(e);
}

fetchData ​

Mesmos parâmetros e mesmos filtros de fetchMatrix. Cada linha de Result é um objeto, com duas chaves por coluna: o columnKeys correspondente e o texto pedido em columns com tudo que não é letra, número ou _ trocado por _, em maiúsculas.

javascript
const res = await app.fetchData({ columns: ['[Vendas]."VENDEDOR":AGP', '[Vendas]."VALOR":SUM'] });
res.Result[0].VENDAS_VENDEDOR_AGP; // "Ana"
res.Result[0].VENDAS_VALOR_SUM;    // 1540.5

Prefira fetchMatrix: a posição não depende de como o nome vira chave.

Filtros ​

javascript
const col = app.columnFromString('[Vendas]."VENDEDOR":AGP');

app.addFilter(col, '=', 'Ana');            // filtra o Dashboard
app.addFilter(col, '=', ['Ana', 'Bruno']); // mais de um valor
app.addFilter(col, '=', false);            // tira o filtro desta coluna

const atual = app.getFilter(col);          // { column, operator, value, comparativeValues } ou undefined
RegraEfeito
Valor false, null, 0, '' ou lista vaziaRemove o filtro da coluna. Para filtrar pelo número zero, passe '0'
Coluna fora dos filtros permitidos no DashboardaddFilter não faz nada, sem aviso
encode não informadoNo app, vira true para valor único: , | ; : & + dentro do valor são escapados. Lista sempre é escapada
Já existe filtro na mesma coluna e sufixoÉ substituído
YEAR ou YEARMONTH com = numa coluna que já tem filtro DATEVira between sobre a data, do primeiro ao último dia do ano ou do mês
Filtro obrigatório declarado no DashboardremoveFilter não remove; filtro de valor único fica com um valor só

Todo addFilter recarrega o Dashboard, e isso inclui o Widget que filtrou: ele é montado de novo. Para mostrar qual item está selecionado, leia app.getFilter(col) ao montar, em vez de guardar a seleção numa variável.

getFilter(col).value vem como foi guardado, escapado. Compare com getFilter(col)?.value === valor só quando o valor não tem os caracteres acima.

Período do filtro de data ​

javascript
import { format } from 'date-fns';

const data = app.columnFromString('[Vendas]."DATA":DATE');
const { start, end } = app.getDateFilterReference(data.column_id);

const res = await app.fetchMatrix({
  columns: ['[Vendas]."VALOR":SUM'],
  filters: [{ column: '[Vendas]."DATA":DATE', operator: 'between',
              value: `${format(start, 'yyyy-MM-dd')},${format(end, 'yyyy-MM-dd')}` }],
});

O primeiro argumento é o column_id, e o filtro precisa estar na mesma coluna com sufixo DATE. Sem filtro, start e end são o segundo argumento, ou a data de agora. Com período relativo ("últimos 30 dias"), a resposta traz também relativeToken.

Link para o Explorer ​

javascript
const url = app.getReportUrl({
  columns: [app.columnFromString('[Vendas]."CLIENTE":AGP'), app.columnFromString('[Vendas]."VALOR":SUM')],
  orderBy: [{ column: app.columnFromString('[Vendas]."VALOR":SUM'), direction: 'DESC' }],
  filters: [{ column: app.columnFromString('[Vendas]."VENDEDOR":AGP'), operator: '=', value: nome }],
});
window.location.href = url;
ChaveO que é
columnsObjetos de coluna, na ordem do relatório
orderBy{ column, direction }
filters{ column, operator, value }, com column como objeto
matrixAxisObjeto de coluna para o eixo da matriz

A função só devolve o texto /app/report/<id da Aplicação>?…; nada acontece até o código navegar. Os filtros do Dashboard entram por baixo, e os seus vencem na mesma coluna com o mesmo sufixo.

Usuário ​

app.user é { id, nome } de quem está vendo, e não há outros campos (nem e-mail, nem grupo). É null em link público, que abre sem sessão. Na exportação de imagem e PDF, é quem disparou a exportação: um alerta enviado para trinta pessoas mostra o nome do dono do alerta.

javascript
const saudacao = app.user?.nome ? `Bom dia, ${app.user.nome}` : 'Bom dia';

Não use app.user para esconder dado. A permissão de linha e coluna é aplicada no servidor, em toda consulta; o que o código esconde no navegador, quem abre o Dashboard consegue ler.

Tema ​

CampoValores
$app.theme'light' ou 'dark'
$app.moldura.ativatrue só dentro do aplicativo móvel do Lumo
$app.moldura.cores{ fundo, cartao, rebaixado, linha, texto, textoSecundario, acao }, em hex, com as cores da marca do aplicativo

Os dois mudam com a página aberta (troca de tema, cor nova vinda do aplicativo). Use com $ dentro de $: para o Widget acompanhar. Como aplicar as cores está em Boas práticas.

JWT ​

app.generateJWTToken() devolve { token, payload }, com payload = { userId, tenantId, data } (data em ISO, UTC). O token é HS256, assinado no servidor com a chave do Tenant, e o código do Widget nunca vê a chave.

Para ligar, um administrador marca Habilitar Gerador de JWT e preenche Chave JWT (até 128 caracteres) no HEC (Horus Enterprise Control), em Tenants › Avançado › Segurança. Sem a opção ligada ou sem a chave, o servidor recusa com "JWT Key não configurada para o tenant.", a mensagem vai para o console do navegador e a função devolve null.

javascript
const jwt = await app.generateJWTToken();
if (!jwt?.token) return;
await fetch('https://api.exemplo.com.br/pedidos/aprovar', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${jwt.token}` },
  body: JSON.stringify({ pedidoId }),
});

A sua API valida o token com a mesma chave:

python
import jwt
from datetime import datetime, timedelta, timezone

payload = jwt.decode(token, CHAVE_JWT, algorithms=['HS256'])
emitido = datetime.fromisoformat(payload['data'].replace('Z', '+00:00'))
if datetime.now(timezone.utc) - emitido > timedelta(minutes=5):
    raise PermissionError('token velho')

O token não tem exp. Confira o campo data na sua API, como acima, para recusar token antigo.

Relacionado ​