Pular para o conteúdo
Lucas LimaDesenvolvedor Fluig
Todos os guias

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.

Live Server
Ao salvar o arquivo no VS Code, o navegador recarrega sozinho com a mudança.
Ao salvar o arquivo no VS Code, o navegador recarrega sozinho com a mudança.

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.ftl e o JavaScript da widget continuam onde estão. Nada muda na estrutura do projeto.
  • Na raiz do projeto, um index.html serve como página de testes. O HTML do view.ftl é copiado para ele, e você vai ajustando conforme precisa.
  • O JavaScript não é recriado: o index.html carrega 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, o DatasetFactory, o WCMAPI e o FLUIGC, é 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' });
});
Widget rodando localmente
A widget aberta pelo Live Server, com os usuários fictícios do colleague e a réplica do topo e do menu do Fluig.
A widget aberta pelo Live Server, com os usuários fictícios do colleague e a réplica do topo e do menu do Fluig.

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

  1. Instale a extensão Live Server no VS Code.
  2. Abra o index.html e clique em Go Live, no canto inferior direito.
  3. 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.

Tem um processo no Fluig para resolver?

Conte o cenário. Respondo em até um dia útil.

Falar comigo Ver cases