Uma pergunta que ouvimos de quem está avaliando o sgcHTML: ele traz muitos componentes prontos, mas e se eu já tiver uma biblioteca JavaScript que gosto de usar? Talvez você prefira gráficos do D3.js ou do Chart.js, ou já tenha pago por um Grid do DevExtreme e queira usá-lo. A resposta curta é que o sgcHTML foi criado para ser aberto. Ele gera páginas completas no servidor a partir de código Delphi, C++Builder ou .NET, e qualquer biblioteca que rode no navegador se encaixa com três pequenos hooks. Se você recorre à mesma biblioteca com frequência, pode encapsulá-la em um componente reutilizável para que o restante da sua aplicação nunca precise lidar com o JavaScript.
Os três hooks
Todo widget de terceiros precisa das mesmas três coisas na página: a biblioteca precisa ser carregada, precisa existir um elemento para ela renderizar dentro, e uma linha de código de inicialização precisa rodar assim que o DOM estiver pronto. O componente TsgcHTMLComponent_Site oferece uma propriedade ou método para cada uma delas.
CustomHeadinjeta marcação em<head>, o lugar natural para um<script>ou<link>que carrega a biblioteca.AddContentemite o elemento de destino no corpo da página.BodyEndHTMLexecuta seu código de inicialização logo antes de</body>, depois que a biblioteca foi carregada.
Aqui está um exemplo completo com D3.js. Carregue a biblioteca, insira um container e deixe o D3 desenhar dentro dele.
uses
sgcHTML_Component_Site;
var
oSite: TsgcHTMLComponent_Site;
begin
oSite := TsgcHTMLComponent_Site.Create(nil);
try
oSite.Title := 'Sales Dashboard';
// 1) Load the library (a public CDN, or a file you serve yourself)
oSite.CustomHead := '<script src="https://d3js.org/d3.v7.min.js"></script>';
// 2) Emit the element your library renders into
oSite.AddContent('<div id="chart"></div>');
// 3) Run your init code at the end of the body
oSite.BodyEndHTML :=
'<script>' +
' const data = [4, 8, 15, 16, 23, 42];' +
' d3.select("#chart").selectAll("div").data(data).enter()' +
' .append("div").attr("class", "bar")' +
' .style("width", d => (d * 12) + "px").text(d => d);' +
'</script>';
WebModule.Response := oSite.HTML;
finally
oSite.Free;
end;
end;
Esse é todo o padrão. Troque a URL e o código de inicialização, e as mesmas três linhas funcionam para Chart.js, um widget do DevExtreme, um calendário, um mapa ou qualquer outra coisa.
CDN ou hospedagem própria
O exemplo acima aponta para um CDN público, que é a forma mais rápida de testar algo. Para uma aplicação de intranet, uma implantação offline, ou quando você quer fixar uma versão exata, sirva o arquivo da biblioteca você mesmo a partir do servidor sgc e referencie-o com um caminho local. Vários componentes internos já fazem isso: o componente Chart embutido carrega o Chart.js a partir de um /chart.umd.min.js local em vez de um CDN, então a página não tem nenhuma dependência externa.
// The .js and .css files sit next to your server executable and are
// served as static files, so the page references them by local path:
oSite.CustomHead :=
'<link rel="stylesheet" href="/assets/mylib.min.css">' +
'<script src="/assets/mylib.min.js"></script>';
Se preferir montar o head como nodes em vez de uma string bruta, a camada de templates expõe as listas HeadNodes e BodyEndNodes com os helpers AddScript e AddStylesheet, além dos nodes TsgcHTMLScript (com Src, Code, Defer, Async) e TsgcHTMLStylesheet. Strings brutas e nodes tipados são intercambiáveis, use o que ficar mais legível no seu código.
Chart.js em poucas linhas
Chart.js é um pedido comum, então aqui está ele usando os mesmos três hooks. Carregue a biblioteca, emita um <canvas> e depois construa o gráfico.
oSite.CustomHead := '<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>';
oSite.AddContent('<canvas id="revenue" width="600" height="300"></canvas>');
oSite.BodyEndHTML :=
'<script>' +
' new Chart(document.getElementById("revenue"), {' +
' type: "bar",' +
' data: {' +
' labels: ["Q1", "Q2", "Q3", "Q4"],' +
' datasets: [{ label: "Revenue", data: [12, 19, 7, 15] }]' +
' }' +
' });' +
'</script>';
Se você só quer gráficos e não precisa de uma biblioteca específica, note que o sgcHTML já traz um componente TsgcHTMLComponent_Chart que é o Chart.js pronto para uso, com uma propriedade CustomOptions que repassa diretamente qualquer configuração bruta do Chart.js. Esta seção é para quando você quer controlar a biblioteca por conta própria.
Um Grid do DevExtreme
DevExtreme é um bom exemplo de um pacote comercial mais pesado. O DataGrid precisa que sua folha de estilos e seu bundle sejam carregados, de um elemento para ser montado e de um objeto de configuração. A abordagem não muda em nada.
oSite.CustomHead :=
'<link rel="stylesheet" href="https://cdn3.devexpress.com/jslib/23.2.3/css/dx.light.css">' +
'<script src="https://cdn3.devexpress.com/jslib/23.2.3/js/dx.all.js"></script>';
oSite.AddContent('<div id="grid"></div>');
oSite.BodyEndHTML :=
'<script>' +
' new DevExpress.ui.dxDataGrid(document.getElementById("grid"), {' +
' dataSource: [' +
' { id: 1, name: "Widget", price: 9.90 },' +
' { id: 2, name: "Gadget", price: 14.50 }' +
' ],' +
' columns: ["id", "name", "price"],' +
' showBorders: true' +
' });' +
'</script>';
A parte interessante é de onde vem o dataSource. Você pode serializar um conjunto de dados em JSON no servidor e incluí-lo diretamente, como mostrado, ou apontar o grid para um endpoint que o seu servidor sgc expõe e deixar que ele carregue os dados ao vivo. Há um exemplo pronto de Grid do DevExtreme na pasta de demos que alimenta um grid a partir de um servidor sgc WebSocket, de modo que os dados são atualizados em tempo real sem recarregar a página.
Encapsule em um componente reutilizável
Passar strings brutas de um lado para outro é aceitável para algo pontual. Quando você perceber que está usando a mesma biblioteca página após página, promova-a a um componente: crie uma subclasse de TsgcHTMLComponent e sobrescreva GetHTML para retornar sua própria marcação junto com a integração da biblioteca. No restante da sua aplicação, basta então definir uma propriedade e chamar HTML, exatamente como em um componente embutido.
uses
sgcHTML_Component;
type
TsgcHTMLComponent_D3Bars = class(TsgcHTMLComponent)
private
FData: string; // a JSON array, e.g. '[4,8,15,16,23,42]'
published
property Data: string read FData write FData;
public
function GetHTML: string; override;
end;
function TsgcHTMLComponent_D3Bars.GetHTML: string;
begin
Result :=
'<div id="d3bars"></div>' +
'<script src="https://d3js.org/d3.v7.min.js"></script>' +
'<script>' +
' const data = ' + FData + ';' +
' d3.select("#d3bars").selectAll("span").data(data).enter()' +
' .append("span").style("padding-right", d => d + "px").text(d => d);' +
'</script>';
end;
Isso não é um caso especial, é assim que vários dos componentes já incluídos são construídos. O componente Map encapsula o Leaflet, o RichEditor encapsula o Quill, o KanbanBoard encapsula o SortableJS, e o PDFViewer encapsula o pdf.js, cada um deles carregando sua biblioteca e emitindo seu código de inicialização exatamente através destes hooks. Seu próprio wrapper fica ao lado deles como um componente de primeira classe, e você pode adicionar um GetCSS correspondente se a biblioteca precisar de estilos, que o sgcHTML coleta e remove duplicidades em toda a página automaticamente.
Experimente
A conclusão é que o sgcHTML nunca limita você. Os componentes embutidos cobrem muito terreno, e quando você precisa de algo específico, D3.js, Chart.js, um Grid ou Chart do DevExtreme, qualquer biblioteca de navegador, bastam três hooks para incorporar e uma pequena subclasse para torná-la reutilizável.
Dúvidas ou sugestões? Fale conosco, você receberá uma resposta de quem escreveu o código.
