Componente QuickGrid
do ASP.NET Core Blazor
O componente QuickGrid
é um componente Razor para exibir dados de forma rápida e eficiente em formato tabular. O QuickGrid
fornece um componente de grade de dados simples e conveniente para cenários comuns de renderização de grade e serve como uma arquitetura de referência e linha de base de desempenho para criar componentes de grade de dados. O QuickGrid
é altamente otimizado e usa técnicas avançadas para obter o desempenho ideal de renderização.
Pacote
Adicione uma referência de pacote para o pacote Microsoft.AspNetCore.Components.QuickGrid
.
Observação
Para obter diretrizes sobre como adicionar pacotes a aplicativos .NET, consulte os artigos em Instalar e gerenciar pacotes no Fluxo de trabalho de consumo de pacotes (documentação do NuGet). Confirme as versões corretas de pacote em NuGet.org.
Aplicativo de exemplo
Para várias QuickGrid
demonstrações, consulte o QuickGrid paraBlazor aplicativo de exemplo. O site de demonstração está hospedado no GitHub Pages. O site é carregado rapidamente graças à pré-geração estática usando o projeto BlazorWasmPrerendering.Build
do GitHub mantido pela comunidade.
QuickGrid
implementação
Para implementar um componente QuickGrid
:
- Especifique as marcas para o componente
QuickGrid
na marcação Razor (<QuickGrid>...</QuickGrid>
). - Nomeie uma fonte de dados que possa ser consultada para a grade. Use uma das seguintes fontes de dados:
- Items: um valor anulável
IQueryable<TGridItem>
, em queTGridItem
é o tipo de dados representado por cada linha na grade. - ItemsProvider: um retorno de chamada que fornece dados para a grade.
- Items: um valor anulável
- Class: um nome de classe CSS opcional. Se fornecido, o nome da classe será incluído no atributo
class
da tabela renderizada. - Theme: um nome de tema (valor padrão:
default
). Isso impacta quais regras de estilo correspondem à tabela. - Virtualize: se for true, a grade será renderizada com virtualização. Normalmente, isso é usado juntamente com a rolagem e faz com que a grade busque e renderize apenas os dados em torno do visor de rolagem atual. Isso pode melhorar muito o desempenho de rolagem em grandes conjuntos de dados. Se usar Virtualize, você deverá fornecer um valor para ItemSize e deverá garantir que cada linha seja renderizada com uma altura constante. Geralmente, é preferível não usar Virtualize se a quantidade de dados renderizados for pequena ou se você estiver usando a paginação.
- ItemSize: aplicável somente ao usar Virtualize. ItemSize define uma altura esperada em pixels para cada linha, permitindo que o mecanismo de virtualização busque o número correto de itens para corresponder ao tamanho da exibição e garantir uma rolagem precisa.
- ItemKey: opcionalmente define um valor para
@key
em cada linha renderizada. Normalmente, é usado para especificar um identificador exclusivo, como um valor de chave primária, para cada item de dados. Isso permite que a grade preserve a associação entre elementos de linha e itens de dados com base em seus identificadores exclusivos, mesmo quando as instânciasTGridItem
são substituídas por novas cópias (por exemplo, após uma nova consulta no armazenamento de dados subjacente). Se não estiver definido,@key
será a instânciaTGridItem
. OverscanCount
: define quantos itens adicionais serão renderizados antes e depois da região visível para reduzir a frequência de renderização durante a rolagem. Embora valores mais altos possam melhorar a suavidade da rolagem renderizando mais itens fora da tela, um valor mais alto também pode resultar em um aumento nos tempos de carga iniciais. É recomendável encontrar um equilíbrio com base no tamanho do conjunto de dados e nos requisitos de experiência do usuário. O valor padrão é 3. Disponível somente ao usar Virtualize.- Pagination: opcionalmente vincula essa instância
TGridItem
a um modelo PaginationState, fazendo com que a grade busque e renderize apenas a página de dados atual. Normalmente, é usado em conjunto com um componente Paginator ou alguma outra lógica de interface do usuário que exiba e atualize a instância PaginationState fornecida. - No conteúdo filho
QuickGrid
(RenderFragment), especifique PropertyColumn<TGridItem,TProp>s, que representam colunasTGridItem
cujas células exibem os valores:- Property: define o valor a ser exibido nas células dessa coluna.
- Format: opcionalmente especifica uma cadeia de caracteres de formato para o valor. Para usar Format, é preciso o tipo
TProp
para implementar IFormattable. - Sortable: indica se os dados devem ser classificados por esta coluna. O valor padrão pode variar de acordo com o tipo de coluna. Por exemplo, um TemplateColumn<TGridItem> é classificável se qualquer parâmetro SortBy for especificado.
- InitialSortDirection: indica a direção de classificação se IsDefaultSortColumn for
true
. - IsDefaultSortColumn: indica se essa coluna deve ser classificada por padrão.
- PlaceholderTemplate: se especificado, as grades virtualizadas usam esse modelo para renderizar células cujos dados não tenham sido carregados.
- HeaderTemplate: um modelo opcional para a célula de cabeçalho desta coluna. Se não for especificado, o modelo de cabeçalho padrão inclui o Title, juntamente com os botões de opções e indicadores de classificação aplicáveis.
- Title: texto do título da coluna. O título será renderizado automaticamente se HeaderTemplate não for usado.
- Especifique as marcas para o componente
QuickGrid
na marcação Razor (<QuickGrid>...</QuickGrid>
). - Nomeie uma fonte de dados que possa ser consultada para a grade. Use uma das seguintes fontes de dados:
- Items: um valor anulável
IQueryable<TGridItem>
, em queTGridItem
é o tipo de dados representado por cada linha na grade. - ItemsProvider: um retorno de chamada que fornece dados para a grade.
- Items: um valor anulável
- Class: um nome de classe CSS opcional. Se fornecido, o nome da classe será incluído no atributo
class
da tabela renderizada. - Theme: um nome de tema (valor padrão:
default
). Isso impacta quais regras de estilo correspondem à tabela. - Virtualize: se for true, a grade será renderizada com virtualização. Normalmente, isso é usado juntamente com a rolagem e faz com que a grade busque e renderize apenas os dados em torno do visor de rolagem atual. Isso pode melhorar muito o desempenho de rolagem em grandes conjuntos de dados. Se usar Virtualize, você deverá fornecer um valor para ItemSize e deverá garantir que cada linha seja renderizada com uma altura constante. Geralmente, é preferível não usar Virtualize se a quantidade de dados renderizados for pequena ou se você estiver usando a paginação.
- ItemSize: aplicável somente ao usar Virtualize. ItemSize define uma altura esperada em pixels para cada linha, permitindo que o mecanismo de virtualização busque o número correto de itens para corresponder ao tamanho da exibição e garantir uma rolagem precisa.
- ItemKey: opcionalmente define um valor para
@key
em cada linha renderizada. Normalmente, é usado para especificar um identificador exclusivo, como um valor de chave primária, para cada item de dados. Isso permite que a grade preserve a associação entre elementos de linha e itens de dados com base em seus identificadores exclusivos, mesmo quando as instânciasTGridItem
são substituídas por novas cópias (por exemplo, após uma nova consulta no armazenamento de dados subjacente). Se não estiver definido,@key
será a instânciaTGridItem
. - Pagination: opcionalmente vincula essa instância
TGridItem
a um modelo PaginationState, fazendo com que a grade busque e renderize apenas a página de dados atual. Normalmente, é usado em conjunto com um componente Paginator ou alguma outra lógica de interface do usuário que exiba e atualize a instância PaginationState fornecida. - No conteúdo filho
QuickGrid
(RenderFragment), especifique PropertyColumn<TGridItem,TProp>s, que representam colunasTGridItem
cujas células exibem os valores:- Property: define o valor a ser exibido nas células dessa coluna.
- Format: opcionalmente especifica uma cadeia de caracteres de formato para o valor. Para usar Format, é preciso o tipo
TProp
para implementar IFormattable. - Sortable: indica se os dados devem ser classificados por esta coluna. O valor padrão pode variar de acordo com o tipo de coluna. Por exemplo, um TemplateColumn<TGridItem> é classificável se qualquer parâmetro SortBy for especificado.
- InitialSortDirection: indica a direção de classificação se IsDefaultSortColumn for
true
. - IsDefaultSortColumn: indica se essa coluna deve ser classificada por padrão.
- PlaceholderTemplate: se especificado, as grades virtualizadas usam esse modelo para renderizar células cujos dados não tenham sido carregados.
- HeaderTemplate: um modelo opcional para a célula de cabeçalho desta coluna. Se não for especificado, o modelo de cabeçalho padrão inclui o Title, juntamente com os botões de opções e indicadores de classificação aplicáveis.
- Title: texto do título da coluna. O título será renderizado automaticamente se HeaderTemplate não for usado.
Por exemplo, adicione o componente a seguir para renderizar uma grade.
Para Blazor Web Apps, o componente QuickGrid
deve adotar um modo de renderização interativo para habilitar recursos interativos, como paginação e classificação.
PromotionGrid.razor
:
@page "/promotion-grid"
@using Microsoft.AspNetCore.Components.QuickGrid
<PageTitle>Promotion Grid</PageTitle>
<h1>Promotion Grid Example</h1>
<QuickGrid Items="people">
<PropertyColumn Property="@(p => p.PersonId)" Sortable="true" />
<PropertyColumn Property="@(p => p.Name)" Sortable="true" />
<PropertyColumn Property="@(p => p.PromotionDate)" Format="yyyy-MM-dd" Sortable="true" />
</QuickGrid>
@code {
private record Person(int PersonId, string Name, DateOnly PromotionDate);
private IQueryable<Person> people = new[]
{
new Person(10895, "Jean Martin", new DateOnly(1985, 3, 16)),
new Person(10944, "António Langa", new DateOnly(1991, 12, 1)),
new Person(11203, "Julie Smith", new DateOnly(1958, 10, 10)),
new Person(11205, "Nur Sari", new DateOnly(1922, 4, 27)),
new Person(11898, "Jose Hernandez", new DateOnly(2011, 5, 3)),
new Person(12130, "Kenji Sato", new DateOnly(2004, 1, 9)),
}.AsQueryable();
}
@page "/promotion-grid"
@using Microsoft.AspNetCore.Components.QuickGrid
<PageTitle>Promotion Grid</PageTitle>
<h1>Promotion Grid Example</h1>
<QuickGrid Items="people">
<PropertyColumn Property="@(p => p.PersonId)" Sortable="true" />
<PropertyColumn Property="@(p => p.Name)" Sortable="true" />
<PropertyColumn Property="@(p => p.PromotionDate)" Format="yyyy-MM-dd" Sortable="true" />
</QuickGrid>
@code {
private record Person(int PersonId, string Name, DateOnly PromotionDate);
private IQueryable<Person> people = new[]
{
new Person(10895, "Jean Martin", new DateOnly(1985, 3, 16)),
new Person(10944, "António Langa", new DateOnly(1991, 12, 1)),
new Person(11203, "Julie Smith", new DateOnly(1958, 10, 10)),
new Person(11205, "Nur Sari", new DateOnly(1922, 4, 27)),
new Person(11898, "Jose Hernandez", new DateOnly(2011, 5, 3)),
new Person(12130, "Kenji Sato", new DateOnly(2004, 1, 9)),
}.AsQueryable();
}
Acesse o componente em um navegador no caminho relativo /promotion-grid
.
No momento, não há planos para estender QuickGrid
com recursos que as grades comerciais completas tendem a oferecer, como linhas hierárquicas, colunas de arrastar para reordenar ou seleções de intervalo semelhantes ao Excel. Se você precisar de recursos avançados que não deseja desenvolver por conta própria, continue usando grades de terceiros.
Classificar por coluna
O componente QuickGrid
pode classificar itens por colunas. Em Blazor Web Apps, a classificação requer que o componente adote um modo de renderização interativo.
Adicione Sortable="true"
(Sortable) à marca PropertyColumn<TGridItem,TProp>:
<PropertyColumn Property="..." Sortable="true" />
No aplicativo em execução, classifique a coluna QuickGrid
selecionando o título da coluna renderizada.
Itens de página com um componente Paginator
O componente QuickGrid
pode paginar dados da fonte de dados. Em Blazor Web Apps, a paginação requer que o componente adote um modo de renderização interativo.
Adicione uma instância PaginationState ao bloco @code
do componente. Defina o ItemsPerPage para o número de itens a serem exibidos por página. No exemplo a seguir, a instância é denominada pagination
e são definidos dez itens por página:
PaginationState pagination = new PaginationState { ItemsPerPage = 10 };
Defina a propriedade Pagination do componente QuickGrid
como pagination
:
<QuickGrid Items="..." Pagination="pagination">
Para fornecer uma interface do usuário de paginação, adicione um componente Paginator
acima ou abaixo do componente QuickGrid
. Defina Paginator.State como pagination
:
<Paginator State="pagination" />
No aplicativo em execução, navegue pelos itens usando um componente Paginator
renderizado.
Atributos e estilos personalizados
QuickGrid também dá suporte a passagem de atributos personalizados e classes de estilo (Class) para o elemento da tabela renderizada:
<QuickGrid Items="..." custom-attribute="value" Class="custom-class">
Fonte de dados do Entity Framework Core (EF Core)
Use o padrão de fábrica para resolver um contexto de banco de dados EF Core que fornece dados para um componente QuickGrid
. Para obter mais informações sobre por que o padrão de fábrica é recomendado, veja ASP.NET CoreBlazor com Entity Framework Core (EF Core).
Uma fábrica de contexto de banco de dados (IDbContextFactory<TContext>) é injetada no componente com a diretiva @inject
. A abordagem de fábrica requer o descarte do contexto do banco de dados, portanto o componente implementa a interface IAsyncDisposable com a diretiva @implements
. O provedor de itens para o componente QuickGrid
é um DbSet<T>
obtido do contexto do banco de dados criado (CreateDbContext) da fábrica de contexto do banco de dados injetado.
O QuickGrid reconhece instâncias IQueryable fornecidas por EF e sabe como resolver consultas de forma assíncrona para aumentar a eficiência.
Adicione uma referência de pacote para o pacote Microsoft.AspNetCore.Components.QuickGrid.EntityFrameworkAdapter
NuGet.
Observação
Para obter diretrizes sobre como adicionar pacotes a aplicativos .NET, consulte os artigos em Instalar e gerenciar pacotes no Fluxo de trabalho de consumo de pacotes (documentação do NuGet). Confirme as versões corretas de pacote em NuGet.org.
Chame AddQuickGridEntityFrameworkAdapter na coleta de serviço no arquivo Program
para registrar uma implementação de IAsyncQueryExecutor com reconhecimento de EF:
builder.Services.AddQuickGridEntityFrameworkAdapter();
O exemplo a seguir usa um ExampleTable
DbSet<TEntity> (tabela) de um contexto de banco de dados AppDbContext
(context
) como fonte de dados para um componente QuickGrid
:
@using Microsoft.AspNetCore.Components.QuickGrid
@using Microsoft.EntityFrameworkCore
@implements IAsyncDisposable
@inject IDbContextFactory<AppDbContext> DbFactory
...
<QuickGrid ... Items="context.ExampleTable" ...>
...
</QuickGrid>
@code {
private AppDbContext context = default!;
protected override void OnInitialized()
{
context = DbFactory.CreateDbContext();
}
public async ValueTask DisposeAsync() => await context.DisposeAsync();
}
No bloco de código (@code
) do exemplo anterior:
- O campo
context
contém o contexto do banco de dados, digitado como umAppDbContext
. - O método
OnInitialized
lifecycle atribui um novo contexto de banco de dados (CreateDbContext) ao campocontext
da fábrica injetada (DbFactory
). - O método assíncrono
DisposeAsync
descarta o contexto do banco de dados quando o componente é descartado.
Você também pode usar qualquer operador LINQ compatível com EF para filtrar os dados antes de passá-los para o parâmetro Items.
O exemplo a seguir filtra filmes por um título de filme inserido em uma caixa de pesquisa. O contexto do banco de dados é BlazorWebAppMoviesContext
e o modelo é Movie
. A propriedade Title
do filme é usada para a operação de filtro.
@using Microsoft.AspNetCore.Components.QuickGrid
@using Microsoft.EntityFrameworkCore
@implements IAsyncDisposable
@inject IDbContextFactory<BlazorWebAppMoviesContext> DbFactory
...
<p>
<input type="search" @bind="titleFilter" @bind:event="oninput" />
</p>
<QuickGrid ... Items="FilteredMovies" ...>
...
</QuickGrid>
@code {
private string titleFilter = string.Empty;
private BlazorWebAppMoviesContext context = default!;
protected override void OnInitialized()
{
context = DbFactory.CreateDbContext();
}
private IQueryable<Movie> FilteredMovies =>
context.Movie.Where(m => m.Title!.Contains(titleFilter));
public async ValueTask DisposeAsync() => await context.DisposeAsync();
}
Suporte ao nome de exibição
Um título de coluna pode ser atribuído usando ColumnBase<TGridItem>.Title na marca de PropertyColumn<TGridItem,TProp>. No exemplo de filme a seguir, a coluna recebe o nome "Release Date
" para os dados da data de lançamento do filme na coluna:
<PropertyColumn Property="movie => movie.ReleaseDate" Title="Release Date" />
No entanto, gerenciar títulos de coluna (nomes) de propriedades de modelo associadas geralmente é uma opção melhor para manter um aplicativo. Um modelo pode controlar o nome de exibição de uma propriedade com o atributo [Display]
. No seguinte exemplo, o modelo especifica um nome de exibição de data de lançamento do filme de "Release Date
" para a propriedade ReleaseDate
dele:
[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }
Para habilitar o componente QuickGrid
a usar o DisplayAttribute.Name, a subclasse PropertyColumn<TGridItem,TProp>, no componente ou em uma classe separada:
public class DisplayNameColumn<TGridItem, TProp> : PropertyColumn<TGridItem, TProp>
{
protected override void OnParametersSet()
{
if (Title is null && Property.Body is MemberExpression memberExpression)
{
var memberInfo = memberExpression.Member;
Title =
memberInfo.GetCustomAttribute<DisplayNameAttribute>().DisplayName ??
memberInfo.GetCustomAttribute<DisplayAttribute>().Name ??
memberInfo.Name;
}
base.OnParametersSet();
}
}
Use a subclasse no componente QuickGrid
. No exemplo a seguir, a DisplayNameColumn
anterior é usada. O nome "Release Date
" é fornecido pelo atributo [Display]
no modelo, portanto, não é necessário especificar um Title:
<DisplayNameColumn Property="movie => movie.ReleaseDate" />
O atributo [DisplayName]
também é compatível:
[DisplayName("Release Date")]
public DateTime ReleaseDate { get; set; }
No entanto, o atributo [Display]
é recomendado porque disponibiliza propriedades adicionais. Por exemplo, o atributo [Display]
oferece a capacidade de atribuir um tipo de recurso para localização.
Dados remotos
Em aplicativos Blazor WebAssembly, buscar dados de uma API Web baseada em JSON em um servidor é um requisito comum. Para buscar apenas os dados necessários para a página/visor atual de dados e aplicar regras de classificação ou filtragem no servidor, use o parâmetro ItemsProvider.
ItemsProvider também poderá ser usado em um aplicativo Blazor do lado do servidor se o aplicativo for necessário para consultar um ponto de extremidade externo ou em outros casos em que os requisitos não são cobertos por um IQueryable.
Forneça um retorno de chamada correspondente ao tipo delegado GridItemsProvider<TGridItem>, em que TGridItem
é o tipo dos dados exibidos na grade. O retorno de chamada recebe um parâmetro de tipo GridItemsProviderRequest<TGridItem>, que especifica o índice inicial, a contagem máxima de linhas e a ordem de classificação dos dados a serem retornados. Além de retornar os itens correspondentes, uma contagem total de itens (totalItemCount
) também é necessária para que a paginação e a virtualização funcionem corretamente.
O exemplo a seguir obtém dados do banco de dados público OpenFDA Food Enforcement.
O GridItemsProvider<TGridItem> converte GridItemsProviderRequest<TGridItem> em uma consulta no banco de dados OpenFDA. Os parâmetros de consulta são convertidos no formato de URL específico compatível com a API JSON externa. Só é possível executar a classificação e a filtragem por meio da classificação e filtragem compatíveis com a API externa. O ponto de extremidade OpenFDA não dá suporte à classificação; portanto, nenhuma das colunas é marcada como classificável. No entanto, ele dá suporte a ignorar registros (parâmetro skip
) e limitar o retorno de registros (parâmetro limit
), de modo que o componente possa habilitar a virtualização e rolar rapidamente por dezenas de milhares de registros.
FoodRecalls.razor
:
@page "/food-recalls"
@inject HttpClient Http
@inject NavigationManager Navigation
<PageTitle>Food Recalls</PageTitle>
<h1>OpenFDA Food Recalls</h1>
<div class="grid" tabindex="-1">
<QuickGrid ItemsProvider="@foodRecallProvider" Virtualize="true">
<PropertyColumn Title="ID" Property="@(c => c.Event_Id)" />
<PropertyColumn Property="@(c => c.State)" />
<PropertyColumn Property="@(c => c.City)" />
<PropertyColumn Title="Company" Property="@(c => c.Recalling_Firm)" />
<PropertyColumn Property="@(c => c.Status)" />
</QuickGrid>
</div>
<p>Total: <strong>@numResults results found</strong></p>
@code {
private GridItemsProvider<FoodRecall>? foodRecallProvider;
private int numResults;
protected override async Task OnInitializedAsync()
{
foodRecallProvider = async req =>
{
var url = Navigation.GetUriWithQueryParameters(
"https://api.fda.gov/food/enforcement.json",
new Dictionary<string, object?>
{
{ "skip", req.StartIndex },
{ "limit", req.Count },
});
var response = await Http.GetFromJsonAsync<FoodRecallQueryResult>(
url, req.CancellationToken);
return GridItemsProviderResult.From(
items: response!.Results,
totalItemCount: response!.Meta.Results.Total);
};
numResults = (await Http.GetFromJsonAsync<FoodRecallQueryResult>(
"https://api.fda.gov/food/enforcement.json"))!.Meta.Results.Total;
}
}
Para obter mais informações sobre como chamar APIs Web, confira Chamar uma API Web de um aplicativo ASP.NET Core Blazor.
QuickGrid
scaffolder
O QuickGrid
scaffolder estrutura Razor componentes com QuickGrid
para exibir dados de um banco de dados.
O scaffolding gera páginas básicas de CRUD (Criação, Leitura, Atualização e Exclusão) com base em um modelo de dados do Entity Framework Core. Você pode criar um scaffolding de páginas individuais ou de todas as páginas CRUD. Selecione a classe de modelo e o DbContext
, opcionalmente criando um DbContext
, se necessário.
Os componentes Razor com scaffold são adicionados ao projeto em uma pasta gerada com o nome da classe do modelo. O componente Index
gerado usa um componente QuickGrid
para exibir os dados. Personalize os componentes gerados conforme necessário e habilite a interatividade para aproveitar recursos interativos, como paginação, classificação e filtragem.
Os componentes produzidos pelo scaffolding exigem a SSR (renderização do lado do servidor), portanto, não há suporte a eles durante a execução no WebAssembly.
Clique com o botão direito do mouse na pasta Components/Pages
e selecione Adicionar>Novo Item Scaffolded.
Com o diálogo Adicionar Novo Item Scaffold aberto em Instalado>Comum>Blazor>Razor Componente, selecione Razor Componentes usando Entity Framework (CRUD). Selecione o botão Adicionar.
CRUD é um acrônimo para Criar, Ler, Atualizar e Excluir. O scaffolder produz componentes de criação, edição, exclusão, detalhes e índice para o aplicativo.
Preencha o campo Adicionar Razor Componentes usando o Entity Framework (CRUD):
- A lista suspensa Modelo inclui outros modelos para criar especificamente componentes de criação, edição, exclusão, detalhes e listagem. Esta lista suspensa é útil quando você precisa criar apenas um tipo específico de componente com scaffold para uma classe de modelo. Deixe a lista suspensa Modelo definida como CRUD para criar um conjunto completo de componentes.
- Na lista suspensa Classe do modelo, selecione a classe do modelo. Uma pasta é criada para os componentes gerados a partir do nome do modelo (se a classe do modelo for nomeada
Movie
, a pasta será automaticamente nomeadaMoviePages
). - Para a classe DbContext, selecione um contexto de banco de dados existente ou selecione o botão + (sinal de mais) e o diálogo modal Adicionar Contexto de Dados para adicionar um novo contexto de banco de dados.
- Após o fechamento do diálogo do modelo, a lista suspensa Provedor de banco de dados será padronizada para SQL Server. Você pode selecionar o provedor apropriado para o banco de dados que estiver usando. As opções incluem SQL Server, SQLite, PostgreSQL e Azure Cosmos DB.
- Selecione Adicionar.