Desenvolvendo widgets do Fluig localmente com o Live Server
Como construir a interface de uma widget no navegador, com o Live Server, sem exportar para o Fluig a cada ajuste. As integrações e regras continuam no servidor.
Para ver qualquer ajuste numa widget, normalmente é preciso exportar pelo Fluig Studio (o Eclipse com o plugin do Fluig), esperar a publicação e recarregar a página no servidor. Quando você está montando a interface, esse ciclo se repete dezenas de vezes por dia.
Uma forma de encurtar isso é desenvolver a interface localmente, com o Live Server recarregando o navegador a cada alteração, sem mexer na estrutura da widget.
A ideia
O objetivo aqui é construir a interface da widget: layout, estilos, componentes, estados da tela e interações. As integrações e as regras que dependem do Fluig, como datasets reais, serviços, usuário logado e permissões, continuam sendo desenvolvidas e testadas no servidor.
Na prática:
- O
view.ftle o JavaScript da widget continuam onde estão. Nada muda na estrutura do projeto. - Na raiz do projeto, um
index.htmlserve como página de testes. O HTML doview.ftlé copiado para ele, e você vai ajustando conforme precisa. - O JavaScript não é recriado: o
index.htmlcarrega o próprio arquivo da widget. Na hora de exportar, não há nada para trocar. - O que só existe dentro do Fluig, como o
SuperWidget, oDatasetFactory, oWCMAPIe oFLUIGC, é simulado em dois arquivos que ficam fora da widget.
Estrutura do projeto
meu-projeto/
├── datasets/ ← pastas padrão do projeto no Fluig Studio
├── events/
├── forms/
├── mechanisms/
├── reports/
├── workflow/
├── wcm/widget/listaUsuarios/
│ └── src/main/
│ ├── resources/view.ftl
│ └── webapp/resources/
│ ├── css/listaUsuarios.css
│ └── js/listaUsuarios.js
├── index.html ← página de testes local
├── preview.mock.js ← dados fictícios dos datasets
└── preview.local.js ← simula o que o Fluig fornece
O index.html, o preview.mock.js e o preview.local.js só existem no ambiente local. Eles não entram na exportação.
A widget
O JavaScript é o mesmo que vai para o Fluig. Neste exemplo, a widget busca no dataset interno colleague os usuários ativos, monta uma tabela e tem um botão para atualizar a lista:
var MyWidget = SuperWidget.extend({
bindings: {
local: {
'atualizar': ['click_atualizar']
}
},
init: function () {
this.carregar();
},
atualizar: function () {
this.carregar();
FLUIGC.toast({ title: '', message: 'Lista atualizada.', type: 'success' });
},
carregar: function () {
var ativos = DatasetFactory.createConstraint('active', 'true', 'true', ConstraintType.MUST);
var ds = DatasetFactory.getDataset('colleague', null, [ativos], null);
var html = ds.values.map(function (usuario) {
return '<tr>' +
'<td>' + usuario.colleagueName + '</td>' +
'<td>' + usuario.login + '</td>' +
'<td>' + usuario.mail + '</td>' +
'<td>' + (usuario.currentProject || '—') + '</td>' +
'</tr>';
}).join('');
$('#tabelaUsuarios_' + this.instanceId + ' tbody').html(html);
}
});
O index.html
O index.html tem o HTML copiado do view.ftl e carrega os arquivos nesta ordem: jQuery, dados fictícios, simulação do Fluig e, por último, o JavaScript real da widget.
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>listaUsuarios · local</title>
<!-- Style Guide do Fluig, ou o design system da sua equipe -->
<link rel="stylesheet" href="[CSS DO STYLE GUIDE]">
<link rel="stylesheet" href="./wcm/widget/listaUsuarios/src/main/webapp/resources/css/listaUsuarios.css">
</head>
<body>
<!-- =========================================================
SUA WIDGET: É AQUI QUE VOCÊ DESENVOLVE
Substitua este bloco pelo HTML do view.ftl da sua widget
e troque o ${instanceId} por "local".
Quando terminar, leve as mudanças de volta para o view.ftl.
========================================================= -->
<div id="listaUsuarios_local" class="super-widget wcm-widget-class fluig-style-guide"
data-params="MyWidget.instance()">
<div class="painel">
<div class="painel-topo">
<h2>Usuários ativos</h2>
<button class="btn btn-default" data-atualizar>Atualizar</button>
</div>
<table class="table table-striped" id="tabelaUsuarios_local">
<thead>
<tr><th>Nome</th><th>Login</th><th>E-mail</th><th>Matrícula</th></tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<!-- ========================================= FIM DA SUA WIDGET -->
<!-- Ambiente local: não vai para o Fluig -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="./preview.mock.js"></script>
<script src="./preview.local.js"></script>
<!-- JS real da widget -->
<script src="./wcm/widget/listaUsuarios/src/main/webapp/resources/js/listaUsuarios.js"></script>
</body>
</html>
Os dados fictícios: preview.mock.js
Os datasets não funcionam fora do Fluig. No lugar deles, o preview.mock.js guarda dados fictícios no mesmo formato que o getDataset retorna. Aqui estão todas as colunas do colleague, com uma função que monta cada linha:
window.MOCK = {
colleague: {
columns: [
'colleaguePK.companyId', 'colleaguePK.colleagueId', 'colleagueName', 'mail', 'login',
'active', 'adminUser', 'emailHtml', 'defaultLanguage', 'volumeId', 'maxPrivateSize',
'currentProject', 'extensionNr', 'especializationArea', 'groupId', 'userTenantId'
],
values: [
usuario('admin', 'Admin Fluig', 'admin@exemplo.com.br', 'admin', 'true', 'true', '', 3),
usuario('204518', 'Lucas Lima.dev', 'lucaslima@exemplo.com.br', 'lucas.lima', 'true', 'false', '204518', 4),
usuario('317642', 'Bruno Henrique Lima', 'bruno.lima@exemplo.com.br', 'bruno.lima', 'true', 'false', '317642', 5),
usuario('carladias', 'Carla Dias', 'carla.dias@exemplo.com.br', 'carla.dias', 'true', 'false', '', 6),
usuario('diegoalves', 'Diego Alves Ribeiro', 'diego.alves@exemplo.com.br', 'diego.alves', 'true', 'false', '128403', 7),
usuario('elisarocha', 'Elisa Rocha', 'elisa.rocha@exemplo.com.br', 'elisa.rocha', 'false', 'false', '', 8),
usuario('128404', 'Fábio Nunes de Castro', 'fabio.nunes@exemplo.com.br', 'fabio.nunes', 'true', 'false', '128404', 9),
usuario('135790', 'Gabriela Melo', 'gabriela.melo@exemplo.com.br', 'gabriela.melo', 'true', 'false', '135790', 10)
]
}
};
// Monta uma linha do dataset colleague com todas as colunas
function usuario(id, nome, email, login, ativo, admin, matricula, tenant) {
return {
'colleaguePK.companyId': '1',
'colleaguePK.colleagueId': id,
colleagueName: nome,
mail: email,
login: login,
active: ativo,
adminUser: admin,
emailHtml: 'true',
defaultLanguage: 'pt_BR',
volumeId: '',
maxPrivateSize: '',
currentProject: matricula,
extensionNr: '',
especializationArea: '',
groupId: '',
userTenantId: String(tenant)
};
}
Para descobrir as colunas e o formato, abra o dataset no Painel de controle > Datasets ou rode o getDataset no console do navegador, dentro do Fluig. Assim os dados fictícios ficam iguais aos reais.
O que o Fluig fornece: preview.local.js
Este arquivo simula, no navegador, o que a widget espera encontrar no Fluig. Ele é carregado depois do jQuery e do preview.mock.js, e antes do JavaScript da widget.
SuperWidget
No Fluig, o SuperWidget.extend cria a widget e a plataforma cuida do resto. A versão local faz o mínimo: cria a instância com o instanceId, liga os bindings.local e chama o init.
window.SuperWidget = {
extend: function (proto) {
function Widget(props) {
$.extend(this, props);
}
$.extend(Widget.prototype, proto);
Widget.instance = function (props) {
var widget = new Widget(props);
var locais = (proto.bindings && proto.bindings.local) || {};
// data-atualizar + 'click_atualizar' → clique chama widget.atualizar()
Object.keys(locais).forEach(function (acao) {
locais[acao].forEach(function (binding) {
var partes = binding.split('_');
var evento = partes[0];
var metodo = partes.slice(1).join('_');
$(document).on(evento, '[data-' + acao + ']', function (event) {
widget[metodo](this, event);
});
});
});
widget.init();
return widget;
};
return Widget;
}
};
ConstraintType, WCMAPI e FLUIGC
Só o que a widget usa. Aqui, o usuário é um valor fixo e o toast vira uma mensagem no console.
window.ConstraintType = { MUST: 'MUST', SHOULD: 'SHOULD', MUST_NOT: 'MUST_NOT' };
window.WCMAPI = {
getUser: function () { return 'usuario.teste'; }
};
window.FLUIGC = {
toast: function (opts) {
console.log('[FLUIGC.toast:' + opts.type + ']', opts.message);
}
};
DatasetFactory
O DatasetFactory local devolve os dados do preview.mock.js. Assim a widget chama o getDataset exatamente como faz no Fluig.
window.DatasetFactory = {
createConstraint: function (campo, inicial, final, tipo) {
return { campo: campo, inicial: inicial, final: final, tipo: tipo };
},
getDataset: function (nome, campos, constraints) {
var ds = window.MOCK[nome];
if (!ds) {
console.warn('Sem dados fictícios para o dataset ' + nome);
return { columns: [], values: [] };
}
// Aplica só filtros MUST por igualdade, o suficiente para testar a interface
var values = ds.values.filter(function (linha) {
return (constraints || []).every(function (c) {
return c.tipo !== ConstraintType.MUST || !(c.campo in linha) || linha[c.campo] == c.inicial;
});
});
return { columns: ds.columns, values: values };
}
};
Se a widget usa constraints para pedir coisas diferentes ao mesmo dataset, dá para olhar a constraint dentro do getDataset e devolver os dados fictícios de cada caso. Chamadas a APIs externas funcionam normalmente no ambiente local.
Inicialização
No Fluig, quem cria a widget é o data-params="MyWidget.instance()". Localmente, a widget é criada à mão quando a página termina de carregar, com o mesmo instanceId usado no HTML:
$(function () {
window.widgetLocal = MyWidget.instance({ instanceId: 'local' });
});
Os estilos base do Fluig
Fora do Fluig, o navegador usa os estilos padrão dele: outra fonte, outros tamanhos, outras margens. Aí a widget fica de um jeito no ambiente local e de outro quando chega no servidor.
Para evitar isso, o <head> do index.html replica os estilos base que o Fluig aplica na página:
body {
display: block;
float: left;
position: relative;
height: 100%;
width: 100%;
padding: 100px 25px 0 90px; /* espaço do topo e do menu lateral */
box-sizing: border-box;
margin: 0;
direction: ltr;
font-family: "Open Sans", Arial, sans-serif;
font-size: 14px;
line-height: 1em;
text-align: left;
}
p { line-height: 22px; }
h2 { font-size: 32px; }
h1, h2, h3 { line-height: normal; }
body, td, th {
font-size: 12px;
color: #000;
}
O mesmo <head> também traz uma réplica do topo e do menu lateral do Fluig, com os ícones trocados pelos do Material Symbols. Assim você vê a widget no espaço real que ela vai ocupar. O CSS completo, com o topo, o menu e os ícones, está no projeto base.
Rodando com o Live Server
- Instale a extensão Live Server no VS Code.
- Abra o
index.htmle clique em Go Live, no canto inferior direito. - O navegador abre a página e recarrega sozinho sempre que você salva o HTML, o CSS ou o JavaScript da widget.
Do local para o Fluig
Como o JavaScript e o CSS são os mesmos arquivos da widget, basta levar para o view.ftl as mudanças que você fez no HTML e exportar a widget pelo Eclipse (Fluig Studio). O index.html e os arquivos preview.*.js ficam só no seu computador.
O que fica para o Fluig
O ambiente local resolve a interface. Tudo o que depende da plataforma continua sendo desenvolvido e testado no servidor:
- As consultas reais aos datasets e os datasets customizados.
- Integrações com serviços e com o ERP.
- Dados do usuário logado e da página.
- Permissões, regras de negócio e o comportamento dentro do portal.
