Blazor - Imprimindo via Javascript - I

Hoje veremos imprimir no Blazor usando os recursos da interoperabilidade JavaScript.

Se você esta chegando agora e não sabe o que é o Blazor leia o artigo ASP .NET Core - Iniciando com o Blazor - Macoratti; se você já conhece e quer saber mais pode fazer o curso de Blazor Essencial.  

Com um pouco de interoperabilidade de JavaScript, podemos realizar muitas tarefas e uma delas é a impressão.

Sim, uma aplicação Blazor pode chamar funções JavaScript a partir dos métodos C#, e também podemos invocar métodos C# a partir do JavaScript.

Para mais detalhes leia o meu artigo: Blazor - Interoperabilidade com JavaScript - I

Hoje, vamos criar um projeto Blazor Server e acessar os dados da tabela Employees do banco de dados Northwind.mdf.

Se você não possui esse banco de dados baixe aqui:  Baixar Northwind

Basta seguir as instruções e gerar o banco e as tabelas com os dados no seu ambiente.

Recursos usados:

Criando o projeto no VS Community 2019

Abra o VS 2019 Community (versão mínima 16.6) e selecione a opção Create a New Project;

A seguir selecione a opção Blazor app e clique em next;

Informe o nome do projeto :  Blog_Print a localização e clique em Create;

Selecione a opção - Blazor Server App. Não vamos usar autenticação e vamos habilitar o https.

Clique no botão Create para criar o projeto.

Com o projeto criado vamos limpar o projeto excluindo os arquivos abaixo e suas referências:

A seguir crie uma pasta images dentro da pasta wwwroot do projeto criado, e nesta pasta inclua os arquivos de imagens que iremos usar em nosso projeto.

Como vamos acessar dados do banco de dados Northwind.mdf do SQL Server vamos usar o Entity Framework e mapear para a tabela Employees para exibir algumas informações dos funcionários.

Inclua o pacote Microsoft.EntityFrameworkCore.SqlServer no projeto via menu Tools ou via comando :
 install-package <nome>

Configurando o domínio e o contexto do EF Core

Vamos agora criar na pasta Data do projeto a classe Employee que representa o nosso domínio:

public class Employee
{
        public int EmployeeId { get; set; }
        public string LastName { get; set; }
        public string FirstName { get; set; }
        public string Address { get; set; }
        public string City { get; set; }
}

Criamos um DTO anêmico apenas para mapear algumas propriedades para as colunas da tabela Employees.

Ainda na pasta Data, vamos criar o arquivo de contexto AppDbContext onde vamos definir o mapeamento ORM:

public class AppDbContext : DbContext
{
        public AppDbContext(DbContextOptions<AppDbContext> options)
         : base(options)
        {
        }
        public DbSet<Employee> Employees { get; set; }
 }

Agora vamos registrar o contexto no método ConfigureServices da classe Startup:

public void ConfigureServices(IServiceCollection services)
 {
            services.AddRazorPages();
            services.AddServerSideBlazor();
            services.AddDbContext<AppDbContext>(options =>
               options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
}

Precisamos criar agora a string de conexão no arquivo appsettings.json com o nome de DefaultConnection:

{
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=\\sqlexpress;Initial Catalog=Northwind;Integrated Security=True"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Aqui você deve indicar a string de conexão do seu ambiente.

Pronto com isso já conseguimos acessar o banco de dados Northwind e a tabela Employees.

Criando o serviço para acessar os dados

Para poder acessar os dados da tabela Employees no SQL Server vamos criar a classe EmployeeService:

using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Blazor_Print.Data
{
    public class EmployeeService
    {
        private readonly AppDbContext _context;
        public EmployeeService(AppDbContext context)
        {
            _context = context;
        }
        public async Task<List<Employee>> GetEmployees()
        {
            return await _context.Employees.ToListAsync();
        }
    }
}

Nosso objetivo é apenas obter os dados da tabela e por isso criamos apenas o método GetEmployees().

Só falta registrar o serviço no arquivo Startup:

public void ConfigureServices(IServiceCollection services)
 {
            services.AddRazorPages();
            services.AddServerSideBlazor();

            services.AddTransient<EmployeeService>();
            services.AddDbContext<AppDbContext>(options =>
               options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
}

Definindo o menu e os componentes

Vamos definir as opções do menu da aplicação no arquivo NavMenu.razor da pasta Shared do projeto:

<div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">Blazor_Print</a>
    <button class="navbar-toggler" @onclick="ToggleNavMenu">
        <span class="navbar-toggler-icon"></span>
    </button>
</div>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="employees"  @onclick="()=> expandSubMenu1 = !expandSubMenu1">
                <span class="oi oi-plus" aria-hidden="true"></span> Funcionários
            </NavLink>
            @if (expandSubMenu1)
            {
                <ul class="nav flex-column">
                    <li class="nav-item px-4">
                        <NavLink class="expand-menu" href="print">
                            <span class="oi oi-print" aria-hidden="true"></span> Imprimir
                        </NavLink>
                    </li>
                </ul>
            }
        </li>
    </ul>
</div>
@code {
    private bool collapseNavMenu = true;
    private bool expandSubMenu1;
    private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
    private void ToggleNavMenu()
    {
        collapseNavMenu = !collapseNavMenu;
    }
}

Neste código estamos criando a opção de menu Funcionários que mapeia para a rota employees e a seguir criamos um sub-menu Imprimir que mapeia para print.

A idéia e que ao acionar o link Funcionários o menu de impressão seja aberto.

Vamos abrir o arquivo Index.razor e incluir o código abaixo neste arquivo:

@page "/"
<h3>Imprimindo com Blazor via Javascript</h3>
<img src="/images/print.jpg" width="500" height="300" class="img-fluid" />

Este componente apenas exibe a página inicial do projeto com uma imagem.

Agora vamos criar o componente EmployeeList.razor na pasta Pages que vai  exibir os dados dos funcionários:

@page "/employees"
@inject EmployeeService empService
<h3>Funcionários</h3>
@if (employees == null)
{
    <img src="/images/basicloader.gif" />
}
else
{
    <table class='table'>
        <thead>
            <tr>
                <th>Nome</th>
                <th>Sobrenome</th>
                <th>Endereço</th>
                <th>Cidade</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var employee in employees)
            {
                <tr>
                    <td>@employee.FirstName</td>
                    <td>@employee.LastName</td>
                    <td>@employee.Address</td>
                    <td>@employee.City</td>
                </tr>
            }
        </tbody>
    </table>
}
@code {
    List<Employee> employees;
    protected override async Task OnInitializedAsync()
    {
        employees = await empService.GetEmployees();
    }
}

Este componente será acionado quando o usuário clicar no menu Funcionários.

Ao clicar neste menu será aberto o submenu Imprimir que vai acionar o componente Print.razor:

@page "/print"
@inject IJSRuntime IJS
<h3>Imprimindo...</h3>
<img src="/images/printanima1.gif" width="300" height="300" class="img-fluid"/>
@code {
    protected override async Task OnInitializedAsync()
    {
        await PrintEmployees();
    }
    public async Task PrintEmployees()
    {
        await IJS.InvokeAsync<object>("open", new object[] { "/printEmployees", "_blank" });
    }
}

É neste código que estamos chamando o método PrintEmployees() que vai usar os recursos da interoperabilidade Javascript do Blazor para abrir o componente PrintEmployees.razor para impressão:

@layout BlankLayout
@page "/printEmployees"
@inject EmployeeService empService
<body onload="window.print()">
    <br />
    <br />
    <h3>Funcionários</h3>
    <br />
    <br />
    @if (employees == null)
    {
        <img src="/images/basicloader.gif" />
    }
    else
    {
        <table class='table'>
            <thead>
                <tr>
                    <th>Nome</th>
                    <th>Sobrenome</th>
                    <th>Endereço</th>
                    <th>Cidade</th>
                </tr>
            </thead>
            <tbody>
                @foreach (var employee in employees)
                {
                    <tr>
                        <td>@employee.FirstName</td>
                        <td>@employee.LastName</td>
                        <td>@employee.Address</td>
                        <td>@employee.City</td>
                    </tr>
                }
            </tbody>
        </table>
    }
</body>
@code {
    List<Employee> employees;
    protected override async Task OnInitializedAsync()
    {
        employees = await empService.GetEmployees();
    }
}

O código deste componente é quase igual ao do componente ListEmployees.razor mas note que o layout usado neste componente é definido em BlankLayout e que temos código Html usando javascript para impressão.

Na pasta Shared do projeto vamos criar o componente BlankLayout:

@inherits LayoutComponentBase
@Body

Pronto ! já podemos testar a impressão da lista de funcionários que implementamos usando Javascript.

Executando o projeto teremos o resultado a seguir:

Na próxima parte do artigo veremos como gerar um relatório customizado e imprimir via JavaScript.

Pegue o projeto aqui:  Blazor_Print.zip (sem as referências)

"E em nada vos espanteis dos que resistem, o que para eles, na verdade, é indício de perdição, mas para vós de salvação, e isto de Deus. Porque a vós vos foi concedido, em relação a Cristo, não somente crer nele, como também padecer por ele"
Filipenses 1:28,29

 


Referências:


José Carlos Macoratti