Become a member!

Construindo Aplicações Web Delphi em Minutos com DMVCFramework e TemplatePro

🌐
Este artigo também está disponível em outros idiomas:
🇬🇧 English  •  🇮🇹 Italiano  •  🇪🇸 Español

No acelerado mundo do desenvolvimento web, velocidade e eficiência não são apenas vantagens—são necessidades. Embora a indústria frequentemente gravite em direção a frameworks JavaScript complexos e cadeias de ferramentas intrincadas, o DMVCFramework se destaca como o projeto Delphi mais popular no GitHub por uma razão convincente: ele entrega aplicações web de nível empresarial com simplicidade e velocidade sem precedentes.

Combinado com o poderoso motor de templates do TemplatePro e aprimorado por ferramentas modernas como WebStencils, este ecossistema representa uma mudança de paradigma em como aplicações web podem ser construídas. Vamos explorar como essas tecnologias permitem que desenvolvedores criem aplicações web sofisticadas em minutos, não em meses.

O Trio Poderoso: DMVCFramework, TemplatePro e WebStencils

DMVCFramework: A Base do Desenvolvimento Web Rápido

DMVCFramework é um framework popular e poderoso para WEB API em Delphi que suporta o desenvolvimento de APIs RESTful e JSON-RPC. O que o diferencia é sua notável capacidade de transformar tarefas complexas de desenvolvimento web em operações simples e intuitivas.

Aqui está o que torna o DMVCFramework excepcional:

Estabilidade Pronta para Produção: Usado por projetos pequenos/médios/grandes desde 2010, o DMVCFramework provou seu valor em ambientes de alto tráfego. Algumas das APIs WEB Delphi (RESTful ou JSONRPC) com maior tráfego são alimentadas pelo DMVCFramework.

Desenvolvimento Instantâneo: Você pode criar um servidor RESTful completo em alguns cliques. O assistente integrado torna a criação de projetos quase instantânea.

Implantação Flexível: Seja um servidor standalone, módulo Apache ou extensão ISAPI, o DMVCFramework se adapta às suas necessidades de infraestrutura.

Ecossistema Rico: Com mais de 100 exemplos e documentação abrangente, os desenvolvedores podem se tornar produtivos imediatamente.

TemplatePro: Templates Modernos Simplificados

TemplatePro é um motor de templates moderno e versátil projetado para simplificar a geração dinâmica de HTML, conteúdo de email e arquivos de texto. Com sintaxe inspirada em sistemas populares como Jinja e Smarty, ele fornece recursos poderosos incluindo blocos condicionais, loops, herança de templates e suporte a dados JSON.

Principais Recursos (atualizado para a versão 0.9.0):

  • Sintaxe Intuitiva: Usa a notação familiar {{}} para expressões e lógica
  • Expressões Poderosas: Suporte completo para operações aritméticas e lógicas com @(...), incluindo funções matemáticas integradas (sqrt, abs, round, min, max) e funções de string (length, upper, trim, left, right)
  • Herança de Templates: Construa layouts e componentes reutilizáveis
  • Filtros Personalizados: Estenda a funcionalidade com transformações de dados específicas do domínio
  • Suporte Nativo TDataSet: Itere sobre registros e campos de datasets com pseudo-variáveis (@@index, @@first, @@last, @@odd, @@even)
  • Include com Mapeamento de Variáveis: Passe variáveis específicas para templates incluídos
  • Integração JSON: Manipule dados de API e objetos complexos de forma transparente
  • Performance: Templates compilados garantem execução rápida, com suporte a cache em disco (.tpc)

Documentação Oficial do TemplatePro está disponível aqui, incluindo a seção Migration Notes para quem está atualizando de versões anteriores.

WebStencils: O Motor de Templates do RAD Studio

WebStencils é o motor de templates nativo do RAD Studio 12.2 que fornece capacidades de script do lado do servidor para estender aplicações WebBroker e RAD Server. WebStencils representa o reconhecimento da Embarcadero da importância de motores de templates modernos no desenvolvimento web.

Ambos os motores suportam lógica condicional, loops, herança de templates e mecanismos de inclusão. No entanto, TemplatePro fornece alguns recursos únicos como filtros personalizados, manipulação nativa de JSON e compilação opcional. O objetivo principal do WebStencils é estender significativamente as tecnologias web existentes no RAD Studio, transformando qualquer motor de Web Service em uma ferramenta abrangente para Web Site e Web Service.

Se você está interessado no motor WebStencils, recomendo fortemente o Guia Gratuito WebStencils and HTMX: Free Guide to Fast Web Development do meu amigo Antonio Zapater.

Início Rápido: Do Download à Aplicação Web Funcionando em 2 Minutos

A beleza do DMVCFramework e TemplatePro está em sua usabilidade imediata. Esqueça procedimentos de configuração complexos—você pode ter uma aplicação web totalmente funcional rodando em menos de 2 minutos.

Passo 1: Baixar e Executar

Simplesmente baixe o projeto quickstart e você está pronto para começar:

  • Baixe o arquivo zip QuickStart
  • Descompacte-o em uma pasta de sua preferência
  • Abra o projeto dmvcframework_templatepro_htmx_quickstart.dproj no Delphi e pressione F9
  • É isso! Seu servidor web está rodando em localhost:8080

A partir de agora você pode renomear arquivos e adicionar as funcionalidades que seu projeto requer.

O projeto quickstart inclui uma aplicação web completa com:

  • Servidor DMVCFramework pré-configurado pronto para rodar
  • Templates TemplatePro já configurados
  • Dados de exemplo e controllers demonstrando melhores práticas
  • Interface web responsiva usando Bootstrap
  • Exemplos funcionais de todos os principais recursos

Passo 2: Explorar o Que Já Está Construído

Abra o projeto e você encontrará um sistema de gerenciamento de clientes totalmente funcional:

// ControllerU.pas - Already implemented!
unit ControllerU;

interface

uses
  MVCFramework, MVCFramework.Commons, MVCFramework.Serializer.Commons, System.Generics.Collections;

type
  [MVCPath]
  TMyController = class(TMVCController)
  public
    [MVCPath]
    [MVCHTTPMethod([httpGET])]
    [MVCProduces(TMVCMediaType.TEXT_HTML)]
    function Index: String;

    [MVCPath('/reversedstrings')]
    [MVCHTTPMethod([httpGET])]
    [MVCProduces(TMVCMediaType.TEXT_PLAIN)]
    function GetReversedString(const [MVCFromQueryString('value','')] Value: String): String;

  end;

implementation

uses
  System.StrUtils, System.SysUtils, MVCFramework.Logger;


function TMyController.Index: String;
begin
  Result := RenderView('index');
end;

function TMyController.GetReversedString(const Value: String): String;
begin
  ViewData['reversed_string'] := System.StrUtils.ReverseString(Value.Trim);
  Result := RenderView('partials/reversed');
end;

end.

Passo 3: Ver TemplatePro em Ação

O quickstart inclui templates funcionais que você pode examinar e modificar:

templates/index.html (incluído no quickstart):

{{extends "_layout.html"}}

{{block "body"}}

<div class="row">
    <div class="col">
        <div class="text-center mt-5">
            <h1>DMVCFramework + TemplatePro + HTMX</h1>
            <p class="lead">A complete project boilerplate built with Bootstrap</p>
            <p>Bootstrap v5.3.3</p>
        </div>
    </div>
</div>

<div class="row mt-5">
    <div class="col col-3 offset-3">
        <div class="mb-3">
            <input type="text" name="value" class="form-control" id="exampleFormControlInput1"
              placeholder="Write something here" value="Hello World">
        </div>
    </div>
    <div class="col col-2">
        <button type="button" hx-get="/reversedstrings" hx-include='input' hx-target="#result"
          class="btn btn-primary">Reverse it!</button>
    </div>
</div>
<div class="row">
    <div class="col col-5 offset-3">
        <div id="result" class="alert alert-primary" role="alert">
            {{include "partials/reversed.html"}}
        </div>
    </div>
</div>

{{endblock}}

Passo 4: Personalização Instantânea

Quer ver suas mudanças imediatamente? O quickstart foi projetado para feedback instantâneo:

  1. Modifique um template - Edite templates/index.html
  2. Atualize o navegador - Veja as mudanças imediatamente (nenhuma compilação necessária para templates!)
  3. Crie novos endpoints - Siga os padrões de controller existentes

templates/_layout.hmtl (também incluído):

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <meta name="description" content="A simple DMVCFramework + TemplatePro + HTMX Template for new projects.">
    <meta name="author" content="Daniele Teti">
    <title>DMVCFramework TemplatePro HTMX Quick Start</title>
    <!-- Latest compiled and minified CSS -->
    <script src="/assets/js/htmx.min.js"></script>
    <link href="/assets/bootstrap-5.3.3/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
    <link rel="stylesheet" href="/assets/css/style.css"/>
    <link rel="icon" type="image/x-icon" href="/assets/images/favicon.ico">
    <link rel="manifest" href="/assets/site.webmanifest" />
</head>
<body>
        <!-- Responsive navbar-->
        <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
            <div class="container">
                <a class="navbar-brand" href="#">Start Bootstrap</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse"
                  data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent"
                  aria-expanded="false" aria-label="Toggle navigation">
                  <span class="navbar-toggler-icon"></span>
                </button>
                <div class="collapse navbar-collapse" id="navbarSupportedContent">
                    <ul class="navbar-nav ms-auto mb-2 mb-lg-0">
                        <li class="nav-item"><a class="nav-link active" aria-current="page" href="#">Home</a></li>
                        <li class="nav-item"><a class="nav-link" href="#">Link</a></li>
                        <li class="nav-item dropdown">
                            <a class="nav-link dropdown-toggle" id="navbarDropdown" href="#"
                              role="button" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</a>
                            <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
                                <li><a class="dropdown-item" href="#">Action</a></li>
                                <li><a class="dropdown-item" href="#">Another action</a></li>
                                <li><hr class="dropdown-divider" /></li>
                                <li><a class="dropdown-item" href="#">Something else here</a></li>
                            </ul>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
<div class="container">
    {{block "body"}}{{endblock}}
</div>
<!-- Bootstrap core JS-->
<script src="/assets/bootstrap-5.3.3/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
<!-- Core theme JS-->
<script src="/assets/js/scripts.js"></script>
</body>
</html>

Agora é hora de outra foto completamente não relacionada…

Recursos Avançados do TemplatePro em Ação

Trabalhando com Dados JSON

TemplatePro se destaca ao lidar com dados JSON de APIs:

function TMyController.GetCustomers: String;
begin
  //Let's create a dataset containing 15 random people we consider as customers.
  //This dataset is generated by the "Delphi Fake Data Utils" project available here https://github.com/danieleteti/delphi_fake_data_utils
  var lDS := ToFree<TDataSet>(GetPeople(15));
  var lJSON := ToFree<TJSONObject>(TJSONObject.Create);

  //calculate customers avg age
  lDS.First;
  var lAgeCumulative := 0;
  _.ForEachRecord(lDS,
    procedure(const DS: TDataSet)
    begin
      lAgeCumulative := lAgeCumulative + YearsBetween(Now, lDS.FieldByName('dob').AsDateTime);
    end);
  var lAvgAge := lAgeCumulative / lDS.RecordCount;
  lDS.First;
  // end - stats

  lJSON.A['customers'] := lDS.AsJDOJSONArray();
  lJSON.F['avg_age'] := lAvgAge;
  ViewData['data'] := lJSON;
  Result := RenderView('index');
end;

Template de Diretório de Clientes (customers.html):

{{extends "_layout.html"}}

{{block "body"}}

       <h2 class="mb-4">Customers Directory</h2>

        <!-- TemplatePro Template for Dataset Table -->
        {{if data.customers}}
            <div class="table-container">
                <table class="table table-striped table-hover mb-0">
                    <thead>
                        <tr>
                            <th scope="col">#</th>
                            <th scope="col">Code</th>
                            <th scope="col">First Name</th>
                            <th scope="col">Last Name</th>
                            <th scope="col">Country</th>
                            <th scope="col">Date of Birth</th>
                            <th scope="col">Age</th>
                        </tr>
                    </thead>
                    <tbody>
                        {{for customer in data.customers}}
                        <tr class="{{if customer.@@odd}}table-light{{endif}}{{if customer.@@first}} first-row{{endif}}{{if customer.@@last}} last-row{{endif}}">
                            <th scope="row">{{:customer.@@index}}</th>
                            <td>
                                <span class="badge bg-secondary">{{:customer.code}}</span>
                            </td>
                            <td>{{:customer.first_name|capitalize}}</td>
                            <td>{{:customer.last_name|uppercase}}</td>
                            <td>
                                <span class="badge bg-primary badge-country">{{:customer.country|uppercase}}</span>
                            </td>
                            <td>{{:customer.dob}}</td>
                            <td>
                                {{if customer.dob}}
                                    <small class="text-muted">
                                        {{:customer.dob|age}} years
                                    </small>
                                {{else}}
                                    <small class="text-muted">N/A</small>
                                {{endif}}
                            </td>
                        </tr>
                        {{endfor}}
                    </tbody>
                </table>
            </div>

            <!-- Dataset Statistics -->
            <div class="row mt-4">
                 <div class="col-md-4">
                    <div class="card text-center">
                        <div class="card-body">
                            <h5 class="card-title">{{:data.customers|count}}</h5>
                            <p class="card-text">Total Customers</p>
                        </div>
                    </div>
                </div>
                <div class="offset-md-4 col-md-4">
                    <div class="card text-center">
                        <div class="card-body">
                            <h5 class="card-title">{{:data.avg_age|round,-2}} years</h5>
                            <p class="card-text">Average Age</p>
                        </div>
                    </div>
                </div>
            </div>

        {{else}}
            <!-- Empty State -->
            <div class="alert alert-info text-center" role="alert">
                <h4 class="alert-heading">No Data Available</h4>
                <p>The employee dataset is empty or not loaded.</p>
                <hr>
                <p class="mb-0">Please check your data source and try again.</p>
            </div>
        {{endif}}

        <!-- Action Buttons -->
        <div class="d-flex justify-content-between mt-4">
            <button class="btn btn-outline-secondary" onclick="window.print()">
                Print Table
            </button>
            <div>
                <button class="btn btn-success me-2" onclick="exportToCSV()">
                    Export CSV
                </button>
                <button class="btn btn-primary" onclick="addEmployee()">
                    Add Employee
                </button>
            </div>
        </div>
    </div>

    <script>
        function exportToCSV() {
            // CSV export functionality
            alert('CSV export would be implemented here');
        }

        function addEmployee() {
            // Add employee functionality
            alert('Add employee form would open here');
        }
    </script>

{{endblock}}

Expressões e Funções Integradas (Novo na 0.9.0)

TemplatePro 0.9.0 introduz um poderoso sistema de expressões com funções integradas que permite cálculos complexos diretamente nos templates:

{{# Cálculos aritméticos #}}
Subtotal: ${{@quantity * unit_price}}
Imposto (10%): ${{@quantity * unit_price * 0.10}}
Total: ${{@(quantity * unit_price * 1.10)|round,-2}}

{{# Funções matemáticas #}}
Raiz quadrada: {{@sqrt(16)}}
Valor absoluto: {{@abs(-42)}}
Mínimo: {{@min(price1, price2)}}
Máximo: {{@max(stock, min_stock)}}

{{# Funções de string #}}
Comprimento: {{@length(customer.name)}}
Maiúsculas: {{@upper("hello")}}
Primeiros 10 caracteres: {{@left(description, 10)}}

{{# Condições complexas com expressões #}}
{{if @(age >= 18 and has_license)}}
  Pode alugar um carro.
{{endif}}

{{if @(total > 100 or is_premium_member)}}
  Frete grátis!
{{endif}}

Você também pode usar o construtor set para criar variáveis calculadas:

{{set subtotal := @(quantity * unit_price)}}
{{set tax := @(subtotal * 0.10)}}
{{set total := @(subtotal + tax)}}

Resumo do Pedido:
  Subtotal: ${{:subtotal|round,-2}}
  Imposto: ${{:tax|round,-2}}
  Total: ${{:total|round,-2}}

Filtros Personalizados para Lógica de Domínio

Crie filtros especializados para seu domínio de negócios:

// Custom filter for "age" expressed as in "I'm x years old"
function Age(const Value: TValue; const Parameters: TArray<TFilterParameter>): TValue;
begin
  //DOB is a string formatted as yyyy-mm-dd
  Result := YearsBetween(Now, ISODateToDate(Value.AsString));
end;

// Register filters
procedure TemplateProContextConfigure;
begin
  TTProConfiguration.OnContextConfiguration := procedure(const CompiledTemplate: ITProCompiledTemplate)
  begin
    // These filters will be available to the TemplatePro views as if they were the standard ones
    CompiledTemplate.AddFilter('age', Age);
  end;
end;

Use nos templates:

{{if customer.dob}}
    <small class="text-muted">
        <!-- Calculate age - you'd implement this as a custom filter -->
        {{:customer.dob|age}} years
    </small>
{{else}}
    <small class="text-muted">N/A</small>
{{endif}}

Aqui está a página de exemplo resultante.

Iteração de Campos de Dataset (Novo na 0.9.0)

Um dos recursos mais poderosos do TemplatePro 0.9.0 é a capacidade de iterar sobre os campos de um TDataSet, permitindo a geração dinâmica de formulários e tabelas:

<form>
{{for field in customers.fields}}
  <div class="form-group">
    <label>{{:field.DisplayLabel}}{{if field.Required}} *{{endif}}</label>
    <input type="text"
           name="{{:field.FieldName}}"
           value="{{:field}}"
           {{if field.ReadOnly}}readonly{{endif}}
           class="form-control">
  </div>
{{endfor}}
</form>

As propriedades disponíveis para cada campo incluem:

  • FieldName - Nome interno do campo
  • DisplayLabel - Rótulo amigável para o usuário
  • DataType - Tipo de dado do campo
  • Size - Tamanho do campo
  • Required - Campo é obrigatório
  • ReadOnly - Campo é somente leitura
  • Visible - Campo é visível
  • Index - Índice do campo (baseado em 0)

Você também pode usar expressões para condições avançadas de campos:

{{for f in customers.fields}}
  {{if @(f.Size > 100)}}
    <textarea name="{{:f.FieldName}}" class="form-control">{{:f}}</textarea>
  {{else}}
    <input type="text" name="{{:f.FieldName}}" value="{{:f}}" class="form-control">
  {{endif}}
{{endfor}}

Integração com HTMX

Páginas HTMX podem se beneficiar da geração de código do lado do servidor e se conectar a servidores REST em relação às atualizações de conteúdo. As tecnologias web Delphi podem oferecer geração de páginas e APIs REST em um nível de altíssima qualidade.

<!-- HTMX-powered dynamic content -->
<div id="customer-list">
    {{include "partials/customer-list.html"}}
</div>

<button hx-get="/api/customers"
        hx-target="#customer-list"
        hx-swap="innerHTML">
    Refresh Customers
</button>

<form hx-post="/api/customers"
      hx-target="#customer-list"
      hx-swap="innerHTML">
    <input name="name" placeholder="Customer Name" required>
    <input name="email" placeholder="Email" required>
    <button type="submit">Add Customer</button>
</form>

Include com Mapeamento de Variáveis (Novo na 0.9.0)

TemplatePro 0.9.0 permite passar variáveis específicas para templates incluídos, tornando os componentes ainda mais reutilizáveis:

{{# Include com variáveis mapeadas #}}
{{include "components/product_card.html", product = :item, show_price = true}}

{{# Em um loop #}}
{{for item in cart.items}}
  {{include "components/cart_item.html", product = :item, qty = :item.quantity}}
{{endfor}}

{{# Com expressões #}}
{{include "components/summary.html", total = @(subtotal * 1.10), discount = :promo_discount}}

No template incluído (product_card.html), as variáveis estão disponíveis com os nomes mapeados:

<div class="product-card">
  <h3>{{:product.name}}</h3>
  {{if show_price}}
    <p class="price">${{:product.price|round,-2}}</p>
  {{endif}}
</div>

TemplatePro suporta recursos empresariais como:

  • Templates Compilados para máximo desempenho e menor carga de CPU, com cache em disco (.tpc)
  • Autenticação e Autorização templates podem ter acesso às funções do usuário
  • Tratamento de Erros em caso de erros não tratados, o tratamento de erros padrão é acionado (verifique o exemplo \samples\custom_exception_handling_using_controller)
  • Multiplataforma compatibilidade com Windows, Linux e macOS

Recursos de Aprendizado e Próximos Passos

Começando Imediatamente

A maneira mais rápida de começar é usar o assistente DMVCFramework na IDE Delphi. DMVCFramework permite criar servidores RESTful poderosos sem esforço. Você pode criar um servidor RESTful completo em alguns cliques.

Comunidade e Suporte

Sobre WebStencils

Para cobertura abrangente do WebStencils e integração HTMX, recomendamos fortemente o download do White Paper gratuito por Antonio Zapater disponível no blog da Embarcadero. Este guia fornece cobertura aprofundada de:

  • Técnicas avançadas de script WebStencils
  • Padrões de integração HTMX
  • Estratégias de implantação empresarial
  • Otimização de desempenho
  • Melhores práticas de segurança

Conclusão

A combinação de DMVCFramework e TemplatePro (ou WebStencils) representa um paradigma poderoso para desenvolvimento web rápido. Enquanto outros frameworks requerem processos de build complexos, conhecimento extenso de JavaScript e procedimentos de implantação intrincados, esta stack baseada em Delphi entrega:

Velocidade: Do conceito à produção em minutos, não semanas Simplicidade: Sintaxe intuitiva que os desenvolvedores podem dominar rapidamente Poder: Recursos de nível empresarial sem complexidade de nível empresarial Flexibilidade: Implante em qualquer lugar, integre com qualquer coisa Estabilidade: Testado em batalha em ambientes de produção em todo o mundo

Como revelado por uma análise da Embarcadero, DMVCFramework é o projeto Delphi mais popular para WebAPI e WebApp no GitHub desde 2017, e sua integração com motores de templates modernos como TemplatePro e WebStencils apenas fortalece sua posição como a solução ideal para desenvolvimento web rápido.

Seja construindo ferramentas internas, aplicações voltadas para o cliente ou APIs empresariais, esta stack fornece o equilíbrio perfeito de produtividade e profissionalismo. A curva de aprendizado é suave, a documentação é abrangente e a comunidade é solidária.

Para desenvolvedores cansados de cadeias de ferramentas complexas e soluções super-engenhadas, DMVCFramework com TemplatePro oferece um retorno revigorante à simplicidade sem sacrificar a capacidade. Em um mundo onde o desenvolvimento web muitas vezes parece desnecessariamente complicado, essas ferramentas provam que às vezes a melhor solução também é a mais simples.

Pronto para experimentar a velocidade e simplicidade por si mesmo? Comece com o assistente DMVCFramework, explore os exemplos, e mergulhe nos recursos gratuitos disponíveis. Sua próxima aplicação web está a apenas alguns minutos de distância.

Comments

comments powered by Disqus